Merge "Fixed 32 bit characteristic value assignment" into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index 5dc3523..104293c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -146,7 +146,7 @@
 	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/ISchedulingPolicyService.aidl \
 	core/java/android/os/IUpdateLock.aidl \
-        core/java/android/os/IUserManager.aidl \
+	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/dreams/IDreamManager.aidl \
 	core/java/android/service/dreams/IDreamService.aidl \
@@ -164,6 +164,8 @@
 	core/java/android/view/IOnKeyguardExitResult.aidl \
 	core/java/android/view/IRotationWatcher.aidl \
 	core/java/android/view/IWindow.aidl \
+	core/java/android/view/IWindowFocusObserver.aidl \
+	core/java/android/view/IWindowId.aidl \
 	core/java/android/view/IWindowManager.aidl \
 	core/java/android/view/IWindowSession.aidl \
 	core/java/android/speech/IRecognitionListener.aidl \
@@ -178,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 a4b4992..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);
@@ -9036,12 +9038,12 @@
     ctor public Picture();
     ctor public Picture(android.graphics.Picture);
     method public android.graphics.Canvas beginRecording(int, int);
-    method public static android.graphics.Picture createFromStream(java.io.InputStream);
+    method public static deprecated android.graphics.Picture createFromStream(java.io.InputStream);
     method public void draw(android.graphics.Canvas);
     method public void endRecording();
     method public int getHeight();
     method public int getWidth();
-    method public void writeToStream(java.io.OutputStream);
+    method public deprecated void writeToStream(java.io.OutputStream);
   }
 
   public class PixelFormat {
@@ -10044,16 +10046,20 @@
     field public static final int TYPE_ACCELEROMETER = 1; // 0x1
     field public static final int TYPE_ALL = -1; // 0xffffffff
     field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
+    field public static final int TYPE_GAME_ROTATION_VECTOR = 15; // 0xf
     field public static final int TYPE_GRAVITY = 9; // 0x9
     field public static final int TYPE_GYROSCOPE = 4; // 0x4
+    field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
     field public static final int TYPE_LIGHT = 5; // 0x5
     field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa
     field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2
+    field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe
     field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3
     field public static final int TYPE_PRESSURE = 6; // 0x6
     field public static final int TYPE_PROXIMITY = 8; // 0x8
     field public static final int TYPE_RELATIVE_HUMIDITY = 12; // 0xc
     field public static final int TYPE_ROTATION_VECTOR = 11; // 0xb
+    field public static final int TYPE_SIGNIFICANT_MOTION = 17; // 0x11
     field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7
   }
 
@@ -10075,6 +10081,7 @@
   }
 
   public abstract class SensorManager {
+    method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
     method public android.hardware.Sensor getDefaultSensor(int);
@@ -10090,6 +10097,7 @@
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
     method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, android.os.Handler);
     method public static boolean remapCoordinateSystem(float[], int, int, float[]);
+    method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
     method public deprecated void unregisterListener(android.hardware.SensorListener);
     method public deprecated void unregisterListener(android.hardware.SensorListener, int);
     method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -10153,6 +10161,17 @@
     field public static final float STANDARD_GRAVITY = 9.80665f;
   }
 
+  public final class TriggerEvent {
+    field public android.hardware.Sensor sensor;
+    field public long timestamp;
+    field public final float[] values;
+  }
+
+  public abstract class TriggerEventListener {
+    ctor public TriggerEventListener();
+    method public abstract void onTrigger(android.hardware.TriggerEvent);
+  }
+
 }
 
 package android.hardware.display {
@@ -11265,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();
@@ -11278,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
@@ -11357,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
@@ -11579,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;
@@ -11792,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);
@@ -14111,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);
@@ -22511,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 {
@@ -24150,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
@@ -24161,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
   }
@@ -24173,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 {
@@ -24181,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;
   }
 
@@ -25279,6 +25321,7 @@
     method public int getVisibility();
     method public final int getWidth();
     method protected int getWindowAttachCount();
+    method public android.view.WindowId getWindowId();
     method public int getWindowSystemUiVisibility();
     method public android.os.IBinder getWindowToken();
     method public int getWindowVisibility();
@@ -26224,6 +26267,21 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback);
   }
 
+  public class WindowId implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isFocused();
+    method public void registerFocusObserver(android.view.WindowId.FocusObserver);
+    method public void unregisterFocusObserver(android.view.WindowId.FocusObserver);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static abstract class WindowId.FocusObserver {
+    ctor public WindowId.FocusObserver();
+    method public abstract void onFocusGained(android.view.WindowId);
+    method public abstract void onFocusLost(android.view.WindowId);
+  }
+
   public abstract interface WindowManager implements android.view.ViewManager {
     method public abstract android.view.Display getDefaultDisplay();
     method public abstract void removeViewImmediate(android.view.View);
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 da398ef..98c82b5 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1094,7 +1094,7 @@
     private boolean deletePackage(String pkg, int unInstallFlags) {
         PackageDeleteObserver obs = new PackageDeleteObserver();
         try {
-            mPm.deletePackage(pkg, obs, unInstallFlags);
+            mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags);
 
             synchronized (obs) {
                 while (!obs.finished) {
@@ -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 e9535ab..dbc9051 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -275,6 +275,38 @@
                 handleException(response, "getAccountRemovalAllowed", account.toString(), e);
             }
         }
+
+        public void getAccountCredentialsForCloning(IAccountAuthenticatorResponse response,
+                Account account) throws RemoteException {
+            checkBinderPermission();
+            try {
+                final Bundle result =
+                        AbstractAccountAuthenticator.this.getAccountCredentialsForCloning(
+                                new AccountAuthenticatorResponse(response), account);
+                if (result != null) {
+                    response.onResult(result);
+                }
+            } catch (Exception e) {
+                handleException(response, "getAccountCredentialsForCloning", account.toString(), e);
+            }
+        }
+
+        public void addAccountFromCredentials(IAccountAuthenticatorResponse response,
+                Account account,
+                Bundle accountCredentials) throws RemoteException {
+            checkBinderPermission();
+            try {
+                final Bundle result =
+                        AbstractAccountAuthenticator.this.addAccountFromCredentials(
+                                new AccountAuthenticatorResponse(response), account,
+                                accountCredentials);
+                if (result != null) {
+                    response.onResult(result);
+                }
+            } catch (Exception e) {
+                handleException(response, "addAccountFromCredentials", account.toString(), e);
+            }
+        }
     }
 
     private void handleException(IAccountAuthenticatorResponse response, String method,
@@ -471,4 +503,52 @@
         result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
         return result;
     }
+
+    /**
+     * 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)}.
+     * The default implementation returns null, indicating that cloning is not supported.
+     * @param response to send the result back to the AccountManager, will never be null
+     * @param account the account to clone, will never be null
+     * @return a Bundle result or null if the result is to be returned via the response.
+     * @throws NetworkErrorException
+     * @see {@link #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)}
+     */
+    public Bundle getAccountCredentialsForCloning(final AccountAuthenticatorResponse response,
+            final Account account) throws NetworkErrorException {
+        new Thread(new Runnable() {
+            public void run() {
+                Bundle result = new Bundle();
+                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+                response.onResult(result);
+            }
+        }).start();
+        return null;
+    }
+
+    /**
+     * 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
+     * @param account the account to clone, will never be null
+     * @param accountCredentials the Bundle containing the required credentials to create the
+     * account. Contents of the Bundle are only meaningful to the authenticator. This Bundle is
+     * provided by {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}.
+     * @return a Bundle result or null if the result is to be returned via the response.
+     * @throws NetworkErrorException
+     * @see {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}
+     */
+    public Bundle addAccountFromCredentials(final AccountAuthenticatorResponse response,
+            Account account,
+            Bundle accountCredentials) throws NetworkErrorException {
+        new Thread(new Runnable() {
+            public void run() {
+                Bundle result = new Bundle();
+                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+                response.onResult(result);
+            }
+        }).start();
+        return null;
+    }
 }
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 6d9bb1d..6aac723 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -1123,6 +1123,57 @@
     }
 
     /**
+     * Adds a shared account from the primary user to a secondary user. Adding the shared account
+     * doesn't take effect immediately. When the target user starts up, any pending shared accounts
+     * are attempted to be copied to the target user from the primary via calls to the
+     * authenticator.
+     * @param account the account to share
+     * @param user the target user
+     * @return
+     * @hide
+     */
+    public boolean addSharedAccount(final Account account, UserHandle user) {
+        try {
+            boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
+            return val;
+        } catch (RemoteException re) {
+            // won't ever happen
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * @hide
+     * Removes the shared account.
+     * @param account the account to remove
+     * @param user the user to remove the account from
+     * @return
+     */
+    public boolean removeSharedAccount(final Account account, UserHandle user) {
+        try {
+            boolean val = mService.removeSharedAccountAsUser(account, user.getIdentifier());
+            return val;
+        } catch (RemoteException re) {
+            // won't ever happen
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
+     * @hide
+     * @param user
+     * @return
+     */
+    public Account[] getSharedAccounts(UserHandle user) {
+        try {
+            return mService.getSharedAccountsAsUser(user.getIdentifier());
+        } catch (RemoteException re) {
+            // won't ever happen
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
      * Confirms that the user knows the password for an account to make extra
      * sure they are the owner of the account.  The user-entered password can
      * be supplied directly, otherwise the authenticator for this account type
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
index 8860710..58612da 100644
--- a/core/java/android/accounts/IAccountAuthenticator.aidl
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -70,4 +70,17 @@
      * Gets whether or not the account is allowed to be removed.
      */
     void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account);
+
+    /**
+     * Returns a Bundle containing the required credentials to copy the account across users.
+     */
+    void getAccountCredentialsForCloning(in IAccountAuthenticatorResponse response,
+            in Account account);
+
+    /**
+     * Uses the Bundle containing credentials from another instance of the authenticator to create
+     * a copy of the account on this user.
+     */
+    void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account,
+            in Bundle accountCredentials);
 }
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index dbb4924..47b257d 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -58,4 +58,9 @@
         in Bundle options, boolean expectActivityLaunch, int userId);
     void getAuthTokenLabel(in IAccountManagerResponse response, String accountType,
         String authTokenType);
+
+    /* Shared accounts */
+    boolean addSharedAccountAsUser(in Account account, int userId);
+    Account[] getSharedAccountsAsUser(int userId);
+    boolean removeSharedAccountAsUser(in Account account, int userId);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f09c2fe..6d55dd5 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -50,6 +50,7 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.Display;
 
@@ -1064,7 +1065,7 @@
     public int installExistingPackage(String packageName)
             throws NameNotFoundException {
         try {
-            int res = mPM.installExistingPackage(packageName);
+            int res = mPM.installExistingPackageAsUser(packageName, UserHandle.myUserId());
             if (res == INSTALL_FAILED_INVALID_URI) {
                 throw new NameNotFoundException("Package " + packageName + " doesn't exist");
             }
@@ -1126,7 +1127,7 @@
     @Override
     public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
         try {
-            mPM.deletePackage(packageName, observer, flags);
+            mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);
         } catch (RemoteException e) {
             // Should never happen!
         }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c5a382d..b6aeb84 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1019,7 +1019,7 @@
     }
 
     /**
-     * Call {@link Activity#startActivity(Intent)} on the fragment's
+     * Call {@link Activity#startActivity(Intent)} from the fragment's
      * containing Activity.
      *
      * @param intent The intent to start.
@@ -1029,7 +1029,7 @@
     }
     
     /**
-     * Call {@link Activity#startActivity(Intent, Bundle)} on the fragment's
+     * Call {@link Activity#startActivity(Intent, Bundle)} from the fragment's
      * containing Activity.
      *
      * @param intent The intent to start.
@@ -1051,7 +1051,7 @@
     }
 
     /**
-     * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
+     * Call {@link Activity#startActivityForResult(Intent, int)} from the fragment's
      * containing Activity.
      */
     public void startActivityForResult(Intent intent, int requestCode) {
@@ -1059,7 +1059,7 @@
     }
 
     /**
-     * Call {@link Activity#startActivityForResult(Intent, int, Bundle)} on the fragment's
+     * Call {@link Activity#startActivityForResult(Intent, int, Bundle)} from the fragment's
      * containing Activity.
      */
     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
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..f7933d38 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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a368451..a32a201 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -194,20 +194,22 @@
     void setInstallerPackageName(in String targetPackage, in String installerPackageName);
 
     /**
-     * Delete a package.
+     * Delete a package for a specific user.
      *
      * @param packageName The fully qualified name of the package to delete.
      * @param observer a callback to use to notify when the package deletion in finished.
+     * @param userId the id of the user for whom to delete the package
      * @param flags - possible values: {@link #DONT_DELETE_DATA}
      */
-    void deletePackage(in String packageName, IPackageDeleteObserver observer, int flags);
+    void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
+            int userId, int flags);
 
     String getInstallerPackageName(in String packageName);
 
     void addPackageToPreferred(String packageName);
-    
+
     void removePackageFromPreferred(String packageName);
-    
+
     List<PackageInfo> getPreferredPackages(int flags);
 
     void resetPreferredActivities(int userId);
@@ -381,7 +383,7 @@
             in VerificationParams verificationParams,
             in ContainerEncryptionParams encryptionParams);
 
-    int installExistingPackage(String packageName);
+    int installExistingPackageAsUser(String packageName, int userId);
 
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c507245..0d463ee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -691,6 +691,17 @@
     public static final int DELETE_ALL_USERS = 0x00000002;
 
     /**
+     * Flag parameter for {@link #deletePackage} to indicate that, if you are calling
+     * uninstall on a system that has been updated, then don't do the normal process
+     * of uninstalling the update and rolling back to the older system version (which
+     * needs to happen for all users); instead, just mark the app as uninstalled for
+     * the current user.
+     *
+     * @hide
+     */
+    public static final int DELETE_SYSTEM_APP = 0x00000004;
+
+    /**
      * Return code for when package deletion succeeds. This is passed to the
      * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
      * succeeded in deleting the package.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e1887bc..5eac903 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1941,6 +1941,28 @@
                     return false;
                 }
 
+            } else if (tagName.equals("library")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestLibrary_name);
+
+                sa.recycle();
+
+                if (lname != null) {
+                    if (owner.libraryNames == null) {
+                        owner.libraryNames = new ArrayList<String>();
+                    }
+                    if (!owner.libraryNames.contains(lname)) {
+                        owner.libraryNames.add(lname.intern());
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+
             } else if (tagName.equals("uses-library")) {
                 sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.AndroidManifestUsesLibrary);
@@ -3182,7 +3204,8 @@
         public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
 
         public ArrayList<String> protectedBroadcasts;
-        
+
+        public ArrayList<String> libraryNames = null;
         public ArrayList<String> usesLibraries = null;
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
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/pm/Signature.java b/core/java/android/content/pm/Signature.java
index 9c9340d..752bf8b 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.ByteArrayInputStream;
 import java.lang.ref.SoftReference;
 import java.security.PublicKey;
@@ -198,4 +200,13 @@
     private Signature(Parcel source) {
         mSignature = source.createByteArray();
     }
+
+    /**
+     * Test if given {@link Signature} sets are exactly equal.
+     *
+     * @hide
+     */
+    public static boolean areExactMatch(Signature[] a, Signature[] b) {
+        return ArrayUtils.containsAll(a, b) && ArrayUtils.containsAll(b, a);
+    }
 }
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 593f826..4c87830 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -97,6 +97,10 @@
         return (flags & FLAG_GUEST) == FLAG_GUEST;
     }
 
+    public boolean isRestricted() {
+        return (flags & FLAG_RESTRICTED) == FLAG_RESTRICTED;
+    }
+
     public UserInfo() {
     }
 
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/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 41384d2..af4c074 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -114,11 +114,90 @@
     /** A constant describing an ambient temperature sensor type */
     public static final int TYPE_AMBIENT_TEMPERATURE = 13;
 
-    /** 
+    /**
+     * A constant describing a magnetic field uncalibrated sensor type. See
+     * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+     * details.
+     * <p>
+     * No periodic calibration is performed (ie: there are no discontinuities
+     * in the data stream while using this sensor). Assumptions that the
+     * magnetic field is due to the Earth's poles is avoided. Factory calibration
+     * and temperature compensation is still performed.
+     * </p>
+     */
+    public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14;
+
+    /**
+     * Identical to {@link #TYPE_ROTATION_VECTOR} except that it doesn't
+     * use the geomagnetic field. Therefore the Y axis doesn't
+     * point north, but instead to some other reference, that reference is
+     * allowed to drift by the same order of magnitude as the gyroscope
+     * drift around the Z axis.
+     * <p>
+     * In the ideal case, a phone rotated and returning to the same real-world
+     * orientation should report the same game rotation vector
+     * (without using the earth's geomagnetic field). However, the orientation
+     * may drift somewhat over time.
+     * </p>
+     */
+
+    public static final int TYPE_GAME_ROTATION_VECTOR = 15;
+
+    /**
+     * A constant describing a gyroscope uncalibrated sensor type. See
+     * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+     * details.
+     * <p>
+     * No gyro-drift compensation is performed.
+     * Factory calibration and temperature compensation is still applied
+     * to the rate of rotation (angular speeds).
+     * </p>
+     */
+    public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16;
+
+    /**
+     * A constant describing the significant motion trigger sensor.
+     * See {@link android.hardware.SensorEvent#values} for more details.
+     * <p>
+     * It triggers when an event occurs and then automatically disables
+     * itself. The sensor continues to operate while the device is asleep
+     * and will automatically wake the device to notify when significant
+     * motion is detected. The application does not need to hold any wake
+     * locks for this sensor to trigger.
+     * </p>
+     */
+    public static final int TYPE_SIGNIFICANT_MOTION = 17;
+
+    /**
      * A constant describing all sensor types.
      */
     public static final int TYPE_ALL = -1;
 
+    /* Reporting mode constants for sensors. Each sensor will have exactly one
+       reporting mode associated with it. */
+    // Events are reported at a constant rate.
+    static int REPORTING_MODE_CONTINUOUS = 1;
+
+    // Events are reported only when the value changes.
+    static int REPORTING_MODE_ON_CHANGE = 2;
+
+    // Upon detection of an event, the sensor deactivates itself and then sends a single event.
+    static int REPORTING_MODE_ONE_SHOT = 3;
+
+    // Note: This needs to be updated, whenever a new sensor is added.
+    private static int[] sSensorReportingModes = {
+            REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+            REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+            REPORTING_MODE_ON_CHANGE, REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS,
+            REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ON_CHANGE,
+            REPORTING_MODE_ON_CHANGE, REPORTING_MODE_CONTINUOUS, REPORTING_MODE_CONTINUOUS,
+            REPORTING_MODE_CONTINUOUS, REPORTING_MODE_ONE_SHOT };
+
+    static int getReportingMode(Sensor sensor) {
+        // mType starts from offset 1.
+        return sSensorReportingModes[sensor.mType - 1];
+    }
+
     /* Some of these fields are set only by the native bindings in
      * SensorManager.
      */
@@ -132,7 +211,6 @@
     private float   mPower;
     private int     mMinDelay;
 
-
     Sensor() {
     }
 
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 51a17c1..84c9131 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,11 +17,9 @@
 package android.hardware;
 
 /**
- * <p>
  * This class represents a {@link android.hardware.Sensor Sensor} event and
  * holds informations such as the sensor's type, the time-stamp, accuracy and of
  * course the sensor's {@link SensorEvent#values data}.
- * </p>
  *
  * <p>
  * <u>Definition of the coordinate system used by the SensorEvent API.</u>
@@ -67,15 +65,9 @@
      * Sensor.TYPE_ACCELEROMETER}:</h4> All values are in SI units (m/s^2)
      * 
      * <ul>
-     * <p>
-     * values[0]: Acceleration minus Gx on the x-axis
-     * </p>
-     * <p>
-     * values[1]: Acceleration minus Gy on the y-axis
-     * </p>
-     * <p>
-     * values[2]: Acceleration minus Gz on the z-axis
-     * </p>
+     * <li> values[0]: Acceleration minus Gx on the x-axis </li>
+     * <li> values[1]: Acceleration minus Gy on the y-axis </li>
+     * <li> values[2]: Acceleration minus Gz on the z-axis </li>
      * </ul>
      * 
      * <p>
@@ -165,15 +157,9 @@
      * definition of positive rotation and does not agree with the definition of
      * roll given earlier.
      * <ul>
-     * <p>
-     * values[0]: Angular speed around the x-axis
-     * </p>
-     * <p>
-     * values[1]: Angular speed around the y-axis
-     * </p>
-     * <p>
-     * values[2]: Angular speed around the z-axis
-     * </p>
+     * <li> values[0]: Angular speed around the x-axis </li>
+     * <li> values[1]: Angular speed around the y-axis </li>
+     * <li> values[2]: Angular speed around the z-axis </li>
      * </ul>
      * <p>
      * Typically the output of the gyroscope is integrated over time to
@@ -233,22 +219,19 @@
      * </p>
      * <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
      * <ul>
-     * <p>
-     * values[0]: Ambient light level in SI lux units
+     * <li>values[0]: Ambient light level in SI lux units </li>
      * </ul>
      * 
      * <h4>{@link android.hardware.Sensor#TYPE_PRESSURE Sensor.TYPE_PRESSURE}:</h4>
      * <ul>
-     * <p>
-     * values[0]: Atmospheric pressure in hPa (millibar)
+     * <li>values[0]: Atmospheric pressure in hPa (millibar) </li>
      * </ul>
      *
      * <h4>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
      * </h4>
      * 
      * <ul>
-     * <p>
-     * values[0]: Proximity sensor distance measured in centimeters
+     * <li>values[0]: Proximity sensor distance measured in centimeters </li>
      * </ul>
      * 
      * <p>
@@ -304,39 +287,23 @@
      * </p>
      *
      * <ul>
-     * <p>
-     * values[0]: x*sin(&#952/2)
-     * </p>
-     * <p>
-     * values[1]: y*sin(&#952/2)
-     * </p>
-     * <p>
-     * values[2]: z*sin(&#952/2)
-     * </p>
-     * <p>
-     * values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i>
-     * </p>
+     * <li> values[0]: x*sin(&#952/2) </li>
+     * <li> values[1]: y*sin(&#952/2) </li>
+     * <li> values[2]: z*sin(&#952/2) </li>
+     * <li> values[3]: cos(&#952/2) <i>(optional: only if value.length = 4)</i> </li>
      * </ul>
      *
      * <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
      * Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
      * 
      * <ul>
-     * <p>
-     * values[0]: Azimuth, angle between the magnetic north direction and the
+     * <li> values[0]: Azimuth, angle between the magnetic north direction and the
      * y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South,
-     * 270=West
-     * </p>
-     * 
-     * <p>
-     * values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
-     * values when the z-axis moves <b>toward</b> the y-axis.
-     * </p>
-     * 
-     * <p>
-     * values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
-     * when the x-axis moves <b>toward</b> the z-axis.
-     * </p>
+     * 270=West </li>
+     * <li> values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
+     * values when the z-axis moves <b>toward</b> the y-axis. </li>
+     * <li> values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
+     * when the x-axis moves <b>toward</b> the z-axis. </li>
      * </ul>
      * 
      * <p>
@@ -364,9 +331,7 @@
      * <h4>{@link android.hardware.Sensor#TYPE_RELATIVE_HUMIDITY
      * Sensor.TYPE_RELATIVE_HUMIDITY}:</h4>
      * <ul>
-     * <p>
-     * values[0]: Relative ambient air humidity in percent
-     * </p>
+     * <li> values[0]: Relative ambient air humidity in percent </li>
      * </ul>
      * <p>
      * When relative ambient air humidity and ambient temperature are
@@ -423,21 +388,58 @@
      * </h4>
      *
      * <ul>
-     * <p>
-     * values[0]: ambient (room) temperature in degree Celsius.
+     * <li> values[0]: ambient (room) temperature in degree Celsius.</li>
      * </ul>
      *
      * @see SensorEvent
      * @see GeomagneticField
+     *
+     * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} </h4>
+     * All values are in micro-Tesla (uT) and measure the ambient magnetic field
+     * in the X, Y and Z axis.
+     * <p>
+     * No periodic calibration is performed (ie: there are no discontinuities
+     * in the data stream while using this sensor). Assumptions that the the
+     * magnetic field is due to the Earth's poles is avoided. Factory calibration
+     * and temperature compensation is still performed.
+     * </p>
+     *
+     * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} </h4>
+     * All values are in radians/second and measure the rate of rotation
+     * around the X, Y and Z axis. An estimation of the drift on each axis is
+     * reported as well.
+     * <p>
+     * No gyro-drift compensation is performed. Factory calibration and temperature
+     * compensation is still applied to the rate of rotation (angular speeds).
+     * </p>
+     * <p>
+     * The coordinate system is the same as is used for the
+     * {@link android.hardware.Sensor#TYPE_ACCELEROMETER}
+     * Rotation is positive in the counter-clockwise direction (right-hand rule).
+     * That is, an observer looking from some positive location on the x, y or z axis
+     * at a device positioned on the origin would report positive rotation if the device
+     * appeared to be rotating counter clockwise.
+     * The range would at least be 17.45 rad/s (ie: ~1000 deg/s).
+     * <ul>
+     * <li> values[0] : angular speed (w/o drift compensation) around the X axis in rad/s </li>
+     * <li> values[1] : angular speed (w/o drift compensation) around the Y axis in rad/s </li>
+     * <li> values[2] : angular speed (w/o drift compensation) around the Z axis in rad/s </li>
+     * <li> values[3] : estimated drift around X axis in rad/s </li>
+     * <li> values[4] : estimated drift around Y axis in rad/s </li>
+     * <li> values[5] : estimated drift around Z axis in rad/s </li>
+     * </ul>
+     * </p>
+     * <h4></h4>
+     * <h4> Pro Tip: Always use the length of the values array while performing operations
+     * on it. In earlier versions, this used to be always 3 which has changed now. </h4>
      */
-
     public final float[] values;
 
     /**
      * The sensor that generated this event. See
      * {@link android.hardware.SensorManager SensorManager} for details.
      */
-   public Sensor sensor;
+    public Sensor sensor;
 
     /**
      * The accuracy of this event. See {@link android.hardware.SensorManager
@@ -445,13 +447,11 @@
      */
     public int accuracy;
 
-
     /**
      * The time in nanosecond at which the event happened
      */
     public long timestamp;
 
-
     SensorEvent(int size) {
         values = new float[size];
     }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index c0d2fae..ce7bc7e 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -38,7 +38,11 @@
  * hours. Note that the system will <i>not</i> disable sensors automatically when
  * the screen turns off.
  * </p>
- *
+ * <p class="note">
+ * Note: Don't use this mechanism with a Trigger Sensor, have a look
+ * at {@link TriggerEventListener}. {@link Sensor#TYPE_SIGNIFICANT_MOTION}
+ * is an example of a trigger sensor.
+ * </p>
  * <pre class="prettyprint">
  * public class SensorActivity extends Activity, implements SensorEventListener {
  *     private final SensorManager mSensorManager;
@@ -515,6 +519,12 @@
     /**
      * Unregisters a listener for the sensors with which it is registered.
      *
+     * <p class="note"></p>
+     * Note: Don't use this method with a one shot trigger sensor such as
+     * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+     * Use {@link #cancelTriggerSensor(TriggerEventListener, Sensor)} instead.
+     * </p>
+     *
      * @param listener
      *        a SensorEventListener object
      *
@@ -524,6 +534,7 @@
      * @see #unregisterListener(SensorEventListener)
      * @see #registerListener(SensorEventListener, Sensor, int)
      *
+     * @throws IllegalArgumentException when sensor is a trigger sensor.
      */
     public void unregisterListener(SensorEventListener listener, Sensor sensor) {
         if (listener == null || sensor == null) {
@@ -558,6 +569,12 @@
      * Registers a {@link android.hardware.SensorEventListener
      * SensorEventListener} for the given sensor.
      *
+     * <p class="note"></p>
+     * Note: Don't use this method with a one shot trigger sensor such as
+     * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+     * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+     * </p>
+     *
      * @param listener
      *        A {@link android.hardware.SensorEventListener SensorEventListener}
      *        object.
@@ -581,6 +598,7 @@
      * @see #unregisterListener(SensorEventListener)
      * @see #unregisterListener(SensorEventListener, Sensor)
      *
+     * @throws IllegalArgumentException when sensor is null or a trigger sensor
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
         return registerListener(listener, sensor, rate, null);
@@ -590,6 +608,12 @@
      * Registers a {@link android.hardware.SensorEventListener
      * SensorEventListener} for the given sensor.
      *
+     * <p class="note"></p>
+     * Note: Don't use this method with a one shot trigger sensor such as
+     * {@link Sensor#TYPE_SIGNIFICANT_MOTION}.
+     * Use {@link #requestTriggerSensor(TriggerEventListener, Sensor)} instead.
+     * </p>
+     *
      * @param listener
      *        A {@link android.hardware.SensorEventListener SensorEventListener}
      *        object.
@@ -617,6 +641,7 @@
      * @see #unregisterListener(SensorEventListener)
      * @see #unregisterListener(SensorEventListener, Sensor)
      *
+     * @throws IllegalArgumentException when sensor is null or a trigger sensor
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
             Handler handler) {
@@ -1304,6 +1329,68 @@
         Q[3] = rv[2];
     }
 
+    /**
+     * Requests receiving trigger events for a trigger sensor.
+     *
+     * <p>
+     * When the sensor detects a trigger event condition, such as significant motion in
+     * the case of the {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the provided trigger listener
+     * will be invoked once and then its request to receive trigger events will be canceled.
+     * To continue receiving trigger events, the application must request to receive trigger
+     * events again.
+     * </p>
+     *
+     * @param listener The listener on which the
+     *        {@link TriggerEventListener#onTrigger(TriggerEvent)} will be delivered.
+     * @param sensor The sensor to be enabled.
+     *
+     * @return true if the sensor was successfully enabled.
+     *
+     * @throws IllegalArgumentException when sensor is null or not a trigger sensor.
+     */
+    public boolean requestTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+        return requestTriggerSensorImpl(listener, sensor);
+    }
+
+    /**
+     * @hide
+     */
+    protected abstract boolean requestTriggerSensorImpl(TriggerEventListener listener,
+            Sensor sensor);
+
+    /**
+     * Cancels receiving trigger events for a trigger sensor.
+     *
+     * <p>
+     * Note that a Trigger sensor will be auto disabled if
+     * {@link TriggerEventListener#onTrigger(TriggerEvent)} has triggered.
+     * This method is provided in case the user wants to explicitly cancel the request
+     * to receive trigger events.
+     * </p>
+     *
+     * @param listener The listener on which the
+     *        {@link TriggerEventListener#onTrigger(TriggerEvent)}
+     *        is delivered.It should be the same as the one used
+     *        in {@link #requestTriggerSensor(TriggerEventListener, Sensor)}
+     * @param sensor The sensor for which the trigger request should be canceled.
+     *        If null, it cancels receiving trigger for all sensors associated
+     *        with the listener.
+     *
+     * @return true if successfully canceled.
+     *
+     * @throws IllegalArgumentException when sensor is a trigger sensor.
+     */
+    public boolean cancelTriggerSensor(TriggerEventListener listener, Sensor sensor) {
+        return cancelTriggerSensorImpl(listener, sensor);
+    }
+
+    /**
+     * @hide
+     */
+    protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
+            Sensor sensor);
+
+
     private LegacySensorManager getLegacySensorManager() {
         synchronized (mSensorListByType) {
             if (mLegacySensorManager == null) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 9591631..3c231e5 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,18 +16,19 @@
 
 package android.hardware;
 
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-
-import dalvik.system.CloseGuard;
-
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.util.Log;
+import android.util.Pools;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import dalvik.system.CloseGuard;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 /**
  * Sensor manager implementation that communicates with the built-in
@@ -45,22 +46,21 @@
     private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
 
     // Listener list
-    private final ArrayList<SensorEventListenerSensorPair> mListenerDelegates = new ArrayList<SensorEventListenerSensorPair>();
+    private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
+            new HashMap<SensorEventListener, SensorEventQueue>();
+    private final HashMap<TriggerEventListener, TriggerEventQueue> mTriggerListeners =
+            new HashMap<TriggerEventListener, TriggerEventQueue>();
 
-    // Common pool of sensor events.
-    private static SensorEventPool sPool;
+    private static final int MAX_EVENTS = 16;
+    private static Pools.SynchronizedPool<SensorEvent> sSensorEventPool;
+    private static Pools.SynchronizedPool<TriggerEvent> sTriggerEventPool;
 
     // Looper associated with the context in which this instance was created.
     private final Looper mMainLooper;
 
-    // maps a SensorEventListener to a SensorEventQueue
-    private final Hashtable<SensorEventListener, SensorEventQueue> mSensorEventQueueMap;
-
     /** {@hide} */
     public SystemSensorManager(Looper mainLooper) {
         mMainLooper = mainLooper;
-        mSensorEventQueueMap = new Hashtable<SensorEventListener, SensorEventQueue>();
-
         synchronized(sSensorModuleLock) {
             if (!sSensorModuleInitialized) {
                 sSensorModuleInitialized = true;
@@ -81,7 +81,10 @@
                     }
                 } while (i>0);
 
-                sPool = new SensorEventPool( sFullSensorsList.size()*2 );
+                sSensorEventPool = new Pools.SynchronizedPool<SensorEvent>(
+                        sFullSensorsList.size()*2);
+                sTriggerEventPool = new Pools.SynchronizedPool<TriggerEvent>(
+                        sFullSensorsList.size()*2);
             }
         }
     }
@@ -102,128 +105,133 @@
         // Invariants to preserve:
         // - one Looper per SensorEventListener
         // - one Looper per SensorEventQueue
-        // We map SensorEventListeners to a SensorEventQueue, which holds the looper
+        // We map SensorEventListener to a SensorEventQueue, which holds the looper
+        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
 
-        if (sensor == null) throw new NullPointerException("sensor cannot be null");
+        // Trigger Sensors should use the requestTriggerSensor call.
+        if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
 
-        boolean result;
-        synchronized (mSensorEventQueueMap) {
-            // check if we already have this SensorEventListener, Sensor pair
-            // registered -- if so, we ignore the register. This is not ideal
-            // but this is what the implementation has always been doing.
-            for (SensorEventListenerSensorPair l : mListenerDelegates) {
-                if (l.isSameListenerSensorPair(listener, sensor)) {
-                    // already added, just return silently.
-                    return true;
-                }
-            }
-
-            // now find the SensorEventQueue associated to this listener
-            SensorEventQueue queue = mSensorEventQueueMap.get(listener);
-            if (queue != null) {
-                result = queue.addSensor(sensor, delay);
-                if (result) {
-                    // create a new ListenerDelegate for this pair
-                    mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
-                }
-            } else {
+        synchronized (mSensorListeners) {
+            SensorEventQueue queue = mSensorListeners.get(listener);
+            if (queue == null) {
                 Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
-                queue = new SensorEventQueue(listener, looper.getQueue());
-                result = queue.addSensor(sensor, delay);
-                if (result) {
-                    // create a new ListenerDelegate for this pair
-                    mListenerDelegates.add(new SensorEventListenerSensorPair(listener, sensor));
-                    mSensorEventQueueMap.put(listener, queue);
-                } else {
+                queue = new SensorEventQueue(listener, looper);
+                if (!queue.addSensor(sensor, delay)) {
                     queue.dispose();
+                    return false;
                 }
+                mSensorListeners.put(listener, queue);
+                return true;
+            } else {
+                return queue.addSensor(sensor, delay);
             }
         }
-        return result;
     }
 
     /** @hide */
     @Override
     protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
-        synchronized (mSensorEventQueueMap) {
+        // Trigger Sensors should use the cancelTriggerSensor call.
+        if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+            return;
+        }
 
-            // remove this listener/sensor from our list
-            final ArrayList<SensorEventListenerSensorPair> copy =
-                    new ArrayList<SensorEventListenerSensorPair>(mListenerDelegates);
-            int lastIndex = copy.size()-1;
-            for (int i=lastIndex ; i>= 0 ; i--) {
-                if (copy.get(i).isSameListenerSensorPair(listener, sensor)) {
-                    mListenerDelegates.remove(i);
-                }
-            }
-
-            // find the SensorEventQueue associated to this SensorEventListener
-            SensorEventQueue queue = mSensorEventQueueMap.get(listener);
+        synchronized (mSensorListeners) {
+            SensorEventQueue queue = mSensorListeners.get(listener);
             if (queue != null) {
-                if (sensor != null) {
-                    queue.removeSensor(sensor);
+                boolean result;
+                if (sensor == null) {
+                    result = queue.removeAllSensors();
                 } else {
-                    queue.removeAllSensors();
+                    result = queue.removeSensor(sensor);
                 }
-                if (!queue.hasSensors()) {
-                    mSensorEventQueueMap.remove(listener);
+                if (result && !queue.hasSensors()) {
+                    mSensorListeners.remove(listener);
                     queue.dispose();
                 }
             }
         }
     }
 
+    /** @hide */
+    @Override
+    protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+        if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
 
-    /*
-     * ListenerDelegate is essentially a SensorEventListener, Sensor pair
-     * and is associated with a single SensorEventQueue.
-     */
-    private static final class SensorEventListenerSensorPair {
-        private final SensorEventListener mSensorEventListener;
-        private final Sensor mSensor;
-        public SensorEventListenerSensorPair(SensorEventListener listener, Sensor sensor) {
-            mSensorEventListener = listener;
-            mSensor = sensor;
-        }
-        public boolean isSameListenerSensorPair(SensorEventListener listener, Sensor sensor) {
-            // if sensor is null, we match only on the listener
-            if (sensor != null) {
-                return (listener == mSensorEventListener) &&
-                        (sensor.getHandle() == mSensor.getHandle());
+        if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false;
+
+        synchronized (mTriggerListeners) {
+            TriggerEventQueue queue = mTriggerListeners.get(listener);
+            if (queue == null) {
+                queue = new TriggerEventQueue(listener, mMainLooper, this);
+                if (!queue.addSensor(sensor, 0)) {
+                    queue.dispose();
+                    return false;
+                }
+                mTriggerListeners.put(listener, queue);
+                return true;
             } else {
-                return (listener == mSensorEventListener);
+                return queue.addSensor(sensor, 0);
             }
         }
     }
 
+    /** @hide */
+    @Override
+    protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+        if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
+            return false;
+        }
+        synchronized (mTriggerListeners) {
+            TriggerEventQueue queue = mTriggerListeners.get(listener);
+            if (queue != null) {
+                boolean result;
+                if (sensor == null) {
+                    result = queue.removeAllSensors();
+                } else {
+                    result = queue.removeSensor(sensor);
+                }
+                if (result && !queue.hasSensors()) {
+                    mTriggerListeners.remove(listener);
+                    queue.dispose();
+                }
+                return result;
+            }
+            return false;
+        }
+    }
+
     /*
-     * SensorEventQueue is the communication channel with the sensor service,
-     * there is a one-to-one mapping between SensorEventQueue and
-     * SensorEventListener.
+     * BaseEventQueue is the communication channel with the sensor service,
+     * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
+     * the queues and the listeners.
      */
-    private static final class SensorEventQueue {
-        private static native int nativeInitSensorEventQueue(SensorEventQueue eventQ, MessageQueue msgQ, float[] scratch);
+    private static abstract class BaseEventQueue {
+        private native int nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
+                float[] scratch);
         private static native int nativeEnableSensor(int eventQ, int handle, int us);
         private static native int nativeDisableSensor(int eventQ, int handle);
         private static native void nativeDestroySensorEventQueue(int eventQ);
         private int nSensorEventQueue;
-        private final SensorEventListener mListener;
         private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
-        private final SparseIntArray mSensorAccuracies = new SparseIntArray();
-        private final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
+        protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+        protected final SparseBooleanArray mFirstEvent = new SparseBooleanArray();
         private final CloseGuard mCloseGuard = CloseGuard.get();
         private final float[] mScratch = new float[16];
 
-        public SensorEventQueue(SensorEventListener listener, MessageQueue msgQ) {
-            nSensorEventQueue = nativeInitSensorEventQueue(this, msgQ, mScratch);
-            mListener = listener;
+        BaseEventQueue(Looper looper) {
+            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
             mCloseGuard.open("dispose");
         }
+
         public void dispose() {
             dispose(false);
         }
 
         public boolean addSensor(Sensor sensor, int delay) {
+            // Check if already present.
+            if (mActiveSensors.get(sensor.getHandle())) return false;
+
             if (enableSensor(sensor, delay) == 0) {
                 mActiveSensors.put(sensor.getHandle(), true);
                 return true;
@@ -231,7 +239,7 @@
             return false;
         }
 
-        public void removeAllSensors() {
+        public boolean removeAllSensors() {
             for (int i=0 ; i<mActiveSensors.size(); i++) {
                 if (mActiveSensors.valueAt(i) == true) {
                     int handle = mActiveSensors.keyAt(i);
@@ -244,21 +252,24 @@
                     }
                 }
             }
+            return true;
         }
 
-        public void removeSensor(Sensor sensor) {
+        public boolean removeSensor(Sensor sensor) {
             final int handle = sensor.getHandle();
             if (mActiveSensors.get(handle)) {
                 disableSensor(sensor);
                 mActiveSensors.put(sensor.getHandle(), false);
+                return true;
             }
+            return false;
         }
 
         public boolean hasSensors() {
             // no more sensors are set
             return mActiveSensors.indexOfValue(true) >= 0;
         }
-        
+
         @Override
         protected void finalize() throws Throwable {
             try {
@@ -291,17 +302,30 @@
             if (sensor == null) throw new NullPointerException();
             return nativeDisableSensor(nSensorEventQueue, sensor.getHandle());
         }
+        protected abstract void dispatchSensorEvent(int handle, float[] values, int accuracy,
+                long timestamp);
+    }
+
+    static final class SensorEventQueue extends BaseEventQueue {
+        private final SensorEventListener mListener;
+
+        public SensorEventQueue(SensorEventListener listener, Looper looper) {
+            super(looper);
+            mListener = listener;
+        }
 
         // Called from native code.
         @SuppressWarnings("unused")
-        private void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) {
-            // this is always called on the same thread.
-            final SensorEvent t = sPool.getFromPool();
+        @Override
+        protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
+                long timestamp) {
+            final Sensor sensor = sHandleToSensor.get(handle);
+            SensorEvent t = sSensorEventPool.acquire();
+            if (t == null) t = new SensorEvent(MAX_EVENTS);
             try {
-                final Sensor sensor = sHandleToSensor.get(handle);
-                final SensorEventListener listener = mListener;
-                // FIXME: handle more than 3 values
-                System.arraycopy(values, 0, t.values, 0, 3);
+                // Copy the entire values array.
+                // Any changes in length will be handled at the native layer.
+                System.arraycopy(values, 0, t.values, 0, t.values.length);
                 t.timestamp = timestamp;
                 t.accuracy = inAccuracy;
                 t.sensor = sensor;
@@ -313,72 +337,57 @@
                         final int accuracy = mSensorAccuracies.get(handle);
                         if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
                             mSensorAccuracies.put(handle, t.accuracy);
-                            listener.onAccuracyChanged(t.sensor, t.accuracy);
+                            mListener.onAccuracyChanged(t.sensor, t.accuracy);
                         }
                         break;
                     default:
                         // For other sensors, just report the accuracy once
                         if (mFirstEvent.get(handle) == false) {
                             mFirstEvent.put(handle, true);
-                            listener.onAccuracyChanged(
+                            mListener.onAccuracyChanged(
                                     t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
                         }
                         break;
                 }
-                listener.onSensorChanged(t);
+                mListener.onSensorChanged(t);
             } finally {
-                sPool.returnToPool(t);
+                sSensorEventPool.release(t);
             }
         }
     }
 
-    /*
-     * A dumb pool of SensorEvent
-     */
-    private static final class SensorEventPool {
-        private final int mPoolSize;
-        private final SensorEvent mPool[];
-        private int mNumItemsInPool;
+    static final class TriggerEventQueue extends BaseEventQueue {
+        private final TriggerEventListener mListener;
+        private SensorManager mManager;
 
-        private SensorEvent createSensorEvent() {
-            // maximal size for all legacy events is 3
-            return new SensorEvent(3);
+        public TriggerEventQueue(TriggerEventListener listener, Looper looper,
+                SensorManager manager) {
+            super(looper);
+            mListener = listener;
+            mManager = manager;
         }
 
-        SensorEventPool(int poolSize) {
-            mPoolSize = poolSize;
-            mNumItemsInPool = poolSize;
-            mPool = new SensorEvent[poolSize];
-        }
+        // Called from native code.
+        @SuppressWarnings("unused")
+        @Override
+        protected void dispatchSensorEvent(int handle, float[] values, int accuracy, long timestamp) {
+            final Sensor sensor = sHandleToSensor.get(handle);
+            TriggerEvent t = sTriggerEventPool.acquire();
+            if (t == null) t = new TriggerEvent(MAX_EVENTS);
 
-        SensorEvent getFromPool() {
-            SensorEvent t = null;
-            synchronized (this) {
-                if (mNumItemsInPool > 0) {
-                    // remove the "top" item from the pool
-                    final int index = mPoolSize - mNumItemsInPool;
-                    t = mPool[index];
-                    mPool[index] = null;
-                    mNumItemsInPool--;
-                }
-            }
-            if (t == null) {
-                // the pool was empty or this item was removed from the pool for
-                // the first time. In any case, we need to create a new item.
-                t = createSensorEvent();
-            }
-            return t;
-        }
+            try {
+                // Copy the entire values array.
+                // Any changes in length will be handled at the native layer.
+                System.arraycopy(values, 0, t.values, 0, t.values.length);
+                t.timestamp = timestamp;
+                t.sensor = sensor;
 
-        void returnToPool(SensorEvent t) {
-            synchronized (this) {
-                // is there space left in the pool?
-                if (mNumItemsInPool < mPoolSize) {
-                    // if so, return the item to the pool
-                    mNumItemsInPool++;
-                    final int index = mPoolSize - mNumItemsInPool;
-                    mPool[index] = t;
-                }
+                // A trigger sensor should be auto disabled.
+                mManager.cancelTriggerSensorImpl(mListener, sensor);
+
+                mListener.onTrigger(t);
+            } finally {
+                sTriggerEventPool.release(t);
             }
         }
     }
diff --git a/core/java/android/hardware/TriggerEvent.java b/core/java/android/hardware/TriggerEvent.java
new file mode 100644
index 0000000..bdd39f3
--- /dev/null
+++ b/core/java/android/hardware/TriggerEvent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.hardware;
+
+/**
+ * This class represents a Trigger Event - the event
+ * associated with a Trigger Sensor. When the sensor detects a trigger
+ * event condition, such as significant motion in the case of the
+ * {@link Sensor#TYPE_SIGNIFICANT_MOTION}, the {@link TriggerEventListener}
+ * is called with the TriggerEvent. The sensor is automatically canceled
+ * after the trigger.
+ * <p>
+ * This class holds information such as the value of the sensor
+ * when the trigger happened, the timestamp along with detailed
+ * information regarding the Sensor itself.
+ * </p>
+ * @see android.hardware.SensorManager
+ * @see android.hardware.TriggerEvent
+ * @see android.hardware.Sensor
+ */
+public final class TriggerEvent {
+    /**
+     * <p>
+     * The length and contents of the {@link #values values} array depends on
+     * which {@link android.hardware.Sensor sensor} type is being monitored (see
+     * also {@link SensorEvent} for a definition of the coordinate system used).
+     * </p>
+     * <h4> {@link Sensor#TYPE_SIGNIFICANT_MOTION} </h4>
+     * The value field is of length 1. value[0] = 1.0 when the sensor triggers.
+     * 1.0 is the only allowed value.
+     */
+    public final float[] values;
+
+    /**
+     * The sensor that generated this event. See
+     * {@link android.hardware.SensorManager SensorManager} for details.
+     */
+    public Sensor sensor;
+
+    /**
+     * The time in nanosecond at which the event happened
+     */
+    public long timestamp;
+
+    TriggerEvent(int size) {
+        values = new float[size];
+    }
+}
diff --git a/core/java/android/hardware/TriggerEventListener.java b/core/java/android/hardware/TriggerEventListener.java
new file mode 100644
index 0000000..82b8907
--- /dev/null
+++ b/core/java/android/hardware/TriggerEventListener.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hardware;
+
+/**
+ * This class is the listener used to handle Trigger Sensors.
+ * Trigger Sensors are sensors that trigger an event and are automatically
+ * disabled. {@link Sensor#TYPE_SIGNIFICANT_MOTION} is one such example.
+ * <p>
+ * SensorManager lets you access the device's {@link android.hardware.Sensor
+ * sensors}. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)
+ * Context.getSystemService()} with the argument
+ * {@link android.content.Context#SENSOR_SERVICE}.
+ * Usage details are explained in the example below.
+ * </p>
+ *
+ * <pre class="prettyprint">
+ * class TriggerListener extends TriggerEventListener {
+ *     @Override
+ *     public void onTrigger(TriggerEvent event) {
+ *          // Do Work.
+ *
+ *     // As it is a one shot sensor, it will be canceled automatically.
+ *     // SensorManager.requestTriggerSensor(this, mSigMotion); needs to
+ *     // be called again, if needed.
+ *     }
+ * }
+ * public class SensorActivity extends Activity {
+ *     private final SensorManager mSensorManager;
+ *     private final Sensor mSigMotion;
+ *     private final TriggerEventListener mListener = new TriggerEventListener();
+ *
+ *     public SensorActivity() {
+ *         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ *         mSigMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+ *     }
+ *
+ *     protected void onResume() {
+ *         super.onResume();
+ *         mSensorManager.requestTriggerSensor(mListener, mSigMotion);
+ *     }
+ *
+ *     protected void onPause() {
+ *         super.onPause();
+ *         // Call disable to ensure that the trigger request has been canceled.
+ *         mSensorManager.cancelTriggerSensor(mListener, mSigMotion);
+ *     }
+ *
+ * }
+ * </pre>
+ *
+ * @see TriggerEvent
+ * @see Sensor
+ */
+public abstract class TriggerEventListener {
+    /**
+     * The method that will be called when the sensor
+     * is triggered. Override this method in your implementation
+     * of this class.
+     *
+     * @param event The details of the event.
+     */
+    public abstract void onTrigger(TriggerEvent event);
+}
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 3493ea71..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/os/Environment.java b/core/java/android/os/Environment.java
index 1bada67..d9846ec 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Provides access to environment variables.
@@ -36,12 +37,16 @@
     private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
     private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
     private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
+    private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
 
     /** {@hide} */
     public static String DIRECTORY_ANDROID = "Android";
 
-    private static final File ROOT_DIRECTORY
-            = getDirectory("ANDROID_ROOT", "/system");
+    private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
+
+    private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
+            ENV_EMULATED_STORAGE_TARGET);
 
     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
 
@@ -178,7 +183,7 @@
      * Gets the Android root directory.
      */
     public static File getRootDirectory() {
-        return ROOT_DIRECTORY;
+        return DIR_ANDROID_ROOT;
     }
 
     /**
@@ -632,6 +637,19 @@
         return path == null ? new File(defaultPath) : new File(path);
     }
 
+    private static String getCanonicalPathOrNull(String variableName) {
+        String path = System.getenv(variableName);
+        if (path == null) {
+            return null;
+        }
+        try {
+            return new File(path).getCanonicalPath();
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to resolve canonical path for " + path);
+            return null;
+        }
+    }
+
     private static void throwIfSystem() {
         if (Process.myUid() == Process.SYSTEM_UID) {
             Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable());
@@ -649,4 +667,40 @@
         }
         return cur;
     }
+
+    /**
+     * If the given path exists on emulated external storage, return the
+     * translated backing path hosted on internal storage. This bypasses any
+     * emulation later, improving performance. This is <em>only</em> suitable
+     * for read-only access.
+     * <p>
+     * Returns original path if given path doesn't meet these criteria. Callers
+     * must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}
+     * permission.
+     *
+     * @hide
+     */
+    public static File maybeTranslateEmulatedPathToInternal(File path) {
+        // Fast return if not emulated, or missing variables
+        if (!Environment.isExternalStorageEmulated()
+                || CANONCIAL_EMULATED_STORAGE_TARGET == null) {
+            return path;
+        }
+
+        try {
+            final String rawPath = path.getCanonicalPath();
+            if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) {
+                final File internalPath = new File(DIR_MEDIA_STORAGE,
+                        rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length()));
+                if (internalPath.exists()) {
+                    return internalPath;
+                }
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to resolve canonical path for " + path);
+        }
+
+        // Unable to translate to internal path; use original
+        return path;
+    }
 }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 1bcf3e0..4379418 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -36,6 +36,7 @@
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Set;
@@ -656,19 +657,18 @@
         }
     }
 
-    private class SynthesisToFileSpeechDescriptorItem extends SynthesisSpeechItem {
-        private final FileDescriptor mFileDescriptor;
+    private class SynthesisToFileOutputStreamSpeechItem extends SynthesisSpeechItem {
+        private final FileOutputStream mFileOutputStream;
 
-        public SynthesisToFileSpeechDescriptorItem(Object callerIdentity, int callerUid,
-                int callerPid, Bundle params, String text, FileDescriptor fileDescriptor) {
+        public SynthesisToFileOutputStreamSpeechItem(Object callerIdentity, int callerUid,
+                int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) {
             super(callerIdentity, callerUid, callerPid, params, text);
-            mFileDescriptor = fileDescriptor;
+            mFileOutputStream = fileOutputStream;
         }
 
         @Override
         protected AbstractSynthesisCallback createSynthesisCallback() {
-            FileOutputStream fileOutputStream = new FileOutputStream(mFileDescriptor);
-            return new FileSynthesisCallback(fileOutputStream.getChannel());
+            return new FileSynthesisCallback(mFileOutputStream.getChannel());
         }
 
         @Override
@@ -680,6 +680,11 @@
             } else {
                 dispatchOnError();
             }
+            try {
+              mFileOutputStream.close();
+            } catch(IOException e) {
+              Log.w(TAG, "Failed to close output file", e);
+            }
             return status;
         }
     }
@@ -805,9 +810,9 @@
                 return TextToSpeech.ERROR;
             }
 
-            SpeechItem item = new SynthesisToFileSpeechDescriptorItem(caller, Binder.getCallingUid(),
-                    Binder.getCallingPid(), params, text,
-                    fileDescriptor.getFileDescriptor());
+            SpeechItem item = new SynthesisToFileOutputStreamSpeechItem(caller,
+                    Binder.getCallingUid(), Binder.getCallingPid(), params, text,
+                    new ParcelFileDescriptor.AutoCloseOutputStream(fileDescriptor));
             return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
         }
 
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/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 086e78c..685dc70 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -89,13 +89,13 @@
 
     @Override
     void end(Canvas currentCanvas) {
-        if (currentCanvas instanceof GLES20Canvas) {
-            ((GLES20Canvas) currentCanvas).resume();
-        }
         HardwareCanvas canvas = getCanvas();
         if (canvas != null) {
             canvas.onPostDraw();
         }
+        if (currentCanvas instanceof GLES20Canvas) {
+            ((GLES20Canvas) currentCanvas).resume();
+        }
     }
 
     @Override
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
new file mode 100644
index 0000000..d14bb48
--- /dev/null
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -0,0 +1,23 @@
+/* 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 android.view;
+
+/** {@hide} */
+interface IWindowFocusObserver
+{
+    void focusGained(IBinder inputToken);
+    void focusLost(IBinder inputToken);
+}
diff --git a/core/java/android/view/IWindowId.aidl b/core/java/android/view/IWindowId.aidl
new file mode 100644
index 0000000..ac8f2db
--- /dev/null
+++ b/core/java/android/view/IWindowId.aidl
@@ -0,0 +1,26 @@
+/* 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 android.view;
+
+import android.view.IWindowFocusObserver;
+
+/** {@hide} */
+interface IWindowId
+{
+    void registerFocusObserver(IWindowFocusObserver observer);
+    void unregisterFocusObserver(IWindowFocusObserver observer);
+    boolean isFocused();
+}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 0a8e609..c32a2c9 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.view.InputChannel;
 import android.view.IWindow;
+import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.Surface;
@@ -188,4 +189,6 @@
      * Notifies that a rectangle on the screen has been requested.
      */
     void onRectangleOnScreenRequested(IBinder token, in Rect rectangle, boolean immediate);
+
+    IWindowId getWindowId(IBinder window);
 }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 3bb9c01d..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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ded2f47..df07dcd 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -20,6 +20,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.view.Surface;
 import android.os.IBinder;
 import android.os.SystemProperties;
 import android.util.Log;
@@ -39,6 +40,8 @@
 
     private static native Bitmap nativeScreenshot(IBinder displayToken,
             int width, int height, int minLayer, int maxLayer, boolean allLayers);
+    private static native void nativeScreenshot(IBinder displayToken, Surface consumer,
+            int width, int height, int minLayer, int maxLayer, boolean allLayers);
 
     private static native void nativeOpenTransaction();
     private static native void nativeCloseTransaction();
@@ -156,17 +159,6 @@
     public static final int FX_SURFACE_NORMAL   = 0x00000000;
 
     /**
-     * Surface creation flag: Creates a Blur surface.
-     * Everything behind this surface is blurred by some amount.
-     * The quality and refresh speed of the blur effect is not settable or guaranteed.
-     * It is an error to lock a Blur surface, since it doesn't have a backing store.
-     *
-     * @deprecated
-     */
-    @Deprecated
-    public static final int FX_SURFACE_BLUR = 0x00010000;
-
-    /**
      * Surface creation flag: Creates a Dim surface.
      * Everything behind this surface is dimmed by the amount specified
      * in {@link #setAlpha}.  It is an error to lock a Dim surface, since it
@@ -176,11 +168,6 @@
     public static final int FX_SURFACE_DIM = 0x00020000;
 
     /**
-     *
-     */
-    public static final int FX_SURFACE_SCREENSHOT = 0x00030000;
-
-    /**
      * Mask used for FX values above.
      *
      */
@@ -524,10 +511,60 @@
         return nativeGetBuiltInDisplay(builtInDisplayId);
     }
 
-    
+
+    /**
+     * Copy the current screen contents into the provided {@link Surface}
+     *
+     * @param display The display to take the screenshot of.
+     * @param consumer The {@link Surface} to take the screenshot into.
+     * @param width The desired width of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     * @param height The desired height of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     * @param minLayer The lowest (bottom-most Z order) surface layer to
+     * include in the screenshot.
+     * @param maxLayer The highest (top-most Z order) surface layer to
+     * include in the screenshot.
+     */
+    public static void screenshot(IBinder display, Surface consumer,
+            int width, int height, int minLayer, int maxLayer) {
+        screenshot(display, consumer, width, height, minLayer, maxLayer, false);
+    }
+
+    /**
+     * Copy the current screen contents into the provided {@link Surface}
+     *
+     * @param display The display to take the screenshot of.
+     * @param consumer The {@link Surface} to take the screenshot into.
+     * @param width The desired width of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     * @param height The desired height of the returned bitmap; the raw
+     * screen will be scaled down to this size.
+     */
+    public static void screenshot(IBinder display, Surface consumer,
+            int width, int height) {
+        screenshot(display, consumer, width, height, 0, 0, true);
+    }
+
+    /**
+     * Copy the current screen contents into the provided {@link Surface}
+     *
+     * @param display The display to take the screenshot of.
+     * @param consumer The {@link Surface} to take the screenshot into.
+     */
+    public static void screenshot(IBinder display, Surface consumer) {
+        screenshot(display, consumer, 0, 0, 0, 0, true);
+    }
+
+
     /**
      * Copy the current screen contents into a bitmap and return it.
      *
+     * CAVEAT: Versions of screenshot that return a {@link Bitmap} can
+     * be extremely slow; avoid use unless absolutely necessary; prefer
+     * the versions that use a {@link Surface} instead, such as
+     * {@link SurfaceControl#screenshot(IBinder, Surface)}.
+     *
      * @param width The desired width of the returned bitmap; the raw
      * screen will be scaled down to this size.
      * @param height The desired height of the returned bitmap; the raw
@@ -538,25 +575,36 @@
      * include in the screenshot.
      * @return Returns a Bitmap containing the screen contents, or null
      * if an error occurs.
-     *
      */
     public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) {
         // TODO: should take the display as a parameter
-        IBinder displayToken = SurfaceControl.getBuiltInDisplay(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+        IBinder displayToken = SurfaceControl.getBuiltInDisplay(
+                SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
         return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false);
     }
 
     /**
      * Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all
      * Surfaces in the screenshot.
-     *
      */
     public static Bitmap screenshot(int width, int height) {
         // TODO: should take the display as a parameter
-        IBinder displayToken = SurfaceControl.getBuiltInDisplay(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+        IBinder displayToken = SurfaceControl.getBuiltInDisplay(
+                SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
         return nativeScreenshot(displayToken, width, height, 0, 0, true);
     }
     
+    private static void screenshot(IBinder display, Surface consumer,
+            int width, int height, int minLayer, int maxLayer, boolean allLayers) {
+        if (display == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        if (consumer == null) {
+            throw new IllegalArgumentException("consumer must not be null");
+        }
+        nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers);
+    }
+
     private static void checkHeadless() {
         if (HEADLESS) {
             throw new UnsupportedOperationException("Device is headless");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ab8f934..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;
@@ -12021,6 +12022,26 @@
     }
 
     /**
+     * Retrieve the {@link WindowId} for the window this view is
+     * currently attached to.
+     */
+    public WindowId getWindowId() {
+        if (mAttachInfo == null) {
+            return null;
+        }
+        if (mAttachInfo.mWindowId == null) {
+            try {
+                mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(
+                        mAttachInfo.mWindowToken);
+                mAttachInfo.mWindowId = new WindowId(
+                        mAttachInfo.mIWindowId);
+            } catch (RemoteException e) {
+            }
+        }
+        return mAttachInfo.mWindowId;
+    }
+
+    /**
      * Retrieve a unique token identifying the top-level "real" window of
      * the window that this view is attached to.  That is, this is like
      * {@link #getWindowToken}, except if the window this view in is a panel
@@ -17898,6 +17919,9 @@
 
         HardwareCanvas mHardwareCanvas;
 
+        IWindowId mIWindowId;
+        WindowId mWindowId;
+
         /**
          * The top view of the hierarchy.
          */
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5105fda..442dfdb 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -70,6 +70,22 @@
  * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
  * guide.</p></div>
  *
+ * <p>Here is a complete implementation of a custom ViewGroup that implements
+ * a simple {@link android.widget.FrameLayout} along with the ability to stack
+ * children in left and right gutters.</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
+ *      Complete}
+ *
+ * <p>If you are implementing XML layout attributes as shown in the example, this is the
+ * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
+ *
+ * <p>Finally the layout manager can be used in an XML layout like so:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
+ *
  * @attr ref android.R.styleable#ViewGroup_clipChildren
  * @attr ref android.R.styleable#ViewGroup_clipToPadding
  * @attr ref android.R.styleable#ViewGroup_layoutAnimation
@@ -1152,8 +1168,10 @@
                 }
 
                 mDragNotifiedChildren.clear();
-                mCurrentDrag.recycle();
-                mCurrentDrag = null;
+                if (mCurrentDrag != null) {
+                    mCurrentDrag.recycle();
+                    mCurrentDrag = null;
+                }
             }
 
             // We consider drag-ended to have been handled if one of our children
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/view/WindowId.java b/core/java/android/view/WindowId.java
new file mode 100644
index 0000000..c4cda2c7
--- /dev/null
+++ b/core/java/android/view/WindowId.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.HashMap;
+
+/**
+ * Safe identifier for a window.  This currently allows you to retrieve and observe
+ * the input focus state of the window.  Most applications will
+ * not use this, instead relying on the simpler (and more efficient) methods available
+ * on {@link View}.  This classes is useful when window input interactions need to be
+ * done across processes: the class itself is a Parcelable that can be passed to other
+ * processes for them to interact with your window, and it provides a limited safe API
+ * that doesn't allow the other process to negatively harm your window.
+ */
+public class WindowId implements Parcelable {
+    private final IWindowId mToken;
+
+    /**
+     * Subclass for observing changes to the focus state of an {@link WindowId}.
+     * You should use the same instance of this class for observing multiple
+     * {@link WindowId} objects, since this class is fairly heavy-weight -- the
+     * base class includes all of the mechanisms for connecting to and receiving updates
+     * from the window.
+     */
+    public static abstract class FocusObserver {
+        final IWindowFocusObserver.Stub mIObserver = new IWindowFocusObserver.Stub() {
+
+            @Override
+            public void focusGained(IBinder inputToken) {
+                WindowId token;
+                synchronized (mRegistrations) {
+                    token = mRegistrations.get(inputToken);
+                }
+                if (mHandler != null) {
+                    mHandler.sendMessage(mHandler.obtainMessage(1, token));
+                } else {
+                    onFocusGained(token);
+                }
+            }
+
+            @Override
+            public void focusLost(IBinder inputToken) {
+                WindowId token;
+                synchronized (mRegistrations) {
+                    token = mRegistrations.get(inputToken);
+                }
+                if (mHandler != null) {
+                    mHandler.sendMessage(mHandler.obtainMessage(2, token));
+                } else {
+                    onFocusLost(token);
+                }
+            }
+        };
+
+        final HashMap<IBinder, WindowId> mRegistrations
+                = new HashMap<IBinder, WindowId>();
+
+        class H extends Handler {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case 1:
+                        onFocusGained((WindowId)msg.obj);
+                        break;
+                    case 2:
+                        onFocusLost((WindowId)msg.obj);
+                        break;
+                    default:
+                        super.handleMessage(msg);
+                }
+            }
+        }
+
+        final Handler mHandler;
+
+        /**
+         * Construct a new observer.  This observer will be configured so that all
+         * of its callbacks are dispatched on the current calling thread.
+         */
+        public FocusObserver() {
+            mHandler = new H();
+        }
+
+        /**
+         * Called when one of the monitored windows gains input focus.
+         */
+        public abstract void onFocusGained(WindowId token);
+
+        /**
+         * Called when one of the monitored windows loses input focus.
+         */
+        public abstract void onFocusLost(WindowId token);
+    }
+
+    /**
+     * Retrieve the current focus state of the associated window.
+     */
+    public boolean isFocused() {
+        try {
+            return mToken.isFocused();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Start monitoring for changes in the focus state of the window.
+     */
+    public void registerFocusObserver(FocusObserver observer) {
+        synchronized (observer.mRegistrations) {
+            if (observer.mRegistrations.containsKey(mToken.asBinder())) {
+                throw new IllegalStateException(
+                        "Focus observer already registered with input token");
+            }
+            observer.mRegistrations.put(mToken.asBinder(), this);
+            try {
+                mToken.registerFocusObserver(observer.mIObserver);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
+     * Stop monitoring changes in the focus state of the window.
+     */
+    public void unregisterFocusObserver(FocusObserver observer) {
+        synchronized (observer.mRegistrations) {
+            if (observer.mRegistrations.remove(mToken.asBinder()) == null) {
+                throw new IllegalStateException("Focus observer not registered with input token");
+            }
+            try {
+                mToken.unregisterFocusObserver(observer.mIObserver);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
+     * Comparison operator on two IntentSender objects, such that true
+     * is returned then they both represent the same operation from the
+     * same package.
+     */
+    @Override
+    public boolean equals(Object otherObj) {
+        if (otherObj instanceof WindowId) {
+            return mToken.asBinder().equals(((WindowId) otherObj)
+                    .mToken.asBinder());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mToken.asBinder().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("IntentSender{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(": ");
+        sb.append(mToken != null ? mToken.asBinder() : null);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStrongBinder(mToken.asBinder());
+    }
+
+    public static final Parcelable.Creator<WindowId> CREATOR
+            = new Parcelable.Creator<WindowId>() {
+        public WindowId createFromParcel(Parcel in) {
+            IBinder target = in.readStrongBinder();
+            return target != null ? new WindowId(target) : null;
+        }
+
+        public WindowId[] newArray(int size) {
+            return new WindowId[size];
+        }
+    };
+
+    /** @hide */
+    public IWindowId getTarget() {
+        return mToken;
+    }
+
+    /** @hide */
+    public WindowId(IWindowId target) {
+        mToken = target;
+    }
+
+    /** @hide */
+    public WindowId(IBinder target) {
+        mToken = IWindowId.Stub.asInterface(target);
+    }
+}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 0b7e92f..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.
@@ -1322,7 +1328,7 @@
     private native void nativeSslCertErrorCancel(int handle, int certError);
 
     native void nativeSslClientCert(int handle,
-                                    int ctx,
+                                    long ctx,
                                     byte[][] asn1DerEncodedCertificateChain);
 
     native void nativeSslClientCert(int handle,
diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java
index dac1510..f5a60f6 100644
--- a/core/java/android/webkit/ClientCertRequestHandler.java
+++ b/core/java/android/webkit/ClientCertRequestHandler.java
@@ -75,7 +75,7 @@
     /**
      * Proceed with the specified private key bytes and client certificate chain.
      */
-    private void setSslClientCertFromCtx(final int ctx, final byte[][] chainBytes) {
+    private void setSslClientCertFromCtx(final long ctx, final byte[][] chainBytes) {
         post(new Runnable() {
                 public void run() {
                     mBrowserFrame.nativeSslClientCert(mHandle, ctx, chainBytes);
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index fc5df2d..684ec07 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -54,14 +54,15 @@
     // The private status of the view that created this player
     private IsPrivateBrowsingEnabledGetter mIsPrivateBrowsingEnabledGetter;
 
-    private static int IDLE        =  0;
-    private static int INITIALIZED =  1;
-    private static int PREPARED    =  2;
-    private static int STARTED     =  4;
-    private static int COMPLETE    =  5;
-    private static int PAUSED      =  6;
-    private static int STOPPED     = -2;
-    private static int ERROR       = -1;
+    private static int IDLE                =  0;
+    private static int INITIALIZED         =  1;
+    private static int PREPARED            =  2;
+    private static int STARTED             =  4;
+    private static int COMPLETE            =  5;
+    private static int PAUSED              =  6;
+    private static int PAUSED_TRANSITORILY =  7;
+    private static int STOPPED             = -2;
+    private static int ERROR               = -1;
 
     private int mState = IDLE;
 
@@ -247,7 +248,7 @@
             // resume playback
             if (mMediaPlayer == null) {
                 resetMediaPlayer();
-            } else if (mState != ERROR && !mMediaPlayer.isPlaying()) {
+            } else if (mState == PAUSED_TRANSITORILY && !mMediaPlayer.isPlaying()) {
                 mMediaPlayer.start();
                 mState = STARTED;
             }
@@ -265,7 +266,9 @@
         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
             // Lost focus for a short time, but we have to stop
             // playback.
-            if (mState != ERROR && mMediaPlayer.isPlaying()) pause();
+            if (mState != ERROR && mMediaPlayer.isPlaying()) {
+                pause(PAUSED_TRANSITORILY);
+            }
             break;
         }
     }
@@ -298,12 +301,16 @@
     }
 
     private void pause() {
+        pause(PAUSED);
+    }
+
+    private void pause(int state) {
         if (mState == STARTED) {
             if (mTimer != null) {
                 mTimer.purge();
             }
             mMediaPlayer.pause();
-            mState = PAUSED;
+            mState = state;
         }
     }
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 0ab49ac..d901d0a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1114,6 +1114,11 @@
      * false. See also {@link #setDatabasePath} for how to correctly set up the
      * database storage API.
      *
+     * This setting is global in effect, across all WebView instances in a process.
+     * Note you should only modify this setting prior to making <b>any</b> WebView
+     * page load within a given process, as the WebView implementation may ignore
+     * changes to this setting after that point.
+     *
      * @param flag true if the WebView should use the database storage API
      */
     public synchronized void setDatabaseEnabled(boolean flag) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f3983187..1f00c9c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -208,8 +208,7 @@
  * and default scaling is not applied to the web page; if the value is "1.5", then the device is
  * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
  * value is "0.75", then the device is considered a low density device (ldpi) and the content is
- * scaled 0.75x. However, if you specify the {@code "target-densitydpi"} meta property
- * (discussed below), then you can stop this default scaling behavior.</li>
+ * scaled 0.75x.</li>
  * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
  * densities for which this style sheet is to be used. The corresponding value should be either
  * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
@@ -219,29 +218,6 @@
  * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5,
  * which is the high density pixel ratio.</p>
  * </li>
- * <li>The {@code target-densitydpi} property for the {@code viewport} meta tag. You can use
- * this to specify the target density for which the web page is designed, using the following
- * values:
- * <ul>
- * <li>{@code device-dpi} - Use the device's native dpi as the target dpi. Default scaling never
- * occurs.</li>
- * <li>{@code high-dpi} - Use hdpi as the target dpi. Medium and low density screens scale down
- * as appropriate.</li>
- * <li>{@code medium-dpi} - Use mdpi as the target dpi. High density screens scale up and
- * low density screens scale down. This is also the default behavior.</li>
- * <li>{@code low-dpi} - Use ldpi as the target dpi. Medium and high density screens scale up
- * as appropriate.</li>
- * <li><em>{@code <value>}</em> - Specify a dpi value to use as the target dpi (accepted
- * values are 70-400).</li>
- * </ul>
- * <p>Here's an example meta tag to specify the target density:</p>
- * <pre>&lt;meta name="viewport" content="target-densitydpi=device-dpi" /&gt;</pre></li>
- * </ul>
- * <p>If you want to modify your web page for different densities, by using the {@code
- * -webkit-device-pixel-ratio} CSS media query and/or the {@code
- * window.devicePixelRatio} DOM property, then you should set the {@code target-densitydpi} meta
- * property to {@code device-dpi}. This stops Android from performing scaling in your web page and
- * allows you to make the necessary adjustments for each density via CSS and JavaScript.</p>
  *
  * <h3>HTML5 Video support</h3>
  *
@@ -597,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
@@ -856,7 +833,7 @@
      *                 defaults to 'text/html'.
      * @param encoding the encoding of the data
      * @param historyUrl the URL to use as the history entry. If null defaults
-     *                   to 'about:blank'.
+     *                   to 'about:blank'. If non-null, this must be a valid URL.
      */
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String historyUrl) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 6fefcca..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);
             }
@@ -8368,8 +8368,10 @@
             mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                 @Override
                 public void onCancel(DialogInterface dialog) {
+                 if (mWebViewCore != null) {
                     mWebViewCore.sendMessage(
                                 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+                    }
                     mListBoxDialog = null;
                 }
             });
diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java
index be010287..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/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index a379157..f26527f 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -200,9 +200,7 @@
             if (view != null) {
                 // Put in recycler for re-measuring and/or layout
                 mRecycler.put(selectedPosition, view);
-            }
 
-            if (view != null) {
                 if (view.getLayoutParams() == null) {
                     mBlockLayoutRequests = true;
                     view.setLayoutParams(generateDefaultLayoutParams());
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 deec41c..c9d2d95 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -37,11 +37,11 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 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;
 
 /**
@@ -221,13 +221,13 @@
     // Some apps came to rely on them. :(
     private boolean mAllowBrokenMeasureSpecs = false;
 
-    private int mDisplayWidth;
+    // A default width used for RTL measure pass
+    private static int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
 
     public RelativeLayout(Context context) {
         super(context);
         mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
                 Build.VERSION_CODES.JELLY_BEAN_MR1;
-        getDisplayWidth();
     }
 
     public RelativeLayout(Context context, AttributeSet attrs) {
@@ -235,7 +235,6 @@
         initFromAttributes(context, attrs);
         mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
                 Build.VERSION_CODES.JELLY_BEAN_MR1;
-        getDisplayWidth();
     }
 
     public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
@@ -243,7 +242,6 @@
         initFromAttributes(context, attrs);
         mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
                 Build.VERSION_CODES.JELLY_BEAN_MR1;
-        getDisplayWidth();
     }
 
     private void initFromAttributes(Context context, AttributeSet attrs) {
@@ -253,11 +251,6 @@
         a.recycle();
     }
 
-    private void getDisplayWidth() {
-        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-        mDisplayWidth = wm.getDefaultDisplay().getWidth();
-    }
-
     @Override
     public boolean shouldDelayChildPressedState() {
         return false;
@@ -451,12 +444,12 @@
 
         // We need to know our size for doing the correct computation of children positioning in RTL
         // mode but there is no practical way to get it instead of running the code below.
-        // So, instead of running the code twice, we just set the width to the "display width"
+        // So, instead of running the code twice, we just set the width to a "default display width"
         // before the computation and then, as a last pass, we will update their real position with
-        // an offset equals to "displayWidth - width".
+        // an offset equals to "DEFAULT_WIDTH - width".
         final int layoutDirection = getLayoutDirection();
         if (isLayoutRtl() && myWidth == -1) {
-            myWidth = mDisplayWidth;
+            myWidth = DEFAULT_WIDTH;
         }
 
         View[] views = mSortedHorizontalChildren;
@@ -1223,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
@@ -1237,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;
@@ -1405,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/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index fa64fd3..b6895a5 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -196,7 +196,7 @@
             break;
         }
         }
-        
+
         mGravity = a.getInt(com.android.internal.R.styleable.Spinner_gravity, Gravity.CENTER);
 
         mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
@@ -608,7 +608,7 @@
             handled = true;
 
             if (!mPopup.isShowing()) {
-                mPopup.show();
+                mPopup.show(getTextDirection(), getTextAlignment());
             }
         }
 
@@ -719,7 +719,7 @@
                     @Override
                     public void onGlobalLayout() {
                         if (!mPopup.isShowing()) {
-                            mPopup.show();
+                            mPopup.show(getTextDirection(), getTextAlignment());
                         }
                         final ViewTreeObserver vto = getViewTreeObserver();
                         if (vto != null) {
@@ -799,8 +799,7 @@
         }
 
         public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            return mAdapter == null ? null :
-                    mAdapter.getDropDownView(position, convertView, parent);
+            return (mAdapter == null) ? null : mAdapter.getDropDownView(position, convertView, parent);
         }
 
         public boolean hasStableIds() {
@@ -868,8 +867,8 @@
         /**
          * Show the popup
          */
-        public void show();
-        
+        public void show(int textDirection, int textAlignment);
+
         /**
          * Dismiss the popup
          */
@@ -922,13 +921,17 @@
             return mPrompt;
         }
 
-        public void show() {
+        public void show(int textDirection, int textAlignment) {
             AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
             if (mPrompt != null) {
                 builder.setTitle(mPrompt);
             }
             mPopup = builder.setSingleChoiceItems(mListAdapter,
-                    getSelectedItemPosition(), this).show();
+                    getSelectedItemPosition(), this).create();
+            final ListView listView = mPopup.getListView();
+            listView.setTextDirection(textDirection);
+            listView.setTextAlignment(textAlignment);
+            mPopup.show();
         }
         
         public void onClick(DialogInterface dialog, int which) {
@@ -1044,15 +1047,17 @@
             setHorizontalOffset(hOffset);
         }
 
-        @Override
-        public void show() {
+        public void show(int textDirection, int textAlignment) {
             final boolean wasShowing = isShowing();
 
             computeContentWidth();
 
             setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
             super.show();
-            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+            final ListView listView = getListView();
+            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+            listView.setTextDirection(textDirection);
+            listView.setTextAlignment(textAlignment);
             setSelection(Spinner.this.getSelectedItemPosition());
 
             if (wasShowing) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a1ced6e..adacef2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -432,10 +432,6 @@
 
     private int mMarqueeRepeatLimit = 3;
 
-    // The alignment to pass to Layout, or null if not resolved.
-    private Layout.Alignment mLayoutAlignment;
-    private int mResolvedTextAlignment;
-
     private int mLastLayoutDirection = -1;
 
     /**
@@ -2859,7 +2855,6 @@
 
         if (gravity != mGravity) {
             invalidate();
-            mLayoutAlignment = null;
         }
 
         mGravity = gravity;
@@ -5805,68 +5800,56 @@
                       physicalWidth, false);
     }
 
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        if (mLayoutAlignment != null) {
-            if (mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
-                    mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
-                mLayoutAlignment = null;
-            }
-        }
-    }
-
     private Layout.Alignment getLayoutAlignment() {
-        if (mLayoutAlignment == null) {
-            mResolvedTextAlignment = getTextAlignment();
-            switch (mResolvedTextAlignment) {
-                case TEXT_ALIGNMENT_GRAVITY:
-                    switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
-                        case Gravity.START:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
-                            break;
-                        case Gravity.END:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
-                            break;
-                        case Gravity.LEFT:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
-                            break;
-                        case Gravity.RIGHT:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
-                            break;
-                        case Gravity.CENTER_HORIZONTAL:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
-                            break;
-                        default:
-                            mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
-                            break;
-                    }
-                    break;
-                case TEXT_ALIGNMENT_TEXT_START:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
-                    break;
-                case TEXT_ALIGNMENT_TEXT_END:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
-                    break;
-                case TEXT_ALIGNMENT_CENTER:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
-                    break;
-                case TEXT_ALIGNMENT_VIEW_START:
-                    mLayoutAlignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
-                            Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
-                    break;
-                case TEXT_ALIGNMENT_VIEW_END:
-                    mLayoutAlignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
-                            Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
-                    break;
-                case TEXT_ALIGNMENT_INHERIT:
-                    // This should never happen as we have already resolved the text alignment
-                    // but better safe than sorry so we just fall through
-                default:
-                    mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
-                    break;
-            }
+        Layout.Alignment alignment;
+        switch (getTextAlignment()) {
+            case TEXT_ALIGNMENT_GRAVITY:
+                switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+                    case Gravity.START:
+                        alignment = Layout.Alignment.ALIGN_NORMAL;
+                        break;
+                    case Gravity.END:
+                        alignment = Layout.Alignment.ALIGN_OPPOSITE;
+                        break;
+                    case Gravity.LEFT:
+                        alignment = Layout.Alignment.ALIGN_LEFT;
+                        break;
+                    case Gravity.RIGHT:
+                        alignment = Layout.Alignment.ALIGN_RIGHT;
+                        break;
+                    case Gravity.CENTER_HORIZONTAL:
+                        alignment = Layout.Alignment.ALIGN_CENTER;
+                        break;
+                    default:
+                        alignment = Layout.Alignment.ALIGN_NORMAL;
+                        break;
+                }
+                break;
+            case TEXT_ALIGNMENT_TEXT_START:
+                alignment = Layout.Alignment.ALIGN_NORMAL;
+                break;
+            case TEXT_ALIGNMENT_TEXT_END:
+                alignment = Layout.Alignment.ALIGN_OPPOSITE;
+                break;
+            case TEXT_ALIGNMENT_CENTER:
+                alignment = Layout.Alignment.ALIGN_CENTER;
+                break;
+            case TEXT_ALIGNMENT_VIEW_START:
+                alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                        Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+                break;
+            case TEXT_ALIGNMENT_VIEW_END:
+                alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                        Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+                break;
+            case TEXT_ALIGNMENT_INHERIT:
+                // This should never happen as we have already resolved the text alignment
+                // but better safe than sorry so we just fall through
+            default:
+                alignment = Layout.Alignment.ALIGN_NORMAL;
+                break;
         }
-        return mLayoutAlignment;
+        return alignment;
     }
 
     /**
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/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index dbf6c8e..d44df0c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -123,14 +123,34 @@
      * @return true if the value is present in the array
      */
     public static <T> boolean contains(T[] array, T value) {
-        for (T element : array) {
-            if (element == null) {
-                if (value == null) return true;
+        return indexOf(array, value) != -1;
+    }
+
+    /**
+     * Return first index of {@code value} in {@code array}, or {@code -1} if
+     * not found.
+     */
+    public static <T> int indexOf(T[] array, T value) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == null) {
+                if (value == null) return i;
             } else {
-                if (value != null && element.equals(value)) return true;
+                if (value != null && array[i].equals(value)) return i;
             }
         }
-        return false;
+        return -1;
+    }
+
+    /**
+     * Test if all {@code check} items are contained in {@code array}.
+     */
+    public static <T> boolean containsAll(T[] array, T[] check) {
+        for (T checkItem : check) {
+            if (!contains(array, checkItem)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     public static boolean contains(int[] array, int value) {
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/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index 0c6b780..cf69a9d 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -19,6 +19,7 @@
 import com.android.internal.R;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
@@ -44,7 +45,10 @@
     }
 
     public boolean showsOverflowMenuButton() {
-        return !ViewConfiguration.get(mContext).hasPermanentMenuKey();
+        return !ViewConfiguration.get(mContext).hasPermanentMenuKey() ||
+                ((mContext.getResources().getConfiguration().uiMode &
+                        Configuration.UI_MODE_TYPE_TELEVISION) ==
+                        Configuration.UI_MODE_TYPE_TELEVISION);
     }
 
     public int getEmbeddedMenuWidthLimit() {
diff --git a/core/java/com/android/internal/view/IInputConnectionCallback.aidl b/core/java/com/android/internal/view/IInputConnectionCallback.aidl
deleted file mode 100644
index 5b5b3df..0000000
--- a/core/java/com/android/internal/view/IInputConnectionCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import android.graphics.Rect;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.inputmethod.TextBoxAttribute;
-import com.android.internal.view.IInputContext;
-import android.os.IBinder;
-
-/**
- * {@hide}
- */
-oneway interface IInputMethodCallback {
-    void finishedEvent(int seq, boolean handled);
-}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 482eba7..f359146 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -16,33 +16,42 @@
 
 package com.android.internal.widget;
 
+import android.view.ViewGroup;
 import com.android.internal.app.ActionBarImpl;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
-import android.widget.FrameLayout;
 
 /**
  * Special layout for the containing of an overlay action bar (and its
  * content) to correctly handle fitting system windows when the content
  * has request that its layout ignore them.
  */
-public class ActionBarOverlayLayout extends FrameLayout {
+public class ActionBarOverlayLayout extends ViewGroup {
     private int mActionBarHeight;
     private ActionBarImpl mActionBar;
     private int mWindowVisibility = View.VISIBLE;
+
+    // The main UI elements that we handle the layout of.
     private View mContent;
     private View mActionBarTop;
+    private View mActionBarBottom;
+
+    // Some interior UI elements.
     private ActionBarContainer mContainerView;
     private ActionBarView mActionView;
-    private View mActionBarBottom;
+
     private boolean mOverlayMode;
     private int mLastSystemUiVisibility;
-    private final Rect mLocalInsets = new Rect();
+    private final Rect mBaseContentInsets = new Rect();
+    private final Rect mLastBaseContentInsets = new Rect();
+    private final Rect mContentInsets = new Rect();
+    private final Rect mBaseInnerInsets = new Rect();
+    private final Rect mInnerInsets = new Rect();
+    private final Rect mLastInnerInsets = new Rect();
 
     static final int[] mActionBarSizeAttr = new int [] {
             com.android.internal.R.attr.actionBarSize
@@ -64,10 +73,6 @@
         ta.recycle();
     }
 
-    public void setOverlayMode(boolean mode) {
-        mOverlayMode = mode;
-    }
-
     public void setActionBar(ActionBarImpl impl, boolean overlayMode) {
         mActionBar = impl;
         mOverlayMode = overlayMode;
@@ -139,7 +144,7 @@
     private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
             boolean bottom, boolean right) {
         boolean changed = false;
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)view.getLayoutParams();
+        LayoutParams lp = (LayoutParams)view.getLayoutParams();
         if (left && lp.leftMargin != insets.left) {
             changed = true;
             lp.leftMargin = insets.left;
@@ -172,50 +177,185 @@
             changed |= applyInsets(mActionBarBottom, insets, true, false, true, true);
         }
 
-        int topSpace = 0;
-        if (stable || mActionBarTop.getVisibility() == VISIBLE) {
-            // This is the space needed on top of the window for the action bar.
-            topSpace = mActionBarHeight;
+        mBaseInnerInsets.set(insets);
+        computeFitSystemWindows(mBaseInnerInsets, mBaseContentInsets);
+        if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
+            changed = true;
+            mLastBaseContentInsets.set(mBaseContentInsets);
         }
-        if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
-            View tabs = mContainerView.getTabContainer();
-            if (tabs != null && (stable || tabs.getVisibility() == VISIBLE)) {
-                // If tabs are not embedded, increase space on top to account for them.
-                topSpace += mActionBarHeight;
-            }
-        }
-
-        int bottomSpace = 0;
-        if (mActionView.isSplitActionBar()) {
-            if ((mActionBarBottom != null
-                    && (stable || mActionBarBottom.getVisibility() == VISIBLE))) {
-                // If action bar is split, adjust bottom insets for it.
-                bottomSpace = mActionBarHeight;
-            }
-        }
-
-        // If the window has not requested system UI layout flags, we need to
-        // make sure its content is not being covered by system UI...  though it
-        // will still be covered by the action bar since they have requested it to
-        // overlay.
-        boolean res = computeFitSystemWindows(insets, mLocalInsets);
-        if (!mOverlayMode && !stable) {
-            mLocalInsets.top += topSpace;
-            mLocalInsets.bottom += bottomSpace;
-        } else {
-            insets.top += topSpace;
-            insets.bottom += bottomSpace;
-        }
-        changed |= applyInsets(mContent, mLocalInsets, true, true, true, true);
 
         if (changed) {
             requestLayout();
         }
 
-        super.fitSystemWindows(insets);
+        // We don't do any more at this point.  To correctly compute the content/inner
+        // insets in all cases, we need to know the measured size of the various action
+        // bar elements.  fitSystemWindows() happens before the measure pass, so we can't
+        // do that here.  Instead we will take this up in onMeasure().
         return true;
     }
 
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new LayoutParams(p);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        pullChildren();
+
+        int maxHeight = 0;
+        int maxWidth = 0;
+        int childState = 0;
+
+        int topInset = 0;
+        int bottomInset = 0;
+
+        measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
+        LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
+        maxWidth = Math.max(maxWidth,
+                mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+        maxHeight = Math.max(maxHeight,
+                mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+        childState = combineMeasuredStates(childState, mActionBarTop.getMeasuredState());
+
+        // xlarge screen layout doesn't have bottom action bar.
+        if (mActionBarBottom != null) {
+            measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0);
+            lp = (LayoutParams) mActionBarBottom.getLayoutParams();
+            maxWidth = Math.max(maxWidth,
+                    mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+            maxHeight = Math.max(maxHeight,
+                    mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+            childState = combineMeasuredStates(childState, mActionBarBottom.getMeasuredState());
+        }
+
+        final int vis = getWindowSystemUiVisibility();
+        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
+
+        if (stable) {
+            // This is the standard space needed for the action bar.  For stable measurement,
+            // we can't depend on the size currently reported by it -- this must remain constant.
+            topInset = mActionBarHeight;
+            if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
+                View tabs = mContainerView.getTabContainer();
+                if (tabs != null) {
+                    // If tabs are not embedded, increase space on top to account for them.
+                    topInset += mActionBarHeight;
+                }
+            }
+        } else if (mActionBarTop.getVisibility() == VISIBLE) {
+            // This is the space needed on top of the window for all of the action bar
+            // and tabs.
+            topInset = mActionBarTop.getMeasuredHeight();
+        }
+
+        if (mActionView.isSplitActionBar()) {
+            // If action bar is split, adjust bottom insets for it.
+            if (mActionBarBottom != null) {
+                if (stable) {
+                    bottomInset = mActionBarHeight;
+                } else {
+                    bottomInset = mActionBarBottom.getMeasuredHeight();
+                }
+            }
+        }
+
+        // If the window has not requested system UI layout flags, we need to
+        // make sure its content is not being covered by system UI...  though it
+        // will still be covered by the action bar if they have requested it to
+        // overlay.
+        mContentInsets.set(mBaseContentInsets);
+        mInnerInsets.set(mBaseInnerInsets);
+        if (!mOverlayMode && !stable) {
+            mContentInsets.top += topInset;
+            mContentInsets.bottom += bottomInset;
+        } else {
+            mInnerInsets.top += topInset;
+            mInnerInsets.bottom += bottomInset;
+        }
+        applyInsets(mContent, mContentInsets, true, true, true, true);
+
+        if (!mLastInnerInsets.equals(mInnerInsets)) {
+            // If the inner insets have changed, we need to dispatch this down to
+            // the app's fitSystemWindows().  We do this before measuring the content
+            // view to keep the same semantics as the normal fitSystemWindows() call.
+            mLastInnerInsets.set(mInnerInsets);
+            super.fitSystemWindows(mInnerInsets);
+        }
+
+        measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
+        lp = (LayoutParams) mContent.getLayoutParams();
+        maxWidth = Math.max(maxWidth,
+                mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+        maxHeight = Math.max(maxHeight,
+                mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+        childState = combineMeasuredStates(childState, mContent.getMeasuredState());
+
+        // Account for padding too
+        maxWidth += getPaddingLeft() + getPaddingRight();
+        maxHeight += getPaddingTop() + getPaddingBottom();
+
+        // Check against our minimum height and width
+        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+                resolveSizeAndState(maxHeight, heightMeasureSpec,
+                        childState << MEASURED_HEIGHT_STATE_SHIFT));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        final int count = getChildCount();
+
+        final int parentLeft = getPaddingLeft();
+        final int parentRight = right - left - getPaddingRight();
+
+        final int parentTop = getPaddingTop();
+        final int parentBottom = bottom - top - getPaddingBottom();
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+                final int width = child.getMeasuredWidth();
+                final int height = child.getMeasuredHeight();
+
+                int childLeft = parentLeft + lp.leftMargin;
+                int childTop;
+                if (child == mActionBarBottom) {
+                    childTop = parentBottom - height - lp.bottomMargin;
+                } else {
+                    childTop = parentTop + lp.topMargin;
+                }
+
+                child.layout(childLeft, childTop, childLeft + width, childTop + height);
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return false;
+    }
+
     void pullChildren() {
         if (mContent == null) {
             mContent = findViewById(com.android.internal.R.id.content);
@@ -226,4 +366,23 @@
             mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
         }
     }
+
+
+    public static class LayoutParams extends MarginLayoutParams {
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(ViewGroup.MarginLayoutParams source) {
+            super(source);
+        }
+    }
 }
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/jni/Android.mk b/core/jni/Android.mk
index 2544c54..1e27be8 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -11,10 +11,6 @@
 	LOCAL_CFLAGS += -DPACKED=""
 endif
 
-ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),)
-  LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
-endif
-
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
 endif
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 296d9b2..753f5dc 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -53,10 +53,10 @@
     GLConsumer* const p =
         (GLConsumer*)env->GetIntField(thiz, fields.surfaceTexture);
     if (surfaceTexture.get()) {
-        surfaceTexture->incStrong(thiz);
+        surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture);
     }
     if (p) {
-        p->decStrong(thiz);
+        p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
     }
     env->SetIntField(thiz, fields.surfaceTexture, (int)surfaceTexture.get());
 }
@@ -68,10 +68,10 @@
         (GLConsumer::FrameAvailableListener*)
             env->GetIntField(thiz, fields.frameAvailableListener);
     if (listener.get()) {
-        listener->incStrong(thiz);
+        listener->incStrong((void*)SurfaceTexture_setSurfaceTexture);
     }
     if (p) {
-        p->decStrong(thiz);
+        p->decStrong((void*)SurfaceTexture_setSurfaceTexture);
     }
     env->SetIntField(thiz, fields.frameAvailableListener, (int)listener.get());
 }
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 362d9a4..6fbaaf2 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -496,7 +496,7 @@
     // We use a weak reference so the Camera object can be garbage collected.
     // The reference is only used as a proxy for callbacks.
     sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
-    context->incStrong(thiz);
+    context->incStrong((void*)android_hardware_Camera_native_setup);
     camera->setListener(context);
 
     // save context in opaque field
@@ -534,7 +534,7 @@
         }
 
         // remove context to prevent further Java access
-        context->decStrong(thiz);
+        context->decStrong((void*)android_hardware_Camera_native_setup);
     }
 }
 
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index e8a6569..6374494 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -31,7 +31,7 @@
 static struct {
     jclass clazz;
     jmethodID dispatchSensorEvent;
-} gSensorEventQueueClassInfo;
+} gBaseEventQueueClassInfo;
 
 namespace android {
 
@@ -145,7 +145,7 @@
                 env->SetFloatArrayRegion(mScratch, 0, 16, buffer[i].data);
 
                 env->CallVoidMethod(mReceiverObject,
-                        gSensorEventQueueClassInfo.dispatchSensorEvent,
+                        gBaseEventQueueClassInfo.dispatchSensorEvent,
                         buffer[i].sensor,
                         mScratch,
                         buffer[i].vector.status,
@@ -176,7 +176,7 @@
     }
 
     sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQ, scratch);
-    receiver->incStrong(clazz);
+    receiver->incStrong((void*)nativeInitSensorEventQueue);
     return jint(receiver.get());
 }
 
@@ -193,7 +193,7 @@
 static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jint eventQ, jint handle) {
     sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
     receiver->destroy();
-    receiver->decStrong(clazz);
+    receiver->decStrong((void*)nativeInitSensorEventQueue);
 }
 
 
@@ -209,9 +209,9 @@
             (void*)nativeGetNextSensor },
 };
 
-static JNINativeMethod gSensorEventQueueMethods[] = {
-    {"nativeInitSensorEventQueue",
-            "(Landroid/hardware/SystemSensorManager$SensorEventQueue;Landroid/os/MessageQueue;[F)I",
+static JNINativeMethod gBaseEventQueueMethods[] = {
+    {"nativeInitBaseEventQueue",
+            "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)I",
             (void*)nativeInitSensorEventQueue },
 
     {"nativeEnableSensor",
@@ -245,13 +245,13 @@
     jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager",
             gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
 
-    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$SensorEventQueue",
-            gSensorEventQueueMethods, NELEM(gSensorEventQueueMethods));
+    jniRegisterNativeMethods(env, "android/hardware/SystemSensorManager$BaseEventQueue",
+            gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
 
-    FIND_CLASS(gSensorEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$SensorEventQueue");
+    FIND_CLASS(gBaseEventQueueClassInfo.clazz, "android/hardware/SystemSensorManager$BaseEventQueue");
 
-    GET_METHOD_ID(gSensorEventQueueClassInfo.dispatchSensorEvent,
-            gSensorEventQueueClassInfo.clazz,
+    GET_METHOD_ID(gBaseEventQueueClassInfo.dispatchSensorEvent,
+            gBaseEventQueueClassInfo.clazz,
             "dispatchSensorEvent", "(I[FIJ)V");
 
     return 0;
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index da2f874..197d240 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -154,10 +154,10 @@
     sp<AudioRecord> old =
             (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
     if (ar.get()) {
-        ar->incStrong(thiz);
+        ar->incStrong((void*)setAudioRecord);
     }
     if (old != 0) {
-        old->decStrong(thiz);
+        old->decStrong((void*)setAudioRecord);
     }
     env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
     return old;
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 9a3736c..e2d34c9 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -191,10 +191,10 @@
     sp<AudioTrack> old =
             (AudioTrack*)env->GetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
     if (at.get()) {
-        at->incStrong(thiz);
+        at->incStrong((void*)setAudioTrack);
     }
     if (old != 0) {
-        old->decStrong(thiz);
+        old->decStrong((void*)setAudioTrack);
     }
     env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (int)at.get());
     return old;
diff --git a/core/jni/android_net_TrafficStats.cpp b/core/jni/android_net_TrafficStats.cpp
index 0df8638..b4c4a1b 100644
--- a/core/jni/android_net_TrafficStats.cpp
+++ b/core/jni/android_net_TrafficStats.cpp
@@ -166,7 +166,7 @@
 
     struct Stats stats;
     memset(&stats, 0, sizeof(Stats));
-    if (parseIfaceStats(NULL, &stats) == 0) {
+    if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
         return getStatsType(&stats, (StatsType) type);
     } else {
         return UNKNOWN;
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 664af07..ac4bc1d 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -1202,6 +1202,22 @@
     return (EGLBoolean) 0;
 }
 
+/* EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time ) */
+static jboolean
+android_eglPresentationTimeANDROID
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sur, jlong time) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSurface sur_native = (EGLSurface) fromEGLHandle(_env, eglsurfaceGetHandleID, sur);
+
+    _returnValue = eglPresentationTimeANDROID(
+        (EGLDisplay)dpy_native,
+        (EGLSurface)sur_native,
+        (EGLnsecsANDROID)time
+    );
+    return _returnValue;
+}
+
 static const char *classPathName = "android/opengl/EGL14";
 
 static JNINativeMethod methods[] = {
@@ -1240,6 +1256,7 @@
 {"eglWaitNative", "(I)Z", (void *) android_eglWaitNative },
 {"eglSwapBuffers", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;)Z", (void *) android_eglSwapBuffers },
 {"eglCopyBuffers", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;I)Z", (void *) android_eglCopyBuffers },
+{"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
 };
 
 int register_android_opengl_jni_EGL14(JNIEnv *_env)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 881d9a0..a7eede2 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -586,7 +586,7 @@
         LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
         // The proxy holds a reference to the native object.
         env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
-        val->incStrong(object);
+        val->incStrong((void*)javaObjectForIBinder);
 
         // The native object needs to hold a weak reference back to the
         // proxy, so we can retrieve the same proxy if it is still active.
@@ -1187,7 +1187,7 @@
     env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
     env->SetIntField(obj, gBinderProxyOffsets.mOrgue, 0);
     drl->decStrong((void*)javaObjectForIBinder);
-    b->decStrong(obj);
+    b->decStrong((void*)javaObjectForIBinder);
 
     IPCThreadState::self()->flushCommands();
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 1ffb1b8..0104f4b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -76,6 +76,9 @@
 
 // ----------------------------------------------------------------------------
 
+// this is just a pointer we use to pass to inc/decStrong
+static const void *sRefBaseOwner;
+
 bool android_view_Surface_isInstanceOf(JNIEnv* env, jobject obj) {
     return env->IsInstanceOf(obj, gSurfaceClassInfo.clazz);
 }
@@ -109,7 +112,7 @@
         }
         return NULL;
     }
-    surface->incStrong(surfaceObj);
+    surface->incStrong(&sRefBaseOwner);
     return surfaceObj;
 }
 
@@ -137,18 +140,18 @@
         return 0;
     }
 
-    surface->incStrong(clazz);
+    surface->incStrong(&sRefBaseOwner);
     return int(surface.get());
 }
 
 static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    sur->decStrong(clazz);
+    sur->decStrong(&sRefBaseOwner);
 }
 
 static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    sur->decStrong(clazz);
+    sur->decStrong(&sRefBaseOwner);
 }
 
 static jboolean nativeIsValid(JNIEnv* env, jclass clazz, jint nativeObject) {
@@ -309,12 +312,12 @@
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
     sp<Surface> other(ctrl->getSurface());
     if (other != NULL) {
-        other->incStrong(clazz);
+        other->incStrong(&sRefBaseOwner);
     }
 
     sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
     if (sur != NULL) {
-        sur->decStrong(clazz);
+        sur->decStrong(&sRefBaseOwner);
     }
 
     return int(other.get());
@@ -329,11 +332,11 @@
     }
     sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));
     if (self != NULL) {
-        self->decStrong(clazz);
+        self->decStrong(&sRefBaseOwner);
     }
     sp<Surface> sur(Surface::readFromParcel(*parcel));
     if (sur != NULL) {
-        sur->incStrong(clazz);
+        sur->incStrong(&sRefBaseOwner);
     }
     return int(sur.get());
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5da5b4f..ec10536 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -27,6 +27,7 @@
 #include "android/graphics/Region.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceSession.h>
 
 #include <gui/Surface.h>
@@ -131,19 +132,19 @@
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
     }
-    surface->incStrong(clazz);
+    surface->incStrong((void *)nativeCreate);
     return int(surface.get());
 }
 
 static void nativeRelease(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
-    ctrl->decStrong(clazz);
+    ctrl->decStrong((void *)nativeCreate);
 }
 
 static void nativeDestroy(JNIEnv* env, jclass clazz, jint nativeObject) {
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
     ctrl->clear();
-    ctrl->decStrong(clazz);
+    ctrl->decStrong((void *)nativeCreate);
 }
 
 static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
@@ -161,7 +162,7 @@
     }
 }
 
-static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
+static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj,
         jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken == NULL) {
@@ -199,6 +200,24 @@
     return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
 }
 
+static void nativeScreenshot(JNIEnv* env, jclass clazz,
+        jobject displayTokenObj, jobject surfaceObj,
+        jint width, jint height, jint minLayer, jint maxLayer, bool allLayers) {
+    sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
+    if (displayToken != NULL) {
+        sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj);
+        if (consumer != NULL) {
+            if (allLayers) {
+                minLayer = 0;
+                maxLayer = -1;
+            }
+            ScreenshotClient::capture(
+                    displayToken, consumer->getIGraphicBufferProducer(),
+                    width, height, uint32_t(minLayer), uint32_t(maxLayer));
+        }
+    }
+}
+
 static void nativeOpenTransaction(JNIEnv* env, jclass clazz) {
     SurfaceComposerClient::openGlobalTransaction();
 }
@@ -393,6 +412,8 @@
     {"nativeDestroy", "(I)V",
             (void*)nativeDestroy },
     {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZ)Landroid/graphics/Bitmap;",
+            (void*)nativeScreenshotBitmap },
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZ)V",
             (void*)nativeScreenshot },
     {"nativeOpenTransaction", "()V",
             (void*)nativeOpenTransaction },
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 1494bc5..87e339c 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -41,13 +41,13 @@
 
 static jint nativeCreate(JNIEnv* env, jclass clazz) {
     SurfaceComposerClient* client = new SurfaceComposerClient();
-    client->incStrong(clazz);
+    client->incStrong((void*)nativeCreate);
     return reinterpret_cast<jint>(client);
 }
 
 static void nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
     SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
-    client->decStrong(clazz);
+    client->decStrong((void*)nativeCreate);
 }
 
 static void nativeKill(JNIEnv* env, jclass clazz, jint ptr) {
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 8a89db5..5baae84 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -109,7 +109,7 @@
     sp<GLConsumer> glConsumer(SurfaceTexture_getSurfaceTexture(env, surface));
     sp<ANativeWindow> window = new Surface(glConsumer->getBufferQueue());
 
-    window->incStrong(0);
+    window->incStrong((void*)android_view_TextureView_createNativeWindow);
     SET_INT(textureView, gTextureViewClassInfo.nativeWindow, jint(window.get()));
 }
 
@@ -120,7 +120,7 @@
 
     if (nativeWindow) {
         sp<ANativeWindow> window(nativeWindow);
-            window->decStrong(0);
+            window->decStrong((void*)android_view_TextureView_createNativeWindow);
         SET_INT(textureView, gTextureViewClassInfo.nativeWindow, 0);
     }
 }
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/res/res/layout-xlarge/screen_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml
index 0b6122d..4f286780 100644
--- a/core/res/res/layout-xlarge/screen_action_bar.xml
+++ b/core/res/res/layout-xlarge/screen_action_bar.xml
@@ -15,33 +15,43 @@
 -->
 
 <!--
-This is an optimized layout for a screen with the Action Bar enabled.
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:fitsSystemWindows="true"
+<com.android.internal.widget.ActionBarOverlayLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/action_bar_overlay_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:splitMotionEvents="false">
-    <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        style="?android:attr/actionBarStyle">
-        <com.android.internal.widget.ActionBarView
-            android:id="@+id/action_bar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            style="?android:attr/actionBarStyle" />
-        <com.android.internal.widget.ActionBarContextView
-            android:id="@+id/action_context_bar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            style="?android:attr/actionModeStyle" />
-    </com.android.internal.widget.ActionBarContainer>
     <FrameLayout android:id="@android:id/content"
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"
-        android:foregroundGravity="fill_horizontal|top"
-        android:foreground="?android:attr/windowContentOverlay" />
-</LinearLayout>
+        android:layout_height="match_parent" />
+    <LinearLayout android:id="@+id/top_action_bar"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+        <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            style="?android:attr/actionBarStyle"
+            android:gravity="top">
+            <com.android.internal.widget.ActionBarView
+                android:id="@+id/action_bar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                style="?android:attr/actionBarStyle" />
+            <com.android.internal.widget.ActionBarContextView
+                android:id="@+id/action_context_bar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                style="?android:attr/actionModeStyle" />
+        </com.android.internal.widget.ActionBarContainer>
+        <ImageView android:src="?android:attr/windowContentOverlay"
+                   android:scaleType="fitXY"
+                   android:layout_width="match_parent"
+                   android:layout_height="wrap_content" />
+    </LinearLayout>
+</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
deleted file mode 100644
index a95635e..0000000
--- a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!--
-This is an optimized layout for a screen with
-the Action Bar enabled overlaying application content.
--->
-
-<com.android.internal.widget.ActionBarOverlayLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/action_bar_overlay_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:splitMotionEvents="false">
-    <FrameLayout android:id="@android:id/content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-    <LinearLayout android:id="@+id/top_action_bar"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="top">
-        <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            style="?android:attr/actionBarStyle"
-            android:gravity="top">
-            <com.android.internal.widget.ActionBarView
-                android:id="@+id/action_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                style="?android:attr/actionBarStyle" />
-            <com.android.internal.widget.ActionBarContextView
-                android:id="@+id/action_context_bar"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:visibility="gone"
-                style="?android:attr/actionModeStyle" />
-        </com.android.internal.widget.ActionBarContainer>
-        <ImageView android:src="?android:attr/windowContentOverlay"
-                   android:scaleType="fitXY"
-                   android:layout_width="match_parent"
-                   android:layout_height="wrap_content" />
-    </LinearLayout>
-</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 95519c6..e310bf5 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -29,8 +29,7 @@
         android:layout_height="match_parent" />
     <LinearLayout android:id="@+id/top_action_bar"
                   android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:layout_gravity="top">
+                  android:layout_height="wrap_content">
         <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -57,7 +56,6 @@
     <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
-                  android:layout_gravity="bottom"
                   style="?android:attr/actionBarSplitStyle"
                   android:visibility="gone"
                   android:gravity="center"/>
diff --git a/core/res/res/layout/simple_spinner_dropdown_item.xml b/core/res/res/layout/simple_spinner_dropdown_item.xml
index cb999b6..e2bd474 100644
--- a/core/res/res/layout/simple_spinner_dropdown_item.xml
+++ b/core/res/res/layout/simple_spinner_dropdown_item.xml
@@ -23,4 +23,5 @@
     android:singleLine="true"
     android:layout_width="match_parent"
     android:layout_height="?android:attr/dropdownListPreferredItemHeight"
-    android:ellipsize="marquee" />
+    android:ellipsize="marquee"
+    android:textAlignment="inherit"/>
diff --git a/core/res/res/layout/simple_spinner_item.xml b/core/res/res/layout/simple_spinner_item.xml
index 61dc025..5c7685e 100644
--- a/core/res/res/layout/simple_spinner_item.xml
+++ b/core/res/res/layout/simple_spinner_item.xml
@@ -23,4 +23,5 @@
     android:singleLine="true"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:ellipsize="marquee" />
+    android:ellipsize="marquee"
+    android:textAlignment="inherit"/>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index b077308..6f0652f 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Permet que l\'aplicació iniciï la IU de confirmació de còpia de seguretat completa. No la pot fer servir qualsevol aplicació."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visualitzar finestres no autoritzades"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Permet que l\'aplicació creï finestres que utilitzarà la interfície d\'usuari del sistema intern. No indicat per a les aplicacions normals."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"crida d\'altres aplicacions"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"mostra sobre altres aplicacions"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permet que l\'aplicació dibuixi sobre les altres aplicacions o parts de la interfície d\'usuari. Pot interferir amb l\'ús de la interfície a qualsevol aplicació o canviar el que et sembla que estàs veient en altres aplicacions."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"modificar la velocitat d\'animacions global"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Permet que l\'aplicació canviï la velocitat d\'animació global (animacions més ràpides o lentes) en qualsevol moment."</string>
@@ -1406,7 +1406,7 @@
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Vols iniciar el navegador?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Vols acceptar la trucada?"</string>
     <string name="activity_resolver_use_always" msgid="8017770747801494933">"Sempre"</string>
-    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Només una"</string>
+    <string name="activity_resolver_use_once" msgid="2404644797149173758">"Només una vegada"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tauleta"</string>
     <string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telèfon"</string>
     <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Auriculars"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 3482ed4f..aef7bdc 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -329,8 +329,8 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Umožňuje aplikaci spustit uživatelské rozhraní potvrzení úplné zálohy. Toto oprávnění nesmí používat žádná aplikace."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"zobrazení nepovolených oken"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Umožňuje aplikaci vytvářet okna, která se budou používat v interním uživatelském rozhraní systému. Toto oprávnění není určeno pro běžné aplikace."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"kreslení přes další aplikace"</string>
-    <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Umožňuje aplikaci využívat jiné aplikace nebo části uživatelského rozhraní. Aplikace tak může zasahovat do používání rozhraní jakékoli aplikace a měnit rozhraní zobrazené v jiných aplikacích."</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"vykreslení přes další aplikace"</string>
+    <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Umožňuje aplikaci vykreslování nad jinými aplikacemi nebo částmi uživatelského rozhraní. Tato funkce může zasahovat do vašeho používání rozhraní jiné aplikace nebo měnit zobrazovaný obsah v jiných aplikacích."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"změna globální rychlosti animace"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Umožňuje aplikaci kdykoliv globálně změnit rychlost animací (rychlejší či pomalejší animace)."</string>
     <string name="permlab_manageAppTokens" msgid="1286505717050121370">"správa aplikačních tokenů"</string>
@@ -959,7 +959,7 @@
   </plurals>
   <plurals name="in_num_days">
     <item quantity="one" msgid="5413088743009839518">"zítra"</item>
-    <item quantity="other" msgid="5109449375100953247">"zbývající počet dní: <xliff:g id="COUNT">%d</xliff:g>"</item>
+    <item quantity="other" msgid="5109449375100953247">"za <xliff:g id="COUNT">%d</xliff:g> dny"</item>
   </plurals>
   <plurals name="abbrev_num_seconds_ago">
     <item quantity="one" msgid="1849036840200069118">"před 1 s"</item>
@@ -991,7 +991,7 @@
   </plurals>
   <plurals name="abbrev_in_num_days">
     <item quantity="one" msgid="2178576254385739855">"zítra"</item>
-    <item quantity="other" msgid="2973062968038355991">"zbývající počet dní: <xliff:g id="COUNT">%d</xliff:g>"</item>
+    <item quantity="other" msgid="2973062968038355991">"za <xliff:g id="COUNT">%d</xliff:g> dny"</item>
   </plurals>
     <string name="preposition_for_date" msgid="9093949757757445117">"dne <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="5506831244263083793">"v <xliff:g id="TIME">%s</xliff:g>"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 3fb84ec..b86ae9f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -143,7 +143,7 @@
     <string name="silent_mode_vibrate" msgid="7072043388581551395">"Ringervibrering"</string>
     <string name="silent_mode_ring" msgid="8592241816194074353">"Ringeren er aktiveret"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Lukker ned..."</string>
-    <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din tabletcomputer slukkes nu."</string>
+    <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din tablet slukkes nu."</string>
     <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon slukkes nu."</string>
     <string name="shutdown_confirm_question" msgid="2906544768881136183">"Vil du slukke?"</string>
     <string name="reboot_safemode_title" msgid="7054509914500140361">"Genstart i sikker tilstand"</string>
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Tillader, at appen kan åbne brugergrænsefladen til bekræftelse af komplet sikkerhedskopiering. Må ikke anvendes af nogen app."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserede vinduer"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Tillader, at appen kan oprette vinduer, der er beregnet til brugergrænsefladen i det interne system. Anvendes ikke af normale apps."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"tegne over andre apps"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"trække over andre apps"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Tillader, at appen trækkes oven på andre applikationer eller dele af brugergrænsefladen. De kan forstyrre din brug af grænsefladen i en applikation eller ændre det, du tror, du ser i andre applikationer."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"rediger global animationshastighed"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Tillader, at appen til enhver tid kan ændre den globale animationshastighed (hurtigere eller langsommere animationer)."</string>
@@ -522,7 +522,7 @@
     <string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"Tillader, at appen kan slukke og tænde din tablet."</string>
     <string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"Tillader, at appen kan slukke og tænde telefonen."</string>
     <string name="permlab_factoryTest" msgid="3715225492696416187">"kør i fabriksindstillet testtilstand"</string>
-    <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"Kør som en producenttest på lavt niveau, der giver fuld adgang til tabletcomputerens hardware. Kun tilgængeligt når en tabletcomputer kører i producenttesttilstand."</string>
+    <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"Kør som en producenttest på lavt niveau, der giver fuld adgang til tabletens hardware. Kun tilgængeligt når en tablet kører i producenttesttilstand."</string>
     <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"Kør som en producenttest på lavt niveau. Giver fuld adgang til telefonens hardware. Kun tilgængeligt når en telefon kører i producenttesttilstand."</string>
     <string name="permlab_setWallpaper" msgid="6627192333373465143">"angiv tapet"</string>
     <string name="permdesc_setWallpaper" msgid="7373447920977624745">"Tillader, at appen kan konfigurere systembaggrunden."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index febefe3..0974ebe 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Permite que la aplicación inicie la interfaz de usuario de confirmación de copia de seguridad completa. Ninguna aplicación debe usar este permiso."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Permite que la aplicación cree ventanas para la interfaz de usuario interna del sistema. Las aplicaciones normales no deben usar este permiso."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"destacar sobre otras aplicaciones"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"mostrar sobre otras aplicaciones"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite que la aplicación escriba sobre otras aplicaciones o en partes de interfaz de usuario. Pueden interferir con el uso de la interfaz en cualquier aplicación o modificar lo que crees que se muestra en otras aplicaciones."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"modificar velocidad de animación global"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Permite que la aplicación cambie la velocidad de animación global (animaciones más rápidas o más lentas) en cualquier momento."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ec9a00e..314d1ae 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Consente all\'applicazione di avviare l\'interfaccia utente di conferma del backup completo. Non utilizzare per nessuna applicazione."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visualizzazione finestre non autorizzate"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Consente all\'applicazione di creare finestre destinate all\'uso da parte dell\'interfaccia utente del sistema interno. Da non usare per normali applicazioni."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"disegno su altre applicazioni"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"spostamento sopra altre app"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"modifica velocità di animazione globale"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Consente all\'applicazione di modificare la velocità di animazione globale (animazioni più veloci o più lente) in qualsiasi momento."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 62da0ae..044415d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -329,7 +329,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Lar appen starte det fullstendige grensesnittet for bekreftelse av sikkerhetskopiering. Skal ikke måtte brukes av noen apper."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserte vinduer"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Lar appen opprette vinduer som er ment for å brukes av brukergrensesnittet til det interne systemet. Ikke beregnet på vanlige apper."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"tegner over andre apper"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"overstyre andre apper"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Lar appen være aktiv over andre apper eller deler av brukergrensesnittet. Dette kan virke inn på bruken din av grensesnittet i andre apper, eller endre det du tror du ser i andre apper."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"endre global animasjonshastighet"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Lar appen når som helst endre den globale animasjonshastigheten (raskere eller langsommere animasjoner)."</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d899e9d..f1d8c03 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1065,6 +1065,21 @@
         <attr name="maxSdkVersion" format="integer" />
     </declare-styleable>
     
+    <!-- The <code>library</code> tag declares that this apk is providing itself
+         as a shared library for other applications to use.  It can only be used
+         with apks that are built in to the system image.  Other apks can link to
+         it with the {@link #AndroidManifestUsesLibrary uses-library} tag.
+
+         <p>This appears as a child tag of the
+         {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestLibrary" parent="AndroidManifest">
+        <!-- Required public name of the library, which other components and
+        packages will use when referring to this library.  This is a string using
+        Java-style scoping to ensure it is unique.  The name should typically
+        be the same as the apk's package name. -->
+        <attr name="name" />
+    </declare-styleable>
+
     <!-- The <code>uses-libraries</code> specifies a shared library that this
          package requires to be linked against.  Specifying this flag tells the
          system to include this library's code in your class loader.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5fc26fc..447d94c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2064,13 +2064,13 @@
     <!-- Spouse relationship type [CHAR LIMIT=20] -->
     <string name="relationTypeSpouse">Spouse</string>
 
-    <!-- Custom SIP address type -->
+    <!-- Custom SIP address type. Same context as Custom phone type.  -->
     <string name="sipAddressTypeCustom">Custom</string>
-    <!-- Home SIP address type -->
+    <!-- Home SIP address type. Same context as Home phone type. -->
     <string name="sipAddressTypeHome">Home</string>
-    <!-- Work SIP address type -->
+    <!-- Work SIP address type. Same context as Work phone type. -->
     <string name="sipAddressTypeWork">Work</string>
-    <!-- Other SIP address type -->
+    <!-- Other SIP address type. Same context as Other phone type. -->
     <string name="sipAddressTypeOther">Other</string>
 
     <!-- Instructions telling the user to enter their SIM PIN to unlock the keyguard.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c87cb27..140ff70 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1363,7 +1363,6 @@
   <java-symbol type="layout" name="keyguard_account_view" />
   <java-symbol type="layout" name="recent_apps_dialog" />
   <java-symbol type="layout" name="screen_action_bar" />
-  <java-symbol type="layout" name="screen_action_bar_overlay" />
   <java-symbol type="layout" name="screen_custom_title" />
   <java-symbol type="layout" name="screen_progress" />
   <java-symbol type="layout" name="screen_simple" />
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/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
new file mode 100644
index 0000000..5dc9ef8
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.internal.util;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link ArrayUtils}
+ */
+public class ArrayUtilsTest extends TestCase {
+    public void testContains() throws Exception {
+        final Object A = new Object();
+        final Object B = new Object();
+        final Object C = new Object();
+        final Object D = new Object();
+
+        assertTrue(ArrayUtils.contains(new Object[] { A, B, C }, A));
+        assertTrue(ArrayUtils.contains(new Object[] { A, B, C }, B));
+        assertTrue(ArrayUtils.contains(new Object[] { A, B, C }, C));
+        assertTrue(ArrayUtils.contains(new Object[] { A, null, C }, null));
+
+        assertFalse(ArrayUtils.contains(new Object[] { A, B, C }, null));
+        assertFalse(ArrayUtils.contains(new Object[] { }, null));
+        assertFalse(ArrayUtils.contains(new Object[] { null }, A));
+    }
+
+    public void testIndexOf() throws Exception {
+        final Object A = new Object();
+        final Object B = new Object();
+        final Object C = new Object();
+        final Object D = new Object();
+
+        assertEquals(0, ArrayUtils.indexOf(new Object[] { A, B, C }, A));
+        assertEquals(1, ArrayUtils.indexOf(new Object[] { A, B, C }, B));
+        assertEquals(2, ArrayUtils.indexOf(new Object[] { A, B, C }, C));
+        assertEquals(-1, ArrayUtils.indexOf(new Object[] { A, B, C }, D));
+
+        assertEquals(-1, ArrayUtils.indexOf(new Object[] { A, B, C }, null));
+        assertEquals(-1, ArrayUtils.indexOf(new Object[] { }, A));
+        assertEquals(-1, ArrayUtils.indexOf(new Object[] { }, null));
+
+        assertEquals(0, ArrayUtils.indexOf(new Object[] { null, null }, null));
+        assertEquals(1, ArrayUtils.indexOf(new Object[] { A, null, B }, null));
+        assertEquals(2, ArrayUtils.indexOf(new Object[] { A, null, B }, B));
+    }
+
+    public void testContainsAll() throws Exception {
+        final Object A = new Object();
+        final Object B = new Object();
+        final Object C = new Object();
+
+        assertTrue(ArrayUtils.containsAll(new Object[] { C, B, A }, new Object[] { A, B, C }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { A, B }, new Object[] { A }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { A }, new Object[] { A }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { A }, new Object[] { }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { }, new Object[] { }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { null }, new Object[] { }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { null }, new Object[] { null }));
+        assertTrue(ArrayUtils.containsAll(new Object[] { A, null, C }, new Object[] { C, null }));
+
+        assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { A }));
+        assertFalse(ArrayUtils.containsAll(new Object[] { B }, new Object[] { A }));
+        assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { null }));
+        assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null }));
+    }
+}
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 14f3dd1..b2d50ce 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -31,32 +31,32 @@
   <th>Distribution</th>
 </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>2.2%</td></tr>
-<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td>    <td>8</td><td>8.1%</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.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>45.4%</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>1.0%</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>
 <tr><td><a href="/about/versions/android-4.0.3.html">4.0.3 - 4.0.4</a></td>
-                                                            <td>Ice Cream Sandwich</td><td>15</td><td>29.0%</td></tr> 
+                                                            <td>Ice Cream Sandwich</td><td>15</td><td>28.6%</td></tr> 
 <tr><td><a href="/about/versions/android-4.1.html">4.1</a></td>
-                                                   <td rowspan="2">Jelly Bean</td><td>16</td><td>12.2%</td></tr>
-<tr><td><a href="/about/versions/android-4.2.html">4.2</a></td><!--Jelly Bean-->  <td>17</td><td>1.4%</td></tr>  
+                                                   <td rowspan="2">Jelly Bean</td><td>16</td><td>14.9%</td></tr>
+<tr><td><a href="/about/versions/android-4.2.html">4.2</a></td><!--Jelly Bean-->  <td>17</td><td>1.6%</td></tr>  
 </table>
 
 </div>
 
 <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.4,8.1,45.4,0.3,29,13.6&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 -->
 
-<p style="clear:both"><em>Data collected during a 14-day period ending on February 4, 2013</em></p>
+<p style="clear:both"><em>Data collected during a 14-day period ending on March 4, 2013</em></p>
 <!--
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 -->
@@ -81,9 +81,9 @@
 Google Play within a 14-day period ending on the date indicated on the x-axis.</p>
 
 <img alt="" height="250" width="660"
-src="//chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2013%7C%7C2013%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.2,99.2,99.3,99.4,99.5,99.5,99.5,99.6,100.0,100.0,100.0,100.0,100.0|95.0,95.2,95.6,95.8,96.1,96.3,96.4,96.7,96.9,97.2,97.4,97.4,97.6|79.5,80.4,81.4,82.3,83.2,83.8,84.7,85.6,86.4,87.0,88.2,88.8,89.4|18.9,21.2,23.7,25.5,27.4,28.7,31.1,33.0,35.4,36.8,40.3,42.0,43.6|16.6,19.0,21.5,23.5,25.5,26.8,29.4,31.4,33.8,35.2,38.8,40.7,42.3|0.8,0.9,1.1,1.4,1.8,2.1,3.2,4.8,6.5,7.5,9.9,11.7,13.3&chm=b,c3df9b,0,1,0|tFroyo,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tGingerbread,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|b,96dd28,3,4,0|tIce%20Cream%20Sandwich,293f07,4,0,15,,t::-5|b,83c916,4,5,0|tJelly%20Bean,131d02,5,11,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Eclair|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08"
+src="//chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C2013%7C%7C%7C%7C2013%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.3,99.4,99.5,99.5,99.5,99.6,100.0,100.0,100.0,100.0,100.0,100.0,100.0|95.6,95.8,96.1,96.3,96.4,96.7,96.9,97.2,97.4,97.4,97.6,97.7,97.9|81.4,82.3,83.2,83.8,84.7,85.6,86.4,87.0,88.2,88.8,89.4,89.9,90.3|23.7,25.5,27.4,28.7,31.1,33.0,35.4,36.8,40.3,42.0,43.6,45.1,46.0|21.5,23.5,25.5,26.8,29.4,31.4,33.8,35.2,38.8,40.7,42.3,43.9,44.8|1.1,1.4,1.8,2.1,3.2,4.8,6.5,7.5,9.9,11.7,13.3,14.8,16.1&chm=b,c3df9b,0,1,0|tFroyo,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tGingerbread,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|b,96dd28,3,4,0|tIce%20Cream%20Sandwich,293f07,4,0,15,,t::-5|b,83c916,4,5,0|tJelly%20Bean,131d02,5,9,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Eclair|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08"
 />
-<p><em>Last historical dataset collected during a 14-day period ending on February 1, 2013</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on March 1, 2013</em></p>
 
 
 
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/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 297e906..3365cfc 100644
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -40,31 +40,63 @@
 
 <p class="note"><strong>Note:</strong> To see a complete implementation and learn how to test your application, see the <a href="{@docRoot}training/in-app-billing/index.html">Selling In-app Products</a> training class. The training class provides a complete sample In-app Billing application, including convenience classes to handle key tasks related to setting up your connection, sending billing requests and processing responses from Google Play, and managing background threading so that you can make In-app Billing calls from your main activity.</p>
 
-<p>Before you start, be sure that you read the <a href="{@docRoot}google/play/billing/billing_overview.html">In-app Billing Overview</a> to familiarize yourself with concepts that will make it easier for you to implement In-app Billing.</p>
+<p>Before you start, be sure that you read the <a href="{@docRoot}google/play/billing/billing_overview.html">In-app Billing Overview</a> to familiarize yourself with 
+concepts that will make it easier for you to implement In-app Billing.</p>
 
-<p>To implement In-app Billing in your application, you need to do the following:</p>
+<p>To implement In-app Billing in your application, you need to do the 
+following:</p>
 <ol>
   <li>Add the In-app Billing library to your project.</li>
   <li>Update your {@code AndroidManifest.xml} file.</li>
-  <li>Create a {@code ServiceConnection} and bind it to {@code IInAppBillingService}.</li>
-  <li>Send In-app Billing requests from your application to {@code IInAppBillingService}.</li>
+  <li>Create a {@code ServiceConnection} and bind it to 
+{@code IInAppBillingService}.</li>
+  <li>Send In-app Billing requests from your application to 
+{@code IInAppBillingService}.</li>
   <li>Handle In-app Billing responses from Google Play.</li>
 </ol>
 
 <h2 id="billing-add-aidl">Adding the AIDL file to your project</h2>
 
-<p>The {@code TriviaDriva} sample application contains an Android Interface Definition Language (AIDL) file which defines the interface to Google Play's In-app Billing service. When you add this file to your project, the Android build environment creates an interface file (<code>IIAppBillingService.java</code>). You can then use this interface to make billing requests by invoking IPC method calls.</p>
+<p>{@code IInAppBillingService.aidl} is an Android Interface Definition 
+Language (AIDL) file that defines the interface to the In-app Billing Version 
+3 service. You will use this interface to make billing requests by invoking IPC 
+method calls.</p>
+<p>To get the AIDL file:</p>
+<ol>
+<li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>.</li>
+<li>In the SDK Manager, expand the {@code Extras} section.</li>
+<li>Select <strong>Google Play Billing Library</strong>.</li>
+<li>Click <strong>Install packages</strong> to complete the download.</li>
+</ol>
+<p>The {@code IInAppBillingService.aidl} file will be installed to {@code &lt;sdk&gt;/extras/google/play_billing/}.</p>
 
-<p>To add the In-app Billing Version 3 library to your project:</p>
+<p>To add the AIDL to your project:</p>
 <ol>
 <li>Copy the {@code IInAppBillingService.aidl} file to your Android project.
   <ul>
-  <li>If you are using Eclipse: Import the {@code IInAppBillingService.aidl} file into your {@code /src} directory. Eclipse automatically generates the interface file when you build your project.</li>
-  <li>If you are developing in a non-Eclipse environment: Create the following directory {@code /src/com/android/vending/billing} and copy the {@code IInAppBillingService.aidl} file into this directory. Put the AIDL file into your project and use the Ant tool to build your project so that the
+  <li>If you are using Eclipse: 
+     <ol type="a">
+        <li>If you are starting from an existing Android project, open the project 
+in Eclipse. If you are creating a new Android project from scratch, click 
+<strong>File</strong> &gt; <strong>New</strong> &gt; <strong>Android Application 
+Project</strong>, then follow the instructions in the <strong>New Android 
+Application</strong> wizard to create a new project in your workspace.</li>
+	<li>In the {@code /src} directory, click <strong>File</strong> &gt; 
+<strong>New</strong> &gt; <strong>Package</strong>, then create a package named {@code com.android.vending.billing}.</li>
+	<li>Copy the {@code IInAppBillingService.aidl} file from {@code &lt;sdk&gt;/extras/google/play_billing/} and paste it into the {@code src/com.android.vending.billing/} 
+folder in your workspace.</li>
+     </ol>
+  </li>
+  <li>If you are developing in a non-Eclipse environment: Create the following 
+directory {@code /src/com/android/vending/billing} and copy the 
+{@code IInAppBillingService.aidl} file into this directory. Put the AIDL file 
+into your project and use the Ant tool to build your project so that the
 <code>IInAppBillingService.java</code> file gets generated.</li>
   </ul>
 </li>
-<li>Build your application. You should see a generated file named {@code IInAppBillingService.java} in the {@code /gen} directory of your project.</li>
+<li>Build your application. You should see a generated file named 
+{@code IInAppBillingService.java} in the {@code /gen} directory of your 
+project.</li>
 </ol>
 
 
diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd
index c2bbb49..2840dbc 100644
--- a/docs/html/google/play/billing/billing_subscriptions.jd
+++ b/docs/html/google/play/billing/billing_subscriptions.jd
@@ -3,26 +3,7 @@
 parent.link=index.html
 @jd:body
 
-<!--notice block -->
-    <div style="background-color:#fffbd9;width:100%;margin-bottom:1em;padding:8px 8px 1px;">
-      <p><em>15 February 2013</em></p>
-      <p>In-app Billing V3 now supports subscriptions and you can get
-        started developing today. A small app update is currently being
-        rolled out to Android devices. This process is automatic and
-        most devices will get the update in the next few days. However,
-        if you wish to get the update today to start developing right
-        away, simply reboot your device. </p>
-
-      <p>However, we recommend that you <em>do not publish</em> an app with 
-        V3 subscriptions until all Android devices have received the update. We'll
-        notify you here that all devices have received the update and its safe
-        to publish your apps that use V3 subscriptions. </p>
-    </div>
-
-<!-- Use non-standard wrapper to support notice block. Restore standard 
-     wrapper when notice is removed. -->
-<!--<div id="qv-wrapper"> -->
-<div id="qv-wrapper" style="margin-top:.25em;">
+<div id="qv-wrapper">
 <div id="qv">
   <h2>Quickview</h2>
   <ul>
diff --git a/docs/html/google/play/filters.jd b/docs/html/google/play/filters.jd
index 6ab223c..1ec68c6 100644
--- a/docs/html/google/play/filters.jd
+++ b/docs/html/google/play/filters.jd
@@ -386,13 +386,10 @@
 must have a SIM card and be running Android 1.1 or later, and it must be in a
 country (as determined by SIM carrier) in which paid apps are available.</p></td>
 </tr> <tr>
-  <td valign="top">Country / Carrier Targeting</td> <td valign="top"> <p>When you upload your app to
-    Google Play, you can select specific countries to target. The app will only
-    be visible to the countries (carriers) that you select, as follows:</p>
-    <ul><li><p>A device's country is determined based on the carrier, if a carrier is
-      available. If no carrier can be determined, Google Play tries to
-      determine the country based on IP.</p></li> <li><p>Carrier is determined based on
-      the device's SIM (for GSM devices), not the current roaming carrier.</p></li></ul>
+  <td valign="top">Country Targeting</td> <td valign="top"> <p>When you upload your app to
+    Google Play, you can select the countries in which to distribute your app
+    under <strong>Pricing and Distribution</strong>. The app will then
+    be available to users in only the countries you select.</p>
 </td> </tr> <tr>
   <td valign="top" style="white-space:nowrap;">CPU Architecture (ABI)</td>
   <td valign="top"><p>An application that includes native
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/providers/content-provider-basics.jd b/docs/html/guide/topics/providers/content-provider-basics.jd
index 527e713..199a671b 100644
--- a/docs/html/guide/topics/providers/content-provider-basics.jd
+++ b/docs/html/guide/topics/providers/content-provider-basics.jd
@@ -143,7 +143,7 @@
 <p>
     A content provider presents data to external applications as one or more tables that are
     similar to the tables found in a relational database. A row represents an instance of some type
-    of data the provider collects, and each row in the column represents an individual piece of
+    of data the provider collects, and each column in the row represents an individual piece of
     data collected for an instance.
 </p>
 <p>
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/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd
new file mode 100644
index 0000000..9a6320b
--- /dev/null
+++ b/docs/html/training/articles/security-ssl.jd
@@ -0,0 +1,539 @@
+page.title=Security with HTTPS and SSL
+page.article=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>In this document</h2>
+<ol class="nolist">
+  <li><a href="#Concepts">Concepts</a></li>
+  <li><a href="#HttpsExample">An HTTP Example</a></li>
+  <li><a href="#CommonProblems">Common Problems Verifying Server Certificates</a>
+    <ol class="nolist">
+      <li><a href="#UnknownCa">Unknown certificate authority</a></li>
+      <li><a href="#SelfSigned">Self-signed server certificate</a></li>
+      <li><a href="#MissingCa">Missing intermediate certificate authority</a></li>
+    </ol>
+  </li>
+  <li><a href="#CommonHostnameProbs">Common Problems with Hostname Verification</a></li>
+  <li><a href="#WarningsSslSocket">Warnings About Using SSLSocket Directly</a></li>
+  <li><a href="#Blacklisting">Blacklisting</a></li>
+  <li><a href="#Pinning">Pinning</a></li>
+  <li><a href="#ClientCert">Client Certificates</a></li>
+</ol>
+
+
+<h2>See also</h2>
+<ul>
+<li><a href="http://source.android.com/tech/security/index.html">Android
+Security Overview</a></li>
+<li><a href="{@docRoot}guide/topics/security/permissions.html">Permissions</a></li>
+</ul>
+</div></div>
+
+
+
+<p>The Secure Sockets Layer (SSL)&mdash;now technically known as <a
+href="http://en.wikipedia.org/wiki/Transport_Layer_Security">Transport Layer Security
+(TLS)</a>&mdash;is a
+common building block for encrypted communications between clients and servers. It's possible that
+an application might use SSL incorrectly such that malicious entities may
+be able to intercept an app's data over the network. To help you ensure that this does not happen
+to your app, this article highlights the common pitfalls when using secure network protocols and addresses some larger concerns about using <a
+href="http://en.wikipedia.org/wiki/Public-key_infrastructure">Public-Key Infrastructure (PKI)</a>.
+
+
+<h2 id="Concepts">Concepts</h2>
+
+<p>In a typical SSL usage scenario, a server is configured with a certificate containing a
+public key as well as a matching private key. As part of the handshake between an SSL client
+and server, the server proves it has the private key by signing its certificate with <a
+href="http://en.wikipedia.org/wiki/Public-key_cryptography">public-key cryptography</a>.</p>
+
+<p>However, anyone can generate their own certificate and private key, so a simple handshake
+doesn't prove anything about the server other than that the server knows the private key that
+matches the public key of the certificate. One way to solve this problem is to have the client
+have a set of one or more certificates it trusts. If the certificate is not in the set, the
+server is not to be trusted.</p>
+
+<p>There are several downsides to this simple approach. Servers should be able to
+upgrade to stronger keys over time ("key rotation"), which replaces the public key in the
+certificate with a new one. Unfortunately, now the client app has to be updated due to what
+is essentially a server configuration change. This is especially problematic if the server
+is not under the app developer's control, for example if it is a third party web service. This
+approach also has issues if the app has to talk to arbitrary servers such as a web browser or
+email app.</p>
+
+<p>In order to address these downsides, servers are typically configured with certificates
+from well known issuers called <a
+href="http://en.wikipedia.org/wiki/Certificate_authority">Certificate Authorities (CAs)</a>.
+The host platform generally contains a list of well known CAs that it trusts.
+As of Android 4.2 (Jelly Bean), Android currently contains over 100 CAs that are updated
+in each release. Similar to a server, a CA has a certificate and a private key. When issuing
+a certificate for a server, the CA <a
+href="http://en.wikipedia.org/wiki/Digital_signature">signs</a>
+the server certificate using its private key. The
+client can then verify that the server has a certificate issued by a CA known to the platform.</p>
+
+<p>However, while solving some problems, using CAs introduces another. Because the CA issues
+certificates for many servers, you still need some way to make sure you are talking to the
+server you want. To address this, the certificate issued by the CA identifies the server
+either with a specific name such as <em>gmail.com</em> or a wildcarded set of
+hosts such as <em>*.google.com</em>. </p>
+
+<p>The following example will make these concepts a little more concrete. In the snippet below
+from a command line, the <a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a>
+tool's {@code s_client} command looks at Wikipedia's server certificate information. It
+specifies port 443 because that is the default for <acronym title="Hypertext Transfer
+Protocol Secure">HTTPS</acronym>. The command sends
+the output of {@code openssl s_client} to {@code openssl x509}, which formats information
+about certificates according to the <a
+href="http://en.wikipedia.org/wiki/X.509">X.509 standard</a>. Specifically,
+the command asks for the subject, which contains the server name information,
+and the issuer, which identifies the CA.</p>
+
+<pre class="no-pretty-print">
+$ openssl s_client -connect wikipedia.org:443 | openssl x509 -noout -subject -issuer
+<b>subject=</b> /serialNumber=sOrr2rKpMVP70Z6E9BT5reY008SJEdYv/C=US/O=*.wikipedia.org/OU=GT03314600/OU=See www.rapidssl.com/resources/cps (c)11/OU=Domain Control Validated - RapidSSL(R)/<b>CN=*.wikipedia.org</b>
+<b>issuer=</b> /C=US/O=GeoTrust, Inc./CN=<b>RapidSSL CA</b>
+</pre>
+
+<p>You can see that the certificate was issued for servers matching <em>*.wikipedia.org</em> by
+the RapidSSL CA.</p>
+
+
+
+<h2 id="HttpsExample">An HTTPS Example</h2>
+
+<p>Assuming you have a web server with a
+certificate issued by a well known CA, you can make a secure request with code as
+simple this:</p>
+
+<pre>
+URL url = new URL("https://wikipedia.org");
+URLConnection urlConnection = url.openConnection();
+InputStream in = urlConnection.getInputStream();
+copyInputStreamToOutputStream(in, System.out);
+</pre>
+
+<p>Yes, it really can be that simple. If you want to tailor the HTTP request, you can cast to
+an {@link java.net.HttpURLConnection}. The Android documentation for
+{@link java.net.HttpURLConnection} has further examples about how to deal with request
+and response headers, posting content, managing cookies, using proxies, caching responses,
+and so on. But in terms of the details for verifying certificates and hostnames, the Android
+framework takes care of it for you through these APIs.
+This is where you want to be if at all possible. That said, below are some other considerations.</p>
+
+
+
+<h2 id="CommonProblems">Common Problems Verifying Server Certificates</h2>
+
+<p>Suppose instead of receiving the content from {@link java.net.URLConnection#getInputStream
+getInputStream()}, it throws an exception:</p>
+
+<pre class="no-pretty-print">
+javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
+        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
+        at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
+        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
+        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
+        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
+        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
+        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
+        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
+        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
+</pre>
+
+<p>This can happen for several reasons, including:
+<ol>
+  <li><a href="#UnknownCa">The CA that issued the server certificate was unknown</a></li>
+  <li><a href="#SelfSigned">The server certificate wasn't signed by a CA, but was self signed</a></li>
+  <li><a href="#MissingCa">The server configuration is missing an intermediate CA</a></li>
+</ol>
+
+<p>The following sections discuss how to address these problems while keeping your
+connection to the server secure.
+
+
+
+<h3 id="UnknownCa">Unknown certificate authority</h3>
+
+<p>In this case, the {@link javax.net.ssl.SSLHandshakeException} occurs
+because you have a CA that isn't trusted by the system. It could be because
+you have a certificate from a new CA that isn't yet trusted by Android or your app is
+running on an older version without the CA. More often a CA is unknown because it isn't a
+public CA, but a private one issued by an organization such as a government, corporation,
+or education institution for their own use.</p>
+
+<p>Fortunately, you can teach {@link javax.net.ssl.HttpsURLConnection}
+to trust a specific set of CAs. The procedure
+can be a little convoluted, so below is an example that takes a specific CA from
+an {@link java.io.InputStream}, uses it to create a {@link java.security.KeyStore},
+which is then used to create and initialize a
+{@link javax.net.ssl.TrustManager}. A {@link javax.net.ssl.TrustManager} is what the system
+uses to validate certificates from the server
+and&mdash;by creating one from a {@link java.security.KeyStore} with one or more CAs&mdash;those
+will be the only CAs trusted by that {@link javax.net.ssl.TrustManager}.</p>
+
+<p>Given the new {@link javax.net.ssl.TrustManager},
+the example initializes a new {@link javax.net.ssl.SSLContext} which provides
+an {@link javax.net.ssl.SSLSocketFactory} you can use to override the default
+{@link javax.net.ssl.SSLSocketFactory} from
+{@link javax.net.ssl.HttpsURLConnection}. This way the
+connection will use your CAs for certificate validation.</p>
+
+<p>Here is the example in
+full using an organizational CA from the University of Washington:</p>
+
+<pre>
+// Load CAs from an InputStream
+// (could be from a resource or ByteArrayInputStream or ...)
+CertificateFactory cf = CertificateFactory.getInstance("X.509");
+// From https://www.washington.edu/itconnect/security/ca/load-der.crt
+InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
+Certificate ca;
+try {
+    ca = cf.generateCertificate(caInput);
+    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
+} finally {
+    caInput.close();
+}
+
+// Create a KeyStore containing our trusted CAs
+String keyStoreType = KeyStore.getDefaultType();
+KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+keyStore.load(null, null);
+keyStore.setCertificateEntry("ca", ca);
+
+// Create a TrustManager that trusts the CAs in our KeyStore
+String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+tmf.init(keyStore);
+
+// Create an SSLContext that uses our TrustManager
+SSLContext context = SSLContext.getInstance("TLS");
+context.init(null, tmf.getTrustManagers(), null);
+
+// Tell the URLConnection to use a SocketFactory from our SSLContext
+URL url = new URL("https://certs.cac.washington.edu/CAtest/");
+HttpsURLConnection urlConnection =
+    (HttpsURLConnection)url.openConnection();
+urlConnection.setSSLSocketFactory(context.getSocketFactory());
+InputStream in = urlConnection.getInputStream();
+copyInputStreamToOutputStream(in, System.out);
+</pre>
+
+<p>With a custom {@link javax.net.ssl.TrustManager} that knows about your CAs,
+the system is able to validate
+that your server certificate come from a trusted issuer.</p>
+
+<p class="caution"><strong>Caution:</strong>
+Many web sites describe a poor alternative solution which is to install a
+{@link javax.net.ssl.TrustManager} that does nothing. If you do this you might as well not
+be encrypting your communication, because anyone can attack your users at a public Wi-Fi hotspot
+by using <acronym title="Domain Name System">DNS</acronym> tricks to send your users'
+traffic through a proxy of their own that pretends to be your server. The attacker can then
+record passwords and other personal data. This works because the attacker can generate a
+certificate and&mdash;without a {@link javax.net.ssl.TrustManager} that actually
+validates that the certificate comes from a trusted
+source&mdash;your app could be talking to anyone. So don't do this, not even temporarily. You can
+always make your app trust the issuer of the server's certificate, so just do it.</p>
+
+
+
+<h3 id="SelfSigned">Self-signed server certificate</h3>
+
+<p>The second case of {@link javax.net.ssl.SSLHandshakeException} is
+due to a self-signed certificate, which means the server is behaving as its own CA.
+This is similar to an unknown certificate authority, so you can use the
+same approach from the previous section.</p>
+
+<p>You can create yout own {@link javax.net.ssl.TrustManager},
+this time trusting the server certificate directly. This has all of the
+downsides discussed earlier of tying your app directly to a certificate, but can be done
+securely. However, you should be careful to make sure your self-signed certificate has a
+reasonably strong key. As of 2012, a 2048-bit RSA signature with an exponent of 65537 expiring
+yearly is acceptable. When rotating keys, you should check for <a
+href="http://csrc.nist.gov/groups/ST/key_mgmt/index.html">recommendations</a> from an
+authority (such as <a href="http://www.nist.gov/">NIST</a>) about what is acceptable.</p>
+
+
+
+<h3 id="MissingCa">Missing intermediate certificate authority</h3>
+
+<p>The third case of {@link javax.net.ssl.SSLHandshakeException}
+occurs due to a missing intermediate CA. Most public
+CAs don't sign server certificates directly. Instead, they use their main CA certificate,
+referred to as the root CA, to sign intermediate CAs. They do this so the root CA can be stored
+offline to reduce risk of compromise. However, operating systems like Android typically
+trust only root CAs directly, which leaves a short gap of trust between the server
+certificate&mdash;signed by the intermediate CA&mdash;and the certificate verifier,
+which knows the root CA. To solve
+this, the server doesn't send the client only it's certificate during the SSL handshake, but
+a chain of certificates from the server CA through any intermediates necessary to reach a
+trusted root CA.</p>
+
+<p>To see what this looks like in practice, here's the <em>mail.google.com</em> certificate
+chain as viewed by the <a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a>
+{@code s_client} command:</p>
+
+<pre class="no-pretty-print">
+$ openssl s_client -connect mail.google.com:443
+---
+Certificate chain
+ 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=mail.google.com
+   i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
+ 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
+   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
+---
+</pre>
+
+
+<p>This shows that the server sends a certificate for <em>mail.google.com</em>
+issued by the <em>Thawte SGC</em> CA, which is an intermediate CA, and a second certificate
+for the <em>Thawte SGC</em> CA issued by a <em>Verisign</em> CA, which is the primary CA that's
+trusted by Android.</p>
+
+<p>However, it is not uncommon to configure a server to not include the necessary
+intermediate CA. For example, here is a server that can cause an error in Android browsers and
+exceptions in Android apps:</p>
+
+<pre class="no-pretty-print">
+$ openssl s_client -connect egov.uscis.gov:443
+---
+Certificate chain
+ 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov
+   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3
+---
+</pre>
+
+<p>What is interesting to note here is that visiting this server in most desktop browsers
+does not cause an error like a completely unknown CA or self-signed server certificate would
+cause. This is because most desktop browsers cache trusted intermediate CAs over time. Once
+a browser has visited and learned about an intermediate CA from one site, it won't
+need to have the intermediate CA included in the certificate chain the next time.</p>
+
+<p>Some sites do this intentionally for secondary web servers used to serve resources. For
+example, they might have their main HTML page served by a server with a full certificate
+chain, but have servers for resources such as images, CSS, or JavaScript not include the
+CA, presumably to save bandwidth. Unfortunately, sometimes these servers might be providing
+a web service you are trying to call from your Android app, which is not as forgiving.</p>
+
+<p>There are two approaches to solve this issue:</p>
+<ul>
+  <li>Configure the server to
+  include the intermediate CA in the server chain. Most CAs provide documentation on how to do
+  this for all common web servers. This is the only approach if you need the site to work with
+  default Android browsers at least through Android 4.2.</li>
+  <li>Or, treat the
+  intermediate CA like any other unknown CA, and create a {@link javax.net.ssl.TrustManager}
+  to trust it directly, as done in the previous two sections.</li>
+</ul>
+
+
+<h2 id="CommonHostnameProbs">Common Problems with Hostname Verification</h2>
+
+<p>As mentioned at the beginning of this article,
+there are two key parts to verifying an SSL connection. The first
+is to verify the certificate is from a trusted source, which was the focus of the previous
+section. The focus of this section is the second part: making sure the server you are
+talking to presents the right certificate. When it doesn't, you'll typically see an error
+like this:</p>
+
+<pre class="no-pretty-print">
+java.io.IOException: Hostname 'example.com' was not verified
+        at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
+        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
+        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
+        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
+        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
+        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
+        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
+</pre>
+
+
+<p>One reason this can happen is due to a server configuration error. The server is
+configured with a certificate that does not have a subject or subject alternative name fields
+that match the server you are trying to reach. It is possible to have one certificate be used
+with many different servers. For example, looking at the <em>google.com</em> certificate with
+<a href="http://www.openssl.org/docs/apps/openssl.html">{@code openssl}</a> {@code
+s_client -connect google.com:443 | openssl x509 -text} you can see that a subject
+that supports <em>*.google.com</em> but also subject alternative names for <em>*.youtube.com</em>,
+<em>*.android.com</em>, and others. The error occurs only when the server name you
+are connecting to isn't listed by the certificate as acceptable.</p>
+
+<p>Unfortunately this can happen for another reason as well: <a
+href="http://en.wikipedia.org/wiki/Virtual_hosting">virtual hosting</a>. When sharing a
+server for more than one hostname with HTTP, the web server can tell from the HTTP/1.1 request
+which target hostname the client is looking for. Unfortunately this is complicated with
+HTTPS, because the server has to know which certificate to return before it sees the HTTP
+request. To address this problem, newer versions of SSL, specifically TLSv.1.0 and later,
+support <a href="http://en.wikipedia.org/wiki/Server_Name_Indication">Server Name Indication
+(SNI)</a>, which allows the SSL client to specify the intended
+hostname to the server so the proper certificate can be returned.</p>
+
+<p>Fortunately, {@link javax.net.ssl.HttpsURLConnection} supports
+SNI since Android 2.3. Unfortunately, Apache
+HTTP Client does not, which is one of the many reasons we discourage its use. One workaround
+if you need to support Android 2.2 (and older) or Apache HTTP Client is to set up an alternative
+virtual host on a unique port so that it's unambiguous which server certificate to return.</p>
+
+<p>The more drastic alternative is to replace {@link javax.net.ssl.HostnameVerifier}
+with one that uses not the
+hostname of your virtual host, but the one returned by the server by default.</p>
+
+<p class="caution"><strong>Caution:</strong> Replacing {@link javax.net.ssl.HostnameVerifier}
+can be <strong>very dangerous</strong> if the other virtual host is
+not under your control, because a man-in-the-middle attack could direct traffic to another
+server without your knowledge.</p>
+
+<p>If you are still sure you want to override hostname verification, here is an example
+that replaces the verifier for a single {@link java.net.URLConnection}
+with one that still verifies that the hostname is at least on expected by the app:</p>
+
+<pre>
+// Create an HostnameVerifier that hardwires the expected hostname.
+// Note that is different than the URL's hostname:
+// example.com versus example.org
+HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+    &#64;Override
+    public boolean verify(String hostname, SSLSession session) {
+        HostnameVerifier hv =
+            HttpsURLConnection.getDefaultHostnameVerifier();
+        return hv.verify("example.com", session);
+    }
+};
+
+// Tell the URLConnection to use our HostnameVerifier
+URL url = new URL("https://example.org/");
+HttpsURLConnection urlConnection =
+    (HttpsURLConnection)url.openConnection();
+urlConnection.setHostnameVerifier(hostnameVerifier);
+InputStream in = urlConnection.getInputStream();
+copyInputStreamToOutputStream(in, System.out);
+</pre>
+
+<p>But remember, if you find yourself replacing hostname verification, especially
+due to virtual hosting, it's still <strong>very dangerous</strong> if the other virtual host is
+not under your control and you should find an alternative hosting arrangement
+that avoids this issue.</p>
+
+
+
+
+<h2 id="WarningsSslSocket">Warnings About Using SSLSocket Directly</h2>
+
+<p>So far, the examples have focused on HTTPS using {@link javax.net.ssl.HttpsURLConnection}.
+Sometimes apps need to use SSL separate from HTTP. For example, an email app might use SSL variants
+of SMTP, POP3, or IMAP. In those cases, the app would want to use {@link javax.net.ssl.SSLSocket}
+directly, much the same way that {@link javax.net.ssl.HttpsURLConnection} does internally.</p>
+
+<p>The techniques described so
+far to deal with certificate verification issues also apply to {@link javax.net.ssl.SSLSocket}.
+In fact, when using a custom {@link javax.net.ssl.TrustManager}, what is passed to
+{@link javax.net.ssl.HttpsURLConnection} is an {@link javax.net.ssl.SSLSocketFactory}.
+So if you need to use a custom {@link javax.net.ssl.TrustManager} with an
+{@link javax.net.ssl.SSLSocket}, follow
+the same steps and use that {@link javax.net.ssl.SSLSocketFactory} to create your
+{@link javax.net.ssl.SSLSocket}.</p>
+
+<p class="caution"><strong>Caution:</strong>
+{@link javax.net.ssl.SSLSocket} <strong>does not</strong> perform hostname verification. It is
+up the your app to do its own hostname verification, preferably by calling {@link
+javax.net.ssl.HttpsURLConnection#getDefaultHostnameVerifier()} with the expected hostname. Further
+beware that {@link javax.net.ssl.HostnameVerifier#verify HostnameVerifier.verify()}
+doesn't throw an exception on error but instead returns a boolean result that you must
+explicitly check.</p>
+
+<p>Here is an example showing how you can do this. It shows that when connecting to
+<em>gmail.com</em> port 443 without SNI support, you'll receive a certificate for
+<em>mail.google.com</em>. This is expected in this case, so check to make sure that
+the certificate is indeed for <em>mail.google.com</em>:</p>
+
+<pre>
+// Open SSLSocket directly to gmail.com
+SocketFactory sf = SSLSocketFactory.getDefault();
+SSLSocket socket = (SSLSocket) sf.createSocket("gmail.com", 443);
+HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
+SSLSession s = socket.getSession();
+
+// Verify that the certicate hostname is for mail.google.com
+// This is due to lack of SNI support in the current SSLSocket.
+if (!hv.verify("mail.google.com", s)) {
+    throw new SSLHandshakeException("Expected mail.google.com, "
+                                    "found " + s.getPeerPrincipal());
+}
+
+// At this point SSLSocket performed certificate verificaiton and
+// we have performed hostname verification, so it is safe to proceed.
+
+// ... use socket ...
+socket.close();
+</pre>
+
+
+
+<h2 id="Blacklisting">Blacklisting</h2>
+
+<p>SSL relies heavily on CAs to issue certificates to only the properly verified owners
+of servers and domains. In rare cases, CAs are either tricked or, in the case of <a
+href="http://en.wikipedia.org/wiki/Comodo_Group#Breach_of_security">Comodo</a> or <a
+href="http://en.wikipedia.org/wiki/DigiNotar">DigiNotar</a>, breached,
+resulting in the certificates for a hostname to be issued to
+someone other than the owner of the server or domain.</p>
+
+<p>In order to mitigate this risk, Android has the ability to blacklist certain certificates or even
+whole CAs. While this list was historically built into the operating system, starting in
+Android 4.2 this list can be remotely updated to deal with future compromises.</p>
+
+
+
+<h2 id="Pinning">Pinning</h2>
+
+<p>An app can further protect itself from fraudulently issued certificates by a
+technique known as pinning. This is basically using the example provided in the unknown CA case
+above to restrict an app's trusted CAs to a small set known to be used by the app's servers. This
+prevents the compromise of one of the other 100+ CAs in the system from resulting in a breach of
+the apps secure channel.</p>
+
+
+
+<h2 id="ClientCert">Client Certificates</h2>
+
+<p>This article has focused on the user of SSL to secure communications with servers. SSL also
+supports the notion of client certificates that allow the server to validate the identity of a
+client. While beyond the scope of this article, the techniques involved are similar to specifying
+a custom {@link javax.net.ssl.TrustManager}.
+See the discussion about creating a custom {@link javax.net.ssl.KeyManager} in the documentation for
+{@link javax.net.ssl.HttpsURLConnection}.</p>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 8576948b..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/docs/html/training/in-app-billing/preparing-iab-app.jd b/docs/html/training/in-app-billing/preparing-iab-app.jd
index ab33ccc..de2dac5 100644
--- a/docs/html/training/in-app-billing/preparing-iab-app.jd
+++ b/docs/html/training/in-app-billing/preparing-iab-app.jd
@@ -33,15 +33,15 @@
 <p>Before you can start using the In-app Billing service, you'll need to add the library that contains the In-app Billing Version 3 API to your Android project. You also need to setting the permissions for your application to communicate with Google Play. In addition, you'll need to establish a connection between your application and  Google Play. You should also verify that the In-app Billing API version that you are using in your application is supported by Google Play.</p>
 
 <h2 id="GetSample">Download the Sample Application</h2>
-<p>In this training class, you will use a reference implementation for the In-app Billing Version 3 API called the {@code TrivialDrive} sample application. The sample includes convenience classes to quickly set up the In-app Billing service, marshal and unmarshal data types, and handle In-app Billing requests from the main thread of your application.  </p>
+<p>In this training class, you will use a reference implementation for the In-app Billing Version 3 API called the {@code TrivialDrive} sample application. The sample includes convenience classes to quickly set up the In-app Billing service, marshal and unmarshal data types, and handle In-app Billing requests from the main thread of your application.</p>
 <p>To download the sample application:</p>
 <ol>
 <li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>.</li>
 <li>In the SDK Manager, expand the {@code Extras} section.</li>
-<li>Select <strong>Google Play Billing Library</strong>. Make sure to select the download for In-app Billing Version 3 or above.</li>
-<li>Click <strong>Install</strong> to complete the download.</li>
+<li>Select <strong>Google Play Billing Library</strong>.</li>
+<li>Click <strong>Install packages</strong> to complete the download.</li>
 </ol>
-<p>The sample files will be installed to {@code /your/sdk/location/extras/google/play_billing/in-app-billing-v03}.</p>
+<p>The sample files will be installed to {@code &lt;sdk&gt;/extras/google/play_billing/}.</p>
 
 <h2 id="AddToDevConsole">Add Your Application to the Developer Console</h2>
 <p>The Google Play Developer Console is where you publish your In-app Billing application and  manage the various digital goods that are available for purchase from your application. When you create a new application entry in the Developer Console, it automatically generates a public license key for your application. You will need this key to establish a trusted connection from your application to the Google Play servers. You only need to generate this key once per application, and don’t need to repeat these steps when you update the APK file for your application.</p>
@@ -133,6 +133,7 @@
 <pre>
 &#64;Override
 public void onDestroy() {
+   super.onDestroy();
    if (mHelper != null) mHelper.dispose();
    mHelper = null;
 }
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 9518046..79980be 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -1069,6 +1069,13 @@
            "How to perform various tasks and keep your app's data and your user's data secure."
           >Security Tips</a>
       </li>
+
+      <li>
+        <a href="<?cs var:toroot ?>training/articles/security-ssl.html"
+           description=
+           "How to ensure that your app is secure when performing network transactions."
+          >Security with HTTPS and SSL</a>
+      </li>
       
       <li class="nav-section">
         <div class="nav-section-header">
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 905dc8e..f155cd2 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -90,9 +90,8 @@
          * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
          * an image that is 1/4 the width/height of the original, and 1/16 the
          * number of pixels. Any value <= 1 is treated the same as 1. Note: the
-         * decoder will try to fulfill this request, but the resulting bitmap
-         * may have different dimensions that precisely what has been requested.
-         * Also, powers of 2 are often faster/easier for the decoder to honor.
+         * decoder uses a final value based on powers of 2, any other value will
+         * be rounded down to the nearest power of 2.
          */
         public int inSampleSize;
 
@@ -566,8 +565,9 @@
             float scale = targetDensity / (float) density;
             if (scale != 1.0f) {
                 final Bitmap oldBitmap = bm;
-                bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
-                        (int) (bm.getHeight() * scale + 0.5f), true);
+                bm = Bitmap.createScaledBitmap(oldBitmap,
+                        Math.max(1, (int) (bm.getWidth() * scale + 0.5f)),
+                        Math.max(1, (int) (bm.getHeight() * scale + 0.5f)), true);
                 if (bm != oldBitmap) oldBitmap.recycle();
 
                 if (isNinePatch) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index fc84715..9bb90f1 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -867,8 +867,11 @@
 
     /**
      * Draw a line segment with the specified start and stop x,y coordinates,
-     * using the specified paint. NOTE: since a line is always "framed", the
-     * Style is ignored in the paint.
+     * using the specified paint.
+     *
+     * <p>Note that since a line is always "framed", the Style is ignored in the paint.</p>
+     *
+     * <p>Degenerate lines (length is 0) will not be drawn.</p>
      *
      * @param startX The x-coordinate of the start point of the line
      * @param startY The y-coordinate of the start point of the line
@@ -1563,6 +1566,10 @@
      * Save the canvas state, draw the picture, and restore the canvas state.
      * This differs from picture.draw(canvas), which does not perform any
      * save/restore.
+     *
+     * <p>
+     * <strong>Note:</strong> This forces the picture to internally call
+     * {@link Picture#endRecording} in order to prepare for playback.
      * 
      * @param picture  The picture to be drawn
      */
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 997141d..71e02f6 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -20,13 +20,12 @@
 import java.io.OutputStream;
 
 /**
- * A picture records drawing calls (via the canvas returned by beginRecording)
- * and can then play them back (via picture.draw(canvas) or canvas.drawPicture).
- * The picture's contents can also be written to a stream, and then later
- * restored to a new picture (via writeToStream / createFromStream). For most
- * content (esp. text, lines, rectangles), drawing a sequence from a picture can
- * be faster than the equivalent API calls, since the picture performs its
- * playback without incurring any java-call overhead.
+ * A Picture records drawing calls (via the canvas returned by beginRecording)
+ * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or 
+ * {@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles),
+ * drawing a sequence from a picture can be faster than the equivalent API
+ * calls, since the picture performs its playback without incurring any
+ * method-call overhead.
  */
 public class Picture {
     private Canvas mRecordingCanvas;
@@ -39,6 +38,9 @@
 
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
+    /**
+     * Creates an empty picture that is ready to record.
+     */
     public Picture() {
         this(nativeConstructor(0), false);
     }
@@ -55,9 +57,10 @@
     /**
      * To record a picture, call beginRecording() and then draw into the Canvas
      * that is returned. Nothing we appear on screen, but all of the draw
-     * commands (e.g. drawRect(...)) will be recorded. To stop recording, call
-     * endRecording(). At this point the Canvas that was returned must no longer
-     * be referenced, and nothing should be drawn into it.
+     * commands (e.g. {@link Canvas#drawRect(Rect, Paint)}) will be recorded.
+     * To stop recording, call endRecording(). After endRecording() the Canvas
+     * that was returned must no longer be used, and nothing should be drawn
+     * into it.
      */
     public Canvas beginRecording(int width, int height) {
         int ni = nativeBeginRecording(mNativePicture, width, height);
@@ -68,8 +71,8 @@
     /**
      * Call endRecording when the picture is built. After this call, the picture
      * may be drawn, but the canvas that was returned by beginRecording must not
-     * be referenced anymore. This is automatically called if Picture.draw() or
-     * Canvas.drawPicture() is called.
+     * be used anymore. This is automatically called if {@link Picture#draw}
+     * or {@link Canvas#drawPicture(Picture)} is called.
      */
     public void endRecording() {
         if (mRecordingCanvas != null) {
@@ -94,6 +97,10 @@
      * Draw this picture on the canvas. The picture may have the side effect
      * of changing the matrix and clip of the canvas.
      * 
+     * <p>
+     * <strong>Note:</strong> This forces the picture to internally call
+     * {@link Picture#endRecording()} in order to prepare for playback.
+     *
      * @param canvas  The picture is drawn to this canvas 
      */
     public void draw(Canvas canvas) {
@@ -105,26 +112,39 @@
 
     /**
      * Create a new picture (already recorded) from the data in the stream. This
-     * data was generated by a previous call to writeToStream().
-     * 
+     * data was generated by a previous call to writeToStream(). Pictures that
+     * have been persisted across device restarts are not guaranteed to decode
+     * properly and are highly discouraged.
+     *
+     * <p>
      * <strong>Note:</strong> a picture created from an input stream cannot be
      * replayed on a hardware accelerated canvas.
      * 
-     * @see #writeToStream(java.io.OutputStream) 
+     * @see #writeToStream(java.io.OutputStream)
+     * @deprecated The recommended alternative is to not use writeToStream and
+     * instead draw the picture into a Bitmap from which you can persist it as
+     * raw or compressed pixels.
      */
+    @Deprecated
     public static Picture createFromStream(InputStream stream) {
         return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true);
     }
 
     /**
      * Write the picture contents to a stream. The data can be used to recreate
-     * the picture in this or another process by calling createFromStream.
+     * the picture in this or another process by calling createFromStream(...)
+     * The resulting stream is NOT to be persisted across device restarts as
+     * there is no guarantee that the Picture can be successfully reconstructed.
      *
+     * <p>
      * <strong>Note:</strong> a picture created from an input stream cannot be
      * replayed on a hardware accelerated canvas.
-     * 
-     * @see #createFromStream(java.io.InputStream) 
+     *
+     * @see #createFromStream(java.io.InputStream)
+     * @deprecated The recommended alternative is to draw the picture into a
+     * Bitmap from which you can persist it as raw or compressed pixels.
      */
+    @Deprecated
     public void writeToStream(OutputStream stream) {
         // do explicit check before calling the native method
         if (stream == null) {
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.cpp b/libs/hwui/Caches.cpp
index 5befb95..a1cc2e8 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -50,9 +50,9 @@
 Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
     init();
     initFont();
-    initExtensions();
     initConstraints();
     initProperties();
+    initExtensions();
 
     mDebugLevel = readDebugLevel();
     ALOGD("Enabling debug mode %d", mDebugLevel);
@@ -103,15 +103,21 @@
 void Caches::initExtensions() {
     if (mExtensions.hasDebugMarker()) {
         eventMark = glInsertEventMarkerEXT;
-        startMark = glPushGroupMarkerEXT;
-        endMark = glPopGroupMarkerEXT;
+        if ((drawDeferDisabled || drawReorderDisabled)) {
+            startMark = glPushGroupMarkerEXT;
+            endMark = glPopGroupMarkerEXT;
+        } else {
+            startMark = startMarkNull;
+            endMark = endMarkNull;
+        }
+
     } else {
         eventMark = eventMarkNull;
         startMark = startMarkNull;
         endMark = endMarkNull;
     }
 
-    if (mExtensions.hasDebugLabel()) {
+    if (mExtensions.hasDebugLabel() && (drawDeferDisabled || drawReorderDisabled)) {
         setLabel = glLabelObjectEXT;
         getLabel = glGetObjectLabelEXT;
     } else {
@@ -164,6 +170,20 @@
         debugStencilClip = kStencilHide;
     }
 
+    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
+        drawDeferDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw defer enabled");
+    }
+
+    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
+        drawReorderDisabled = !strcasecmp(property, "true");
+        INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
+    } else {
+        INIT_LOGD("  Draw reorder enabled");
+    }
+
     return (prevDebugLayersUpdates != debugLayersUpdates) ||
             (prevDebugOverdraw != debugOverdraw) ||
             (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index c35ad883..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"
@@ -240,6 +243,9 @@
     Program* currentProgram;
     bool scissorEnabled;
 
+    bool drawDeferDisabled;
+    bool drawReorderDisabled;
+
     // VBO to draw with
     GLuint meshBuffer;
 
@@ -275,6 +281,8 @@
 
     GammaFontRenderer* fontRenderer;
 
+    TaskManager tasks;
+
     Dither dither;
     Stencil stencil;
 
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 8962964..a4e9950 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -146,6 +146,8 @@
     if (isEmpty()) return status; // nothing to flush
 
     DEFER_LOGD("--flushing");
+    renderer.eventMark("Flush");
+
     DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
     int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     int opCount = 0;
@@ -166,6 +168,7 @@
     }
 
     DEFER_LOGD("--flushed, drew %d batches (total %d ops)", mBatches.size(), opCount);
+
     renderer.restoreToCount(restoreTo);
     renderer.setDrawModifiers(restoreDrawModifiers);
     clear();
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index bdd539e..5781f4d 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -442,9 +442,8 @@
     for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
         DisplayListOp *op = mDisplayListData->displayListOps[i];
 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
-        Caches::getInstance().eventMark(strlen(op->name()), op->name());
+        renderer.eventMark(strlen(op->name()), op->name());
 #endif
-
         drawGlStatus |= op->replay(renderer, dirty, flags,
                 saveCount, level, mCaching, mMultipliedAlpha, deferredList);
         logBuffer.writeCommand(level, op->name());
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 9ecfb5a..105f45f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -147,7 +147,8 @@
 
         if (!renderer.storeDisplayState(state)) {
             // op wasn't quick-rejected, so defer
-            deferredList->add(this, renderer.disallowReorder());
+            deferredList->add(this, renderer.getCaches().drawReorderDisabled);
+            onDrawOpDeferred(renderer);
         }
 
         return DrawGlInfo::kStatusDone;
@@ -156,6 +157,9 @@
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) = 0;
 
+    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+    }
+
     // returns true if bounds exist
     virtual bool getLocalBounds(Rect& localBounds) { return false; }
 
@@ -1001,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);
@@ -1012,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));
     }
@@ -1081,6 +1090,12 @@
         OP_LOG("Draw some text, %d bytes", mBytesCount);
     }
 
+    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+        SkPaint* paint = getPaint(renderer);
+        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
+        fontRenderer.precache(paint, mText, mCount, mat4::identity());
+    }
+
     virtual DeferredDisplayList::OpBatchId getBatchId() {
         return mPaint->getColor() == 0xff000000 ?
                 DeferredDisplayList::kOpBatch_Text :
@@ -1156,6 +1171,19 @@
         mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
     }
 
+    /*
+     * When this method is invoked the state field  is initialized to have the
+     * final rendering state. We can thus use it to process data as it will be
+     * used at draw time.
+     */
+    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+        SkPaint* paint = getPaint(renderer);
+        FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
+        const bool pureTranslate = state.mMatrix.isPureTranslate();
+        const mat4 transform = renderer.findBestFontTransform(state.mMatrix);
+        fontRenderer.precache(paint, mText, mCount, transform);
+    }
+
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) {
         return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 16218fa..b011443 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -403,11 +403,7 @@
 
     DrawOp* op = new (alloc()) DrawTextOnPathOp(text, bytesCount, count, path,
             hOffset, vOffset, paint);
-    if (addDrawOp(op)) {
-        // precache if draw operation is visible
-        FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-        fontRenderer.precache(paint, text, count, mat4::identity());
-    }
+    addDrawOp(op);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -420,11 +416,7 @@
     paint = refPaint(paint);
 
     DrawOp* op = new (alloc()) DrawPosTextOp(text, bytesCount, count, positions, paint);
-    if (addDrawOp(op)) {
-        // precache if draw operation is visible
-        FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-        fontRenderer.precache(paint, text, count, mat4::identity());
-    }
+    addDrawOp(op);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -439,13 +431,7 @@
     paint = refPaint(paint);
 
     DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
-    if (addDrawOp(op)) {
-        // precache if draw operation is visible
-        FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-        const bool pureTranslate = mSnapshot->transform->isPureTranslate();
-        fontRenderer.precache(paint, text, count,
-                pureTranslate ? mat4::identity() : *mSnapshot->transform);
-    }
+    addDrawOp(op);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -515,17 +501,15 @@
     addOpInternal(op);
 }
 
-bool DisplayListRenderer::addDrawOp(DrawOp* op) {
-    bool rejected = false;
+void DisplayListRenderer::addDrawOp(DrawOp* op) {
     Rect localBounds;
     if (op->getLocalBounds(localBounds)) {
-        rejected = quickRejectNoScissor(localBounds.left, localBounds.top,
+        bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top,
                 localBounds.right, localBounds.bottom);
         op->setQuickRejected(rejected);
     }
     mHasDrawOps = true;
     addOpInternal(op);
-    return !rejected;
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index bff3b97..38619bf 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -195,7 +195,7 @@
 
     LinearAllocator& alloc() { return mDisplayListData->allocator; }
     void addStateOp(StateOp* op);
-    bool addDrawOp(DrawOp* op); // returns true if op not rejected
+    void addDrawOp(DrawOp* op);
     void addOpInternal(DisplayListOp* op) {
         insertRestoreToCount();
         insertTranslate();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index d5ea0f9..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"
@@ -242,35 +244,31 @@
     uint8_t* cacheBuffer = cacheTexture->getTexture();
     uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
 
-    // Zero out the borders
-    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
-        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
-        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
-    }
-
-    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
-            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
-        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
-        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
-    }
-
     // Copy the glyph image, taking the mask format into account
     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
     int stride = glyph.rowBytes();
 
+    uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+
     switch (format) {
         case SkMask::kA8_Format: {
             if (mGammaTable) {
                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
+                    row = cacheY * cacheWidth;
+                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
                         uint8_t tempCol = bitmapBuffer[bY + bX];
-                        cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
+                        cacheBuffer[row + cacheX] = mGammaTable[tempCol];
                     }
+                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                 }
             } else {
                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
-                    memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
-                            glyph.fWidth);
+                    row = cacheY * cacheWidth;
+                    memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
+                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
+                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                 }
             }
             break;
@@ -283,12 +281,15 @@
                 int rowBytes = stride;
                 uint8_t* buffer = bitmapBuffer;
 
+                row = cacheY * cacheWidth;
+                cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                 while (--rowBytes >= 0) {
                     uint8_t b = *buffer++;
                     for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
                         cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
                     }
                 }
+                cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
 
                 bitmapBuffer += stride;
             }
@@ -299,6 +300,9 @@
             break;
     }
 
+    row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+
     cachedGlyph->mIsValid = true;
 }
 
@@ -567,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;
@@ -653,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/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 14c7c39..9aa9615 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -92,11 +92,11 @@
     // who will invoke OpenGLRenderer::resume()
 }
 
-GLint LayerRenderer::getTargetFbo() {
+GLint LayerRenderer::getTargetFbo() const {
     return mLayer->getFbo();
 }
 
-bool LayerRenderer::suppressErrorChecks() {
+bool LayerRenderer::suppressErrorChecks() const {
     return true;
 }
 
@@ -104,7 +104,7 @@
 // Layer support
 ///////////////////////////////////////////////////////////////////////////////
 
-bool LayerRenderer::hasLayer() {
+bool LayerRenderer::hasLayer() const {
     return true;
 }
 
@@ -116,7 +116,7 @@
 // Dirty region tracking
 ///////////////////////////////////////////////////////////////////////////////
 
-Region* LayerRenderer::getRegion() {
+Region* LayerRenderer::getRegion() const {
     if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
         return OpenGLRenderer::getRegion();
     }
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 7a8bdc5..5f86731 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -65,10 +65,10 @@
 
 protected:
     virtual void ensureStencilBuffer();
-    virtual bool hasLayer();
-    virtual Region* getRegion();
-    virtual GLint getTargetFbo();
-    virtual bool suppressErrorChecks();
+    virtual bool hasLayer() const;
+    virtual Region* getRegion() const;
+    virtual GLint getTargetFbo() const;
+    virtual bool suppressErrorChecks() const;
 
 private:
     void generateMesh();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 2d017df..6a5ea51 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -232,11 +232,11 @@
     memcpy(v, data, sizeof(data));
 }
 
-float Matrix4::getTranslateX() {
+float Matrix4::getTranslateX() const {
     return data[kTranslateX];
 }
 
-float Matrix4::getTranslateY() {
+float Matrix4::getTranslateY() const {
     return data[kTranslateY];
 }
 
@@ -454,6 +454,14 @@
     }
 }
 
+void Matrix4::decomposeScale(float& sx, float& sy) const {
+    float len;
+    len = data[mat4::kScaleX] * data[mat4::kScaleX] + data[mat4::kSkewX] * data[mat4::kSkewX];
+    sx = copysignf(sqrtf(len), data[mat4::kScaleX]);
+    len = data[mat4::kScaleY] * data[mat4::kScaleY] + data[mat4::kSkewY] * data[mat4::kSkewY];
+    sy = copysignf(sqrtf(len), data[mat4::kScaleY]);
+}
+
 void Matrix4::dump() const {
     ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType());
     ALOGD("  %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 2fe96bc..7b7357e 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -79,6 +79,20 @@
         load(v);
     }
 
+    float operator[](int index) const {
+        return data[index];
+    }
+
+    float& operator[](int index) {
+        mType = kTypeUnknown;
+        return data[index];
+    }
+
+    Matrix4& operator=(const SkMatrix& v) {
+        load(v);
+        return *this;
+    }
+
     void loadIdentity();
 
     void load(const float* v);
@@ -147,8 +161,10 @@
     void mapRect(Rect& r) const;
     void mapPoint(float& x, float& y) const;
 
-    float getTranslateX();
-    float getTranslateY();
+    float getTranslateX() const;
+    float getTranslateY() const;
+
+    void decomposeScale(float& sx, float& sy) const;
 
     void dump() const;
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7e9734f..7fe0a69 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -122,8 +122,6 @@
     mFirstSnapshot = new Snapshot;
 
     mScissorOptimizationDisabled = false;
-    mDrawDeferDisabled = false;
-    mDrawReorderDisabled = false;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -140,20 +138,6 @@
     } else {
         INIT_LOGD("  Scissor optimization enabled");
     }
-
-    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
-        mDrawDeferDisabled = !strcasecmp(property, "true");
-        INIT_LOGD("  Draw defer %s", mDrawDeferDisabled ? "disabled" : "enabled");
-    } else {
-        INIT_LOGD("  Draw defer enabled");
-    }
-
-    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
-        mDrawReorderDisabled = !strcasecmp(property, "true");
-        INIT_LOGD("  Draw reorder %s", mDrawReorderDisabled ? "disabled" : "enabled");
-    } else {
-        INIT_LOGD("  Draw reorder enabled");
-    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -292,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;
@@ -462,6 +452,10 @@
 // Debug
 ///////////////////////////////////////////////////////////////////////////////
 
+void OpenGLRenderer::eventMark(const char* name) const {
+    mCaches.eventMark(0, name);
+}
+
 void OpenGLRenderer::startMark(const char* name) const {
     mCaches.startMark(0, name);
 }
@@ -735,7 +729,7 @@
     Rect clip;
     Rect bounds(left, top, right, bottom);
     Rect untransformedBounds(bounds);
-    mSnapshot->transform->mapRect(bounds);
+    currentTransform().mapRect(bounds);
 
     // Layers only make sense if they are in the framebuffer's bounds
     if (bounds.intersect(*mSnapshot->clipRect)) {
@@ -750,7 +744,7 @@
         } else if (fboLayer) {
             clip.set(bounds);
             mat4 inverse;
-            inverse.loadInverse(*mSnapshot->transform);
+            inverse.loadInverse(currentTransform());
             inverse.mapRect(clip);
             clip.snapToPixelBoundaries();
             if (clip.intersect(untransformedBounds)) {
@@ -950,11 +944,11 @@
     } else {
         setupDrawExternalTexture(layer->getTexture());
     }
-    if (mSnapshot->transform->isPureTranslate() &&
+    if (currentTransform().isPureTranslate() &&
             layer->getWidth() == (uint32_t) rect.getWidth() &&
             layer->getHeight() == (uint32_t) rect.getHeight()) {
-        const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
-        const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
+        const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
+        const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
 
         layer->setFilter(GL_NEAREST);
         setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
@@ -978,15 +972,15 @@
 
         float x = rect.left;
         float y = rect.top;
-        bool simpleTransform = mSnapshot->transform->isPureTranslate() &&
+        bool simpleTransform = currentTransform().isPureTranslate() &&
                 layer->getWidth() == (uint32_t) rect.getWidth() &&
                 layer->getHeight() == (uint32_t) rect.getHeight();
 
         if (simpleTransform) {
             // When we're swapping, the layer is already in screen coordinates
             if (!swap) {
-                x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
-                y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
+                x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
+                y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
             }
 
             layer->setFilter(GL_NEAREST, true);
@@ -1053,9 +1047,9 @@
         setupDrawPureColorUniforms();
         setupDrawColorFilterUniforms();
         setupDrawTexture(layer->getTexture());
-        if (mSnapshot->transform->isPureTranslate()) {
-            const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f);
-            const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f);
+        if (currentTransform().isPureTranslate()) {
+            const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f);
+            const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f);
 
             layer->setFilter(GL_NEAREST);
             setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true);
@@ -1253,7 +1247,7 @@
 }
 
 void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
-    mSnapshot->transform->load(state.mMatrix);
+    currentTransform().load(state.mMatrix);
 
     // NOTE: a clip RECT will be saved and restored, but DeferredDisplayState doesn't support
     // complex clips. In the future, we should add support for deferral of operations clipped by
@@ -1268,42 +1262,42 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::translate(float dx, float dy) {
-    mSnapshot->transform->translate(dx, dy, 0.0f);
+    currentTransform().translate(dx, dy, 0.0f);
 }
 
 void OpenGLRenderer::rotate(float degrees) {
-    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
+    currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f);
 }
 
 void OpenGLRenderer::scale(float sx, float sy) {
-    mSnapshot->transform->scale(sx, sy, 1.0f);
+    currentTransform().scale(sx, sy, 1.0f);
 }
 
 void OpenGLRenderer::skew(float sx, float sy) {
-    mSnapshot->transform->skew(sx, sy);
+    currentTransform().skew(sx, sy);
 }
 
 void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
     if (matrix) {
-        mSnapshot->transform->load(*matrix);
+        currentTransform().load(*matrix);
     } else {
-        mSnapshot->transform->loadIdentity();
+        currentTransform().loadIdentity();
     }
 }
 
 bool OpenGLRenderer::hasRectToRectTransform() {
-    return CC_LIKELY(mSnapshot->transform->rectToRect());
+    return CC_LIKELY(currentTransform().rectToRect());
 }
 
 void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
-    mSnapshot->transform->copyTo(*matrix);
+    currentTransform().copyTo(*matrix);
 }
 
 void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
     SkMatrix transform;
-    mSnapshot->transform->copyTo(transform);
+    currentTransform().copyTo(transform);
     transform.preConcat(*matrix);
-    mSnapshot->transform->load(transform);
+    currentTransform().load(transform);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1397,7 +1391,7 @@
     }
 
     Rect r(left, top, right, bottom);
-    mSnapshot->transform->mapRect(r);
+    currentTransform().mapRect(r);
     r.snapToPixelBoundaries();
 
     Rect clipRect(*mSnapshot->clipRect);
@@ -1413,7 +1407,7 @@
     }
 
     transformed.set(left, top, right, bottom);
-    mSnapshot->transform->mapRect(transformed);
+    currentTransform().mapRect(transformed);
     transformed.snapToPixelBoundaries();
 
     clip.set(*mSnapshot->clipRect);
@@ -1438,7 +1432,7 @@
     }
 
     Rect r(left, top, right, bottom);
-    mSnapshot->transform->mapRect(r);
+    currentTransform().mapRect(r);
     r.snapToPixelBoundaries();
 
     Rect clipRect(*mSnapshot->clipRect);
@@ -1461,7 +1455,7 @@
 }
 
 bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
-    if (CC_LIKELY(mSnapshot->transform->rectToRect())) {
+    if (CC_LIKELY(currentTransform().rectToRect())) {
         bool clipped = mSnapshot->clip(left, top, right, bottom, op);
         if (clipped) {
             dirtyClip();
@@ -1477,7 +1471,7 @@
 
 bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
     SkMatrix transform;
-    mSnapshot->transform->copyTo(transform);
+    currentTransform().copyTo(transform);
 
     SkPath transformed;
     path->transform(transform, &transformed);
@@ -1654,8 +1648,8 @@
         bool ignoreTransform) {
     mModelView.loadTranslate(left, top, 0.0f);
     if (!ignoreTransform) {
-        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
-        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
+        if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform());
     } else {
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
         if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
@@ -1663,7 +1657,7 @@
 }
 
 void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
-    mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), *mSnapshot->transform, offset);
+    mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), currentTransform(), offset);
 }
 
 void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
@@ -1676,9 +1670,9 @@
     }
     bool dirty = right - left > 0.0f && bottom - top > 0.0f;
     if (!ignoreTransform) {
-        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform());
         if (mTrackDirtyRegions && dirty) {
-            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+            dirtyLayer(left, top, right, bottom, currentTransform());
         }
     } else {
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
@@ -1706,7 +1700,7 @@
 void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
     if (mDrawModifiers.mShader) {
         if (ignoreTransform) {
-            mModelView.loadInverse(*mSnapshot->transform);
+            mModelView.loadInverse(currentTransform());
         }
         mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
                 mModelView, *mSnapshot, &mTextureUnit);
@@ -1815,7 +1809,7 @@
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
-        if (CC_UNLIKELY(mDrawDeferDisabled)) {
+        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
             return displayList->replay(*this, dirty, flags, 0);
         }
 
@@ -1845,9 +1839,9 @@
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
 
     bool ignoreTransform = false;
-    if (mSnapshot->transform->isPureTranslate()) {
-        x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
-        y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+    if (currentTransform().isPureTranslate()) {
+        x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
+        y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
         ignoreTransform = true;
 
         texture->setFilter(GL_NEAREST, true);
@@ -2011,7 +2005,7 @@
     float a = alpha / 255.0f;
 
     if (hasLayer()) {
-        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        dirtyLayer(left, top, right, bottom, currentTransform());
     }
 
     setupDraw();
@@ -2081,9 +2075,9 @@
     bool useScaleTransform = mDrawModifiers.mShader && scaled;
     bool ignoreTransform = false;
 
-    if (CC_LIKELY(mSnapshot->transform->isPureTranslate() && !useScaleTransform)) {
-        float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
-        float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
+    if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) {
+        float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f);
+        float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f);
 
         dstRight = x + (dstRight - dstLeft);
         dstBottom = y + (dstBottom - dstTop);
@@ -2162,11 +2156,11 @@
         texture->setWrap(GL_CLAMP_TO_EDGE, true);
         texture->setFilter(GL_LINEAR, true);
 
-        const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+        const bool pureTranslate = currentTransform().isPureTranslate();
         // Mark the current layer dirty where we are going to draw the patch
         if (hasLayer() && mesh->hasEmptyQuads) {
-            const float offsetX = left + mSnapshot->transform->getTranslateX();
-            const float offsetY = top + mSnapshot->transform->getTranslateY();
+            const float offsetX = left + currentTransform().getTranslateX();
+            const float offsetY = top + currentTransform().getTranslateY();
             const size_t count = mesh->quads.size();
             for (size_t i = 0; i < count; i++) {
                 const Rect& bounds = mesh->quads.itemAt(i);
@@ -2176,14 +2170,14 @@
                     dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight());
                 } else {
                     dirtyLayer(left + bounds.left, top + bounds.top,
-                            left + bounds.right, top + bounds.bottom, *mSnapshot->transform);
+                            left + bounds.right, top + bounds.bottom, currentTransform());
                 }
             }
         }
 
         if (CC_LIKELY(pureTranslate)) {
-            const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
-            const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+            const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
+            const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
 
             drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f,
                     mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
@@ -2265,7 +2259,7 @@
 
     SkRect bounds = path.getBounds();
     PathTessellator::expandBoundsForStroke(bounds, paint, false);
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
+    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
 
     return drawVertexBuffer(vertexBuffer, paint);
 }
@@ -2294,7 +2288,7 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
+    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
 
     bool useOffset = !paint->isAntiAlias();
     return drawVertexBuffer(buffer, paint, useOffset);
@@ -2354,7 +2348,7 @@
         float top = points[i + 1] - halfWidth;
         float bottom = points [i + 1] + halfWidth;
 
-        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        dirtyLayer(left, top, right, bottom, currentTransform());
     }
 
     glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
@@ -2514,7 +2508,7 @@
         return drawConvexPath(path, p);
     }
 
-    if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
+    if (p->isAntiAlias() && !currentTransform().isSimple()) {
         SkPath path;
         path.addRect(left, top, right, bottom);
         return drawConvexPath(path, p);
@@ -2574,16 +2568,16 @@
     }
 
     // NOTE: Skia does not support perspective transform on drawPosText yet
-    if (!mSnapshot->transform->isSimple()) {
+    if (!currentTransform().isSimple()) {
         return DrawGlInfo::kStatusDone;
     }
 
     float x = 0.0f;
     float y = 0.0f;
-    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+    const bool pureTranslate = currentTransform().isPureTranslate();
     if (pureTranslate) {
-        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
-        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+        x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
+        y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
     }
 
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
@@ -2599,7 +2593,7 @@
     }
 
     // Pick the appropriate texture filtering
-    bool linearFilter = mSnapshot->transform->changesBounds();
+    bool linearFilter = currentTransform().changesBounds();
     if (pureTranslate && !linearFilter) {
         linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
     }
@@ -2630,7 +2624,7 @@
             positions, hasActiveLayer ? &bounds : NULL)) {
         if (hasActiveLayer) {
             if (!pureTranslate) {
-                mSnapshot->transform->mapRect(bounds);
+                currentTransform().mapRect(bounds);
             }
             dirtyLayerUnchecked(bounds, getRegion());
         }
@@ -2639,6 +2633,22 @@
     return DrawGlInfo::kStatusDrew;
 }
 
+mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
+    mat4 fontTransform;
+    if (CC_LIKELY(transform.isPureTranslate())) {
+        fontTransform = mat4::identity();
+    } else {
+        if (CC_UNLIKELY(transform.isPerspective())) {
+            fontTransform = mat4::identity();
+        } else {
+            float sx, sy;
+            currentTransform().decomposeScale(sx, sy);
+            fontTransform.loadScale(sx, sy, 1.0f);
+        }
+    }
+    return fontTransform;
+}
+
 status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, const float* positions, SkPaint* paint, float length) {
     if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
@@ -2665,12 +2675,13 @@
 
     const float oldX = x;
     const float oldY = y;
-    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
-    const bool isPerspective = mSnapshot->transform->isPerspective();
+
+    const mat4& transform = currentTransform();
+    const bool pureTranslate = transform.isPureTranslate();
 
     if (CC_LIKELY(pureTranslate)) {
-        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
-        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+        x = (int) floorf(x + transform.getTranslateX() + 0.5f);
+        y = (int) floorf(y + transform.getTranslateY() + 0.5f);
     }
 
     int alpha;
@@ -2685,7 +2696,21 @@
                 alpha, mode, oldX, oldY);
     }
 
-    fontRenderer.setFont(paint, pureTranslate ? mat4::identity() : *mSnapshot->transform);
+    const bool hasActiveLayer = hasLayer();
+
+    // We only pass a partial transform to the font renderer. That partial
+    // matrix defines how glyphs are rasterized. Typically we want glyphs
+    // to be rasterized at their final size on screen, which means the partial
+    // matrix needs to take the scale factor into account.
+    // When a partial matrix is used to transform glyphs during rasterization,
+    // the mesh is generated with the inverse transform (in the case of scale,
+    // the mesh is generated at 1.0 / scale for instance.) This allows us to
+    // apply the full transform matrix at draw time in the vertex shader.
+    // Applying the full matrix in the shader is the easiest way to handle
+    // rotation and perspective and allows us to always generated quads in the
+    // font renderer which greatly simplifies the code, clipping in particular.
+    mat4 fontTransform = findBestFontTransform(transform);
+    fontRenderer.setFont(paint, fontTransform);
 
     // Pick the appropriate texture filtering
     bool linearFilter = !pureTranslate || fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
@@ -2701,20 +2726,19 @@
     setupDrawShader();
     setupDrawBlending(true, mode);
     setupDrawProgram();
-    setupDrawModelView(x, y, x, y, !isPerspective, true);
+    setupDrawModelView(x, y, x, y, pureTranslate, true);
     // See comment above; the font renderer must use texture unit 0
     // assert(mTextureUnit == 0)
     setupDrawTexture(fontRenderer.getTexture(linearFilter));
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawShaderUniforms(!isPerspective);
+    setupDrawShaderUniforms(pureTranslate);
     setupDrawTextGammaUniforms();
 
-    const Rect* clip = mSnapshot->hasPerspectiveTransform() ? NULL : mSnapshot->clipRect;
+    // TODO: Implement better clipping for scaled/rotated text
+    const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
-    const bool hasActiveLayer = hasLayer();
-
     bool status;
     if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
         SkPaint paintCopy(*paint);
@@ -2727,8 +2751,8 @@
     }
 
     if (status && hasActiveLayer) {
-        if (isPerspective) {
-            mSnapshot->transform->mapRect(bounds);
+        if (!pureTranslate) {
+            transform.mapRect(bounds);
         }
         dirtyLayerUnchecked(bounds, getRegion());
     }
@@ -2776,7 +2800,7 @@
     if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
             hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
         if (hasActiveLayer) {
-            mSnapshot->transform->mapRect(bounds);
+            currentTransform().mapRect(bounds);
             dirtyLayerUnchecked(bounds, getRegion());
         }
     }
@@ -2811,7 +2835,7 @@
         transform = &layer->getTransform();
         if (!transform->isIdentity()) {
             save(0);
-            mSnapshot->transform->multiply(*transform);
+            currentTransform().multiply(*transform);
         }
     }
 
@@ -2849,9 +2873,9 @@
             setupDrawPureColorUniforms();
             setupDrawColorFilterUniforms();
             setupDrawTexture(layer->getTexture());
-            if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
-                int tx = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
-                int ty = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+            if (CC_LIKELY(currentTransform().isPureTranslate())) {
+                int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f);
+                int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f);
 
                 layer->setFilter(GL_NEAREST);
                 setupDrawModelViewTranslate(tx, ty,
@@ -3069,6 +3093,9 @@
 
 status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
         SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
+    if (count == 0) {
+        return DrawGlInfo::kStatusDone;
+    }
 
     float left = FLT_MAX;
     float top = FLT_MAX;
@@ -3085,24 +3112,22 @@
         float r = rects[index + 2];
         float b = rects[index + 3];
 
-        if (ignoreTransform || !quickRejectNoScissor(left, top, right, bottom)) {
-            Vertex::set(vertex++, l, b);
-            Vertex::set(vertex++, l, t);
-            Vertex::set(vertex++, r, t);
-            Vertex::set(vertex++, l, b);
-            Vertex::set(vertex++, r, t);
-            Vertex::set(vertex++, r, b);
+        Vertex::set(vertex++, l, b);
+        Vertex::set(vertex++, l, t);
+        Vertex::set(vertex++, r, t);
+        Vertex::set(vertex++, l, b);
+        Vertex::set(vertex++, r, t);
+        Vertex::set(vertex++, r, b);
 
-            vertexCount += 6;
+        vertexCount += 6;
 
-            left = fminf(left, l);
-            top = fminf(top, t);
-            right = fmaxf(right, r);
-            bottom = fmaxf(bottom, b);
-        }
+        left = fminf(left, l);
+        top = fminf(top, t);
+        right = fmaxf(right, r);
+        bottom = fmaxf(bottom, b);
     }
 
-    if (count == 0 || (clip && quickReject(left, top, right, bottom))) {
+    if (clip && quickReject(left, top, right, bottom)) {
         return DrawGlInfo::kStatusDone;
     }
 
@@ -3121,7 +3146,7 @@
     setupDrawVertices((GLvoid*) &mesh[0].position[0]);
 
     if (dirty && hasLayer()) {
-        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        dirtyLayer(left, top, right, bottom, currentTransform());
     }
 
     glDrawArrays(GL_TRIANGLES, 0, vertexCount);
@@ -3160,9 +3185,9 @@
 
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
 
-    if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
-        const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
-        const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
+    if (CC_LIKELY(currentTransform().isPureTranslate())) {
+        const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
+        const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
 
         texture->setFilter(GL_NEAREST, true);
         drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index cfad593..e961af2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -270,8 +270,6 @@
                 (mSnapshot->flags & Snapshot::kFlagFboTarget); // ensure we're not in a layer
     }
 
-    bool disallowReorder() { return mDrawReorderDisabled; }
-
     bool storeDisplayState(DeferredDisplayState& state);
     void restoreDisplayState(const DeferredDisplayState& state);
 
@@ -282,6 +280,10 @@
         return mSnapshot->transform->isSimple();
     }
 
+    Caches& getCaches() {
+        return mCaches;
+    }
+
     /**
      * Sets the alpha on the current snapshot. This alpha value will be modulated
      * with other alpha values when drawing primitives.
@@ -291,6 +293,11 @@
     }
 
     /**
+     * Inserts a named event marker in the stream of GL commands.
+     */
+    void eventMark(const char* name) const;
+
+    /**
      * Inserts a named group marker in the stream of GL commands. This marker
      * can be used by tools to group commands into logical groups. A call to
      * this method must always be followed later on by a call to endMark().
@@ -328,6 +335,12 @@
         }
     }
 
+    /**
+     * Return the best transform to use to rasterize text given a full
+     * transform matrix.
+     */
+    mat4 findBestFontTransform(const mat4& transform) const;
+
 protected:
     /**
      * Computes the projection matrix, initialize the first snapshot
@@ -377,28 +390,28 @@
     /**
      * Returns the current snapshot.
      */
-    sp<Snapshot> getSnapshot() {
+    sp<Snapshot> getSnapshot() const {
         return mSnapshot;
     }
 
     /**
      * Returns the region of the current layer.
      */
-    virtual Region* getRegion() {
+    virtual Region* getRegion() const {
         return mSnapshot->region;
     }
 
     /**
      * Indicates whether rendering is currently targeted at a layer.
      */
-    virtual bool hasLayer() {
+    virtual bool hasLayer() const {
         return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
     }
 
     /**
      * Returns the name of the FBO this renderer is rendering into.
      */
-    virtual GLint getTargetFbo() {
+    virtual GLint getTargetFbo() const {
         return 0;
     }
 
@@ -435,14 +448,10 @@
     /**
      * Set to true to suppress error checks at the end of a frame.
      */
-    virtual bool suppressErrorChecks() {
+    virtual bool suppressErrorChecks() const {
         return false;
     }
 
-    Caches& getCaches() {
-        return mCaches;
-    }
-
 private:
     /**
      * Discards the content of the framebuffer if supported by the driver.
@@ -900,6 +909,10 @@
         mDirtyClip = true;
     }
 
+    inline mat4& currentTransform() const {
+        return *mSnapshot->transform;
+    }
+
     // Dimensions of the drawing surface
     int mWidth, mHeight;
 
@@ -957,8 +970,6 @@
     // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
     // Properties.h
     bool mScissorOptimizationDisabled;
-    bool mDrawDeferDisabled;
-    bool mDrawReorderDisabled;
 
     // No-ops start/endTiling when set
     bool mSuppressTiling;
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/Program.h b/libs/hwui/Program.h
index 13ee336..e8b6d47 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -24,6 +24,7 @@
 
 #include <SkXfermode.h>
 
+#include "Debug.h"
 #include "Matrix.h"
 #include "Properties.h"
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 0f014cb..f78fb2d 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -433,6 +433,12 @@
 
 Program* ProgramCache::get(const ProgramDescription& description) {
     programid key = description.key();
+    if (key == (PROGRAM_KEY_TEXTURE | PROGRAM_KEY_A8_TEXTURE)) {
+        // program for A8, unmodulated, texture w/o shader (black text/path textures) is equivalent
+        // to standard texture program (bitmaps, patches). Consider them equivalent.
+        key = PROGRAM_KEY_TEXTURE;
+    }
+
     ssize_t index = mCache.indexOfKey(key);
     Program* program = NULL;
     if (index < 0) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 6cfe0c7..1ca148d 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -31,17 +31,6 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_PROGRAMS
-    #define PROGRAM_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-    #define PROGRAM_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
 // Cache
 ///////////////////////////////////////////////////////////////////////////////
 
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/font/Font.cpp b/libs/hwui/font/Font.cpp
index d48b612..c932087 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -53,10 +53,11 @@
     mStrokeWidth = paint->getStrokeWidth();
     mAntiAliasing = paint->isAntiAlias();
     mLookupTransform.reset();
-    mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
-    mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
-    mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
-    mLookupTransform[SkMatrix::kMSkewY] = matrix.data[mat4::kSkewY];
+    mLookupTransform[SkMatrix::kMScaleX] = roundf(fmaxf(1.0f, matrix[mat4::kScaleX]));
+    mLookupTransform[SkMatrix::kMScaleY] = roundf(fmaxf(1.0f, matrix[mat4::kScaleY]));
+    if (!mLookupTransform.invert(&mInverseLookupTransform)) {
+        ALOGW("Could not query the inverse lookup transform for this font");
+    }
 }
 
 Font::~Font() {
@@ -78,8 +79,6 @@
     hash = JenkinsHashMix(hash, int(mAntiAliasing));
     hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
     hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
-    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewX]));
-    hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewY]));
     return JenkinsHashWhiten(hash);
 }
 
@@ -119,16 +118,6 @@
     if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
             rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;
 
-    if (lhs.mLookupTransform[SkMatrix::kMSkewX] <
-            rhs.mLookupTransform[SkMatrix::kMSkewX]) return -1;
-    if (lhs.mLookupTransform[SkMatrix::kMSkewX] >
-            rhs.mLookupTransform[SkMatrix::kMSkewX]) return +1;
-
-    if (lhs.mLookupTransform[SkMatrix::kMSkewY] <
-            rhs.mLookupTransform[SkMatrix::kMSkewY]) return -1;
-    if (lhs.mLookupTransform[SkMatrix::kMSkewY] >
-            rhs.mLookupTransform[SkMatrix::kMSkewY]) return +1;
-
     return 0;
 }
 
@@ -182,20 +171,15 @@
             nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
 }
 
-void Font::drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
+void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
-    SkMatrix i;
-    if (!mDescription.mLookupTransform.invert(&i)) {
-        return;
-    }
-
     SkPoint p[4];
-    p[0].set(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
-    p[1].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
-    p[2].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
-    p[3].set(glyph->mBitmapLeft, glyph->mBitmapTop);
+    p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
+    p[1].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
+    p[2].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
+    p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop);
 
-    i.mapPoints(p, 4);
+    mDescription.mInverseLookupTransform.mapPoints(p, 4);
 
     p[0].offset(x, y);
     p[1].offset(x, y);
@@ -208,10 +192,10 @@
     float v2 = glyph->mBitmapMaxV;
 
     mState->appendRotatedMeshQuad(
-            p[0].fX, p[0].fY, u1, v2,
-            p[1].fX, p[1].fY, u2, v2,
-            p[2].fX, p[2].fY, u2, v1,
-            p[3].fX, p[3].fY, u1, v1, glyph->mCacheTexture);
+            p[0].x(), p[0].y(), u1, v2,
+            p[1].x(), p[1].y(), u2, v2,
+            p[2].x(), p[2].y(), u2, v1,
+            p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture);
 }
 
 void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
@@ -265,14 +249,14 @@
     const float v2 = glyph->mBitmapMaxV;
 
     mState->appendRotatedMeshQuad(
-            position->fX + destination[0].fX,
-            position->fY + destination[0].fY, u1, v2,
-            position->fX + destination[1].fX,
-            position->fY + destination[1].fY, u2, v2,
-            position->fX + destination[2].fX,
-            position->fY + destination[2].fY, u2, v1,
-            position->fX + destination[3].fX,
-            position->fY + destination[3].fY, u1, v1,
+            position->x() + destination[0].x(),
+            position->y() + destination[0].y(), u1, v2,
+            position->x() + destination[1].x(),
+            position->y() + destination[1].y(), u2, v2,
+            position->x() + destination[2].x(),
+            position->y() + destination[2].y(), u2, v1,
+            position->x() + destination[3].x(),
+            position->y() + destination[3].y(), u1, v1,
             glyph->mCacheTexture);
 }
 
@@ -284,7 +268,8 @@
 
         // Is the glyph still in texture cache?
         if (!cachedGlyph->mIsValid) {
-            const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, &mDescription.mLookupTransform);
+            const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit,
+                    &mDescription.mLookupTransform);
             updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
         }
     } else {
@@ -389,18 +374,17 @@
 
     static RenderGlyph gRenderGlyph[] = {
             &android::uirenderer::Font::drawCachedGlyph,
-            &android::uirenderer::Font::drawCachedGlyphPerspective,
+            &android::uirenderer::Font::drawCachedGlyphTransformed,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
             &android::uirenderer::Font::measureCachedGlyph,
             &android::uirenderer::Font::measureCachedGlyph
     };
-    RenderGlyph render = gRenderGlyph[(mode << 1) + mTransform.isPerspective()];
+    RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
 
     text += start;
     int glyphsCount = 0;
 
-    const bool applyTransform = !mTransform.isIdentity() && !mTransform.isPerspective();
     const SkPaint::Align align = paint->getTextAlign();
 
     while (glyphsCount < numGlyphs) {
@@ -419,10 +403,6 @@
             float penX = x + positions[(glyphsCount << 1)];
             float penY = y + positions[(glyphsCount << 1) + 1];
 
-            if (applyTransform) {
-                mTransform.mapPoint(penX, penY);
-            }
-
             (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
                     bitmap, bitmapW, bitmapH, bounds, positions);
         }
@@ -496,7 +476,7 @@
         font = new Font(state, description);
         state->mActiveFonts.put(description, font);
     }
-    font->mTransform.load(matrix);
+    font->mIdentityTransform = matrix.isIdentity();
 
     return font;
 }
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 542b552..52cca1c 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -70,6 +70,7 @@
         float mStrokeWidth;
         bool mAntiAliasing;
         SkMatrix mLookupTransform;
+        SkMatrix mInverseLookupTransform;
     };
 
     ~Font();
@@ -124,7 +125,7 @@
     void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
             Rect* bounds, const float* pos);
-    void drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
+    void drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
             Rect* bounds, const float* pos);
     void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
@@ -141,7 +142,7 @@
     // Cache of glyphs
     DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
 
-    mat4 mTransform;
+    bool mIdentityTransform;
 };
 
 inline int strictly_order_type(const Font::FontDescription& lhs,
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/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 3147ea5..4cd3e37 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -117,6 +117,9 @@
      * Reads Exif tags from the specified JPEG file.
      */
     public ExifInterface(String filename) throws IOException {
+        if (filename == null) {
+            throw new IllegalArgumentException("filename cannot be null");
+        }
         mFilename = filename;
         loadAttributes();
     }
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 d5515eb..b6b49a2 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -263,6 +263,15 @@
             Surface surface, MediaCrypto crypto, int flags);
 
     /**
+     * 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.
+     */
+    public native final Surface createInputSurface();
+
+    /**
      * After successfully configuring the component, call start. On return
      * you can query the component for its input/output buffers.
      */
@@ -458,6 +467,13 @@
     public native final void releaseOutputBuffer(int index, boolean render);
 
     /**
+     * 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}.
+     */
+    public native final void signalEndOfInputStream();
+
+    /**
      * Call this after dequeueOutputBuffer signals a format change by returning
      * {@link #INFO_OUTPUT_FORMAT_CHANGED}
      */
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f6593e0..1501c79 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -93,6 +93,9 @@
         public final static int COLOR_Format24BitABGR6666           = 43;
 
         public final static int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
+        // 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/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 8eb9332..756638c 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -48,7 +48,7 @@
 
     /* Maximum pixels size for created bitmap. */
     private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;
-    private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128;
+    private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120;
     private static final int UNCONSTRAINED = -1;
 
     /* Options used internally. */
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_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 86700b3..3a42db2 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -123,6 +123,11 @@
     return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
 }
 
+status_t JMediaCodec::createInputSurface(
+        sp<IGraphicBufferProducer>* bufferProducer) {
+    return mCodec->createInputSurface(bufferProducer);
+}
+
 status_t JMediaCodec::start() {
     return mCodec->start();
 }
@@ -190,6 +195,10 @@
         : mCodec->releaseOutputBuffer(index);
 }
 
+status_t JMediaCodec::signalEndOfInputStream() {
+    return mCodec->signalEndOfInputStream();
+}
+
 status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
     sp<AMessage> msg;
     status_t err;
@@ -417,6 +426,29 @@
     throwExceptionAsNecessary(env, err);
 }
 
+static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
+        jobject thiz) {
+    ALOGV("android_media_MediaCodec_createInputSurface");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    // Tell the MediaCodec that we want to use a Surface as input.
+    sp<IGraphicBufferProducer> bufferProducer;
+    status_t err = codec->createInputSurface(&bufferProducer);
+    if (err != NO_ERROR) {
+        throwExceptionAsNecessary(env, err);
+        return NULL;
+    }
+
+    // Wrap the IGBP in a Java-language Surface.
+    return android_view_Surface_createFromIGraphicBufferProducer(env,
+            bufferProducer);
+}
+
 static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
     ALOGV("android_media_MediaCodec_start");
 
@@ -685,6 +717,21 @@
     throwExceptionAsNecessary(env, err);
 }
 
+static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
+        jobject thiz) {
+    ALOGV("android_media_MediaCodec_signalEndOfInputStream");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = codec->signalEndOfInputStream();
+
+    throwExceptionAsNecessary(env, err);
+}
+
 static jobject android_media_MediaCodec_getOutputFormatNative(
         JNIEnv *env, jobject thiz) {
     ALOGV("android_media_MediaCodec_getOutputFormatNative");
@@ -852,6 +899,9 @@
       "Landroid/media/MediaCrypto;I)V",
       (void *)android_media_MediaCodec_native_configure },
 
+    { "createInputSurface", "()Landroid/view/Surface;",
+      (void *)android_media_MediaCodec_createInputSurface },
+
     { "start", "()V", (void *)android_media_MediaCodec_start },
     { "stop", "()V", (void *)android_media_MediaCodec_stop },
     { "flush", "()V", (void *)android_media_MediaCodec_flush },
@@ -871,6 +921,9 @@
     { "releaseOutputBuffer", "(IZ)V",
       (void *)android_media_MediaCodec_releaseOutputBuffer },
 
+    { "signalEndOfInputStream", "()V",
+      (void *)android_media_MediaCodec_signalEndOfInputStream },
+
     { "getOutputFormatNative", "()Ljava/util/Map;",
       (void *)android_media_MediaCodec_getOutputFormatNative },
 
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index f478788..282d2c5 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -47,6 +47,8 @@
             const sp<ICrypto> &crypto,
             int flags);
 
+    status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+
     status_t start();
     status_t stop();
 
@@ -76,6 +78,8 @@
 
     status_t releaseOutputBuffer(size_t index, bool render);
 
+    status_t signalEndOfInputStream();
+
     status_t getOutputFormat(JNIEnv *env, jobject *format) const;
 
     status_t getBuffers(
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 7421022..e9f6a1e 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -136,10 +136,10 @@
     Mutex::Autolock l(sLock);
     sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
     if (player.get()) {
-        player->incStrong(thiz);
+        player->incStrong((void*)setMediaPlayer);
     }
     if (old != 0) {
-        old->decStrong(thiz);
+        old->decStrong((void*)setMediaPlayer);
     }
     env->SetIntField(thiz, fields.context, (int)player.get());
     return old;
@@ -252,7 +252,7 @@
 
     sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
     if (old_st != NULL) {
-        old_st->decStrong(thiz);
+        old_st->decStrong((void*)decVideoSurfaceRef);
     }
 }
 
@@ -279,7 +279,7 @@
                     "The surface does not have a binding SurfaceTexture!");
                 return;
             }
-            new_st->incStrong(thiz);
+            new_st->incStrong((void*)decVideoSurfaceRef);
         } else {
             jniThrowException(env, "java/lang/IllegalArgumentException",
                     "The surface has been released");
@@ -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 cd53c17..2c9508a 100644
--- a/opengl/java/android/opengl/EGL14.java
+++ b/opengl/java/android/opengl/EGL14.java
@@ -445,4 +445,12 @@
         int target
     );
 
+    // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
+
+    public static native boolean eglPresentationTimeANDROID(
+        EGLDisplay dpy,
+        EGLSurface sur,
+        long time
+    );
+
 }
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 3dcd232..57c87e4 100644
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -7,6 +7,8 @@
     <uses-permission android:name="android.permission.ASEC_DESTROY"/>
     <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <!-- Used to improve MeasureUtils performance on emulated storage -->
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
     <uses-permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" />
 
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 731a09c..de77cac 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,16 +16,12 @@
 
 package com.android.defcontainer;
 
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
-
 import android.app.IntentService;
 import android.content.Intent;
-import android.content.pm.MacAuthenticatedInputStream;
 import android.content.pm.ContainerEncryptionParams;
 import android.content.pm.IPackageManager;
 import android.content.pm.LimitedLengthInputStream;
+import android.content.pm.MacAuthenticatedInputStream;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
@@ -43,10 +39,16 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.PackageHelper;
+
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -228,9 +230,10 @@
         public long calculateDirectorySize(String path) throws RemoteException {
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 
-            final File directory = new File(path);
-            if (directory.exists() && directory.isDirectory()) {
-                return MeasurementUtils.measureDirectory(path);
+            final File dir = Environment.maybeTranslateEmulatedPathToInternal(new File(path));
+            if (dir.exists() && dir.isDirectory()) {
+                final String targetPath = dir.getAbsolutePath();
+                return MeasurementUtils.measureDirectory(targetPath);
             } else {
                 return 0L;
             }
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_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index e2d9d6e..b51b333 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -40,12 +40,14 @@
             android:layout_height="match_parent"
             android:fadingEdge="horizontal"
             android:scrollbars="none"
-            android:layout_gravity="end"
+            android:layout_gravity="right"
             android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length">
 
             <LinearLayout android:id="@+id/recents_linear_layout"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
+                android:layoutDirection="ltr"
+                android:layout_gravity="left"
                 android:orientation="horizontal">
             </LinearLayout>
 
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..b27536d8 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/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8e1773a..f824a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -156,6 +156,12 @@
 
     private void updateAlphaFromOffset(View animView, boolean dismissable) {
         if (FADE_OUT_DURING_SWIPE && dismissable) {
+            float alpha = getAlphaForOffset(animView);
+            if (alpha != 0f && alpha != 1f) {
+                animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            } else {
+                animView.setLayerType(View.LAYER_TYPE_NONE, null);
+            }
             animView.setAlpha(getAlphaForOffset(animView));
         }
         invalidateGlobalRegion(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index e79b2c6..9c2bca9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -83,6 +83,7 @@
     private boolean mAnimateIconOfFirstTask;
     private boolean mWaitingForWindowAnimation;
     private long mWindowAnimationStartTime;
+    private boolean mCallUiHiddenBeforeNextReload;
 
     private RecentTasksLoader mRecentTasksLoader;
     private ArrayList<TaskDescription> mRecentTaskDescriptions;
@@ -180,17 +181,18 @@
             }
             if (index == 0) {
                 if (mAnimateIconOfFirstTask) {
-                    if (mItemToAnimateInWhenWindowAnimationIsFinished != null) {
-                        holder.iconView.setAlpha(1f);
-                        holder.iconView.setTranslationX(0f);
-                        holder.iconView.setTranslationY(0f);
-                        holder.labelView.setAlpha(1f);
-                        holder.labelView.setTranslationX(0f);
-                        holder.labelView.setTranslationY(0f);
-                        if (holder.calloutLine != null) {
-                            holder.calloutLine.setAlpha(1f);
-                            holder.calloutLine.setTranslationX(0f);
-                            holder.calloutLine.setTranslationY(0f);
+                    ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
+                    if (oldHolder != null) {
+                        oldHolder.iconView.setAlpha(1f);
+                        oldHolder.iconView.setTranslationX(0f);
+                        oldHolder.iconView.setTranslationY(0f);
+                        oldHolder.labelView.setAlpha(1f);
+                        oldHolder.labelView.setTranslationX(0f);
+                        oldHolder.labelView.setTranslationY(0f);
+                        if (oldHolder.calloutLine != null) {
+                            oldHolder.calloutLine.setAlpha(1f);
+                            oldHolder.calloutLine.setTranslationX(0f);
+                            oldHolder.calloutLine.setTranslationY(0f);
                         }
                     }
                     mItemToAnimateInWhenWindowAnimationIsFinished = null;
@@ -198,17 +200,18 @@
                     final ViewTreeObserver observer = getViewTreeObserver();
                     final OnGlobalLayoutListener animateFirstIcon = new OnGlobalLayoutListener() {
                         public void onGlobalLayout() {
-                            if (mItemToAnimateInWhenWindowAnimationIsFinished != null) {
-                                holder.iconView.setAlpha(1f);
-                                holder.iconView.setTranslationX(0f);
-                                holder.iconView.setTranslationY(0f);
-                                holder.labelView.setAlpha(1f);
-                                holder.labelView.setTranslationX(0f);
-                                holder.labelView.setTranslationY(0f);
-                                if (holder.calloutLine != null) {
-                                    holder.calloutLine.setAlpha(1f);
-                                    holder.calloutLine.setTranslationX(0f);
-                                    holder.calloutLine.setTranslationY(0f);
+                            ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
+                            if (oldHolder != null) {
+                                oldHolder.iconView.setAlpha(1f);
+                                oldHolder.iconView.setTranslationX(0f);
+                                oldHolder.iconView.setTranslationY(0f);
+                                oldHolder.labelView.setAlpha(1f);
+                                oldHolder.labelView.setTranslationX(0f);
+                                oldHolder.labelView.setTranslationY(0f);
+                                if (oldHolder.calloutLine != null) {
+                                    oldHolder.calloutLine.setAlpha(1f);
+                                    oldHolder.calloutLine.setTranslationX(0f);
+                                    oldHolder.calloutLine.setTranslationY(0f);
                                 }
                             }
                             mItemToAnimateInWhenWindowAnimationIsFinished = holder;
@@ -325,8 +328,15 @@
 
     public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions,
             boolean firstScreenful, boolean animateIconOfFirstTask) {
-        mAnimateIconOfFirstTask = animateIconOfFirstTask;
-        mWaitingForWindowAnimation = animateIconOfFirstTask;
+        if (show && mCallUiHiddenBeforeNextReload) {
+            onUiHidden();
+            recentTaskDescriptions = null;
+            mAnimateIconOfFirstTask = false;
+            mWaitingForWindowAnimation = false;
+        } else {
+            mAnimateIconOfFirstTask = animateIconOfFirstTask;
+            mWaitingForWindowAnimation = animateIconOfFirstTask;
+        }
         if (show) {
             mWaitingToShow = true;
             refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
@@ -372,6 +382,7 @@
         } else {
             mWaitingToShow = false;
             // call onAnimationEnd() and clearRecentTasksList() in onUiHidden()
+            mCallUiHiddenBeforeNextReload = true;
             if (mPopup != null) {
                 mPopup.dismiss();
             }
@@ -379,6 +390,7 @@
     }
 
     public void onUiHidden() {
+        mCallUiHiddenBeforeNextReload = false;
         if (!mShowing && mRecentTaskDescriptions != null) {
             onAnimationEnd(null);
             clearRecentTasksList();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 4599dd4..7bdcf6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -559,6 +559,11 @@
                             + thumbBgPadding + thumbLeftMargin);
                     y = (int) (dm.heightPixels
                             - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
+                    if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) {
+                        x = dm.widthPixels - x - res
+                                .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
+                    }
+
                 } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                     float thumbTopMargin = res
                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
@@ -596,10 +601,6 @@
                     y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin
                             + recentsItemTopPadding + thumbBgPadding + statusBarHeight);
                 }
-                if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) {
-                    x = dm.widthPixels - x - res
-                            .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
-                }
 
                 ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(
                         getStatusBarView(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 988951c..e351429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -19,7 +19,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Iterator;
 
 import android.animation.ObjectAnimator;
@@ -30,7 +29,6 @@
 import android.util.AttributeSet;
 import android.util.Slog;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -39,6 +37,9 @@
 public class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
+
+    public static final boolean DEBUG_NAN = true; // http://b/7686690
+
     public final void LOG(String fmt, Object... args) {
         if (!DEBUG) return;
         Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -141,8 +142,17 @@
                 last = event;
                 i++;
             }
-            mVX /= totalweight;
-            mVY /= totalweight;
+            if (totalweight > 0) {
+                mVX /= totalweight;
+                mVY /= totalweight;
+            } else {
+                if (DEBUG_NAN) {
+                    Slog.v("FlingTracker", "computeCurrentVelocity warning: totalweight=0",
+                            new Throwable());
+                }
+                // so as not to contaminate the velocities with NaN
+                mVX = mVY = 0;
+            }
 
             if (FlingTracker.DEBUG) {
                 Slog.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY);
@@ -150,15 +160,19 @@
         }
         public float getXVelocity() {
             if (Float.isNaN(mVX)) {
-                Slog.v("FlingTracker", "warning: vx=NaN");
-                // XXX: should return 0
+                if (DEBUG_NAN) {
+                    Slog.v("FlingTracker", "warning: vx=NaN");
+                }
+                mVX = 0;
             }
             return mVX;
         }
         public float getYVelocity() {
             if (Float.isNaN(mVY)) {
-                Slog.v("FlingTracker", "warning: vx=NaN");
-                // XXX: should return 0
+                if (DEBUG_NAN) {
+                    Slog.v("FlingTracker", "warning: vx=NaN");
+                }
+                mVY = 0;
             }
             return mVY;
         }
@@ -531,8 +545,12 @@
 
     public void setExpandedHeightInternal(float h) {
         if (Float.isNaN(h)) {
-            Slog.v(TAG, "setExpandedHeightInternal: warning: h=NaN");
-            // XXX: should set h to 0
+            // If a NaN gets in here, it will freeze the Animators.
+            if (DEBUG_NAN) {
+                Slog.v(TAG, "setExpandedHeightInternal: warning: h=NaN, using 0 instead",
+                        new Throwable());
+            }
+            h = 0;
         }
 
         float fh = getFullHeight();
@@ -566,8 +584,12 @@
 
     public void setExpandedFraction(float frac) {
         if (Float.isNaN(frac)) {
-            Slog.v(TAG, "setExpandedFraction: frac=NaN");
-            // XXX: set frac to 0 to defend
+            // If a NaN gets in here, it will freeze the Animators.
+            if (DEBUG_NAN) {
+                Slog.v(TAG, "setExpandedFraction: frac=NaN, using 0 instead",
+                        new Throwable());
+            }
+            frac = 0;
         }
         setExpandedHeight(getFullHeight() * frac);
     }
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/EntropyMixer.java b/services/java/com/android/server/EntropyMixer.java
index 8c0d260..fbb66f9 100644
--- a/services/java/com/android/server/EntropyMixer.java
+++ b/services/java/com/android/server/EntropyMixer.java
@@ -23,6 +23,10 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -44,9 +48,6 @@
  * <p>This class was modeled after the script in
  * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man
  * 4 random</a>.
- *
- * <p>TODO: Investigate attempting to write entropy data at shutdown time
- * instead of periodically.
  */
 public class EntropyMixer extends Binder {
     private static final String TAG = "EntropyMixer";
@@ -73,12 +74,19 @@
         }
     };
 
-    public EntropyMixer() {
-        this(getSystemDir() + "/entropy.dat", "/dev/urandom");
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            writeEntropy();
+        }
+    };
+
+    public EntropyMixer(Context context) {
+        this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
     }
 
     /** Test only interface, not for public use */
-    public EntropyMixer(String entropyFile, String randomDevice) {
+    public EntropyMixer(Context context, String entropyFile, String randomDevice) {
         if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
         if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
 
@@ -88,6 +96,10 @@
         addDeviceSpecificEntropy();
         writeEntropy();
         scheduleEntropyWriter();
+        IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
+        broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
+        broadcastFilter.addAction(Intent.ACTION_REBOOT);
+        context.registerReceiver(mBroadcastReceiver, broadcastFilter);
     }
 
     private void scheduleEntropyWriter() {
@@ -107,6 +119,7 @@
 
     private void writeEntropy() {
         try {
+            Slog.i(TAG, "Writing entropy...");
             RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
         } catch (IOException e) {
             Slog.w(TAG, "Unable to write entropy", e);
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/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8ef247e..4631395 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -206,9 +206,6 @@
             installer = new Installer();
             installer.ping();
 
-            Slog.i(TAG, "Entropy Mixer");
-            ServiceManager.addService("entropy", new EntropyMixer());
-
             Slog.i(TAG, "Power Manager");
             power = new PowerManagerService();
             ServiceManager.addService(Context.POWER_SERVICE, power);
@@ -257,6 +254,9 @@
 
             ActivityManagerService.setSystemProcess();
 
+            Slog.i(TAG, "Entropy Mixer");
+            ServiceManager.addService("entropy", new EntropyMixer(context));
+
             Slog.i(TAG, "User Service");
             ServiceManager.addService(Context.USER_SERVICE,
                     UserManagerService.getInstance());
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 2a62c17..49295f5 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -21,7 +21,6 @@
 import android.accounts.AccountAndUser;
 import android.accounts.AccountAuthenticatorResponse;
 import android.accounts.AccountManager;
-import android.accounts.AccountManagerResponse;
 import android.accounts.AuthenticatorDescription;
 import android.accounts.GrantCredentialsPermissionActivity;
 import android.accounts.IAccountAuthenticator;
@@ -70,6 +69,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
@@ -103,7 +103,7 @@
 
     private static final int TIMEOUT_DELAY_MS = 1000 * 60;
     private static final String DATABASE_NAME = "accounts.db";
-    private static final int DATABASE_VERSION = 4;
+    private static final int DATABASE_VERSION = 5;
 
     private final Context mContext;
 
@@ -146,6 +146,8 @@
     private static final String META_KEY = "key";
     private static final String META_VALUE = "value";
 
+    private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
+
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
     private static final Intent ACCOUNTS_CHANGED_INTENT;
@@ -249,12 +251,18 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiver(new BroadcastReceiver() {
+        userFilter.addAction(Intent.ACTION_USER_STARTED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                onUserRemoved(intent);
+                String action = intent.getAction();
+                if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                    onUserRemoved(intent);
+                } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+                    onUserStarted(intent);
+                }
             }
-        }, userFilter);
+        }, UserHandle.ALL, userFilter, null, null);
     }
 
     public void systemReady() {
@@ -430,6 +438,21 @@
         }
     }
 
+    private void onUserStarted(Intent intent) {
+        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+        if (userId < 1) return;
+
+        // Check if there's a shared account that needs to be created as an account
+        Account[] sharedAccounts = getSharedAccountsAsUser(userId);
+        if (sharedAccounts == null || sharedAccounts.length == 0) return;
+        Account[] accounts = getAccountsAsUser(null, userId);
+        for (Account sa : sharedAccounts) {
+            if (ArrayUtils.contains(accounts, sa)) continue;
+            // Account doesn't exist. Copy it now.
+            copyAccountToUser(sa, UserHandle.USER_OWNER, userId);
+        }
+    }
+
     @Override
     public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
         Slog.d(TAG, "onServiceChanged() for userId " + userId);
@@ -535,14 +558,120 @@
         // fails if the account already exists
         long identityToken = clearCallingIdentity();
         try {
-            return addAccountInternal(accounts, account, password, extras);
+            return addAccountInternal(accounts, account, password, extras, false);
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
+    private boolean copyAccountToUser(final Account account, int userFrom, int userTo) {
+        final UserAccounts fromAccounts = getUserAccounts(userFrom);
+        final UserAccounts toAccounts = getUserAccounts(userTo);
+        if (fromAccounts == null || toAccounts == null) {
+            return false;
+        }
+
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(fromAccounts, null, account.type, false,
+                    false /* stripAuthTokenFromResult */) {
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
+                            + ", " + account.type;
+                }
+
+                public void run() throws RemoteException {
+                    mAuthenticator.getAccountCredentialsForCloning(this, account);
+                }
+
+                public void onResult(Bundle result) {
+                    if (result != null) {
+                        if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
+                            // Create a Session for the target user and pass in the bundle
+                            Slog.i(TAG, "getAccountCredentialsForCloning returned success, "
+                                    + "sending result to target user");
+                            completeCloningAccount(result, account, toAccounts);
+                        } else {
+                            Slog.e(TAG, "getAccountCredentialsForCloning returned failure");
+                            clonePassword(fromAccounts, toAccounts, account);
+                        }
+                        return;
+                    } else {
+                        Slog.e(TAG, "getAccountCredentialsForCloning returned null");
+                        clonePassword(fromAccounts, toAccounts, account);
+                        super.onResult(result);
+                    }
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+        return true;
+    }
+
+    // TODO: Remove fallback - move to authenticator
+    private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts,
+            Account account) {
+        long id = clearCallingIdentity();
+        try {
+            String password = readPasswordInternal(fromAccounts, account);
+            String extraFlags = readUserDataInternal(fromAccounts, account, "flags");
+            String extraServices = readUserDataInternal(fromAccounts, account, "services");
+            Bundle extras = new Bundle();
+            extras.putString("flags", extraFlags);
+            extras.putString("services", extraServices);
+            addAccountInternal(toAccounts, account, password, extras, true);
+        } finally {
+            restoreCallingIdentity(id);
+        }
+    }
+
+    void completeCloningAccount(final Bundle result, final Account account,
+            final UserAccounts targetUser) {
+        long id = clearCallingIdentity();
+        try {
+            new Session(targetUser, null, account.type, false,
+                    false /* stripAuthTokenFromResult */) {
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
+                            + ", " + account.type;
+                }
+
+                public void run() throws RemoteException {
+                    mAuthenticator.addAccountFromCredentials(this, account, result);
+                }
+
+                public void onResult(Bundle result) {
+                    if (result != null) {
+                        if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
+                            // TODO: Anything?
+                            Slog.i(TAG, "addAccount returned success");
+                        } else {
+                            // TODO: Show error notification
+                            // TODO: Should we remove the shadow account to avoid retries?
+                            Slog.e(TAG, "addAccountFromCredentials returned failure");
+                        }
+                        return;
+                    } else {
+                        Slog.e(TAG, "addAccountFromCredentials returned null");
+                        super.onResult(result);
+                    }
+                }
+
+                public void onError(int errorCode, String errorMessage) {
+                    super.onError(errorCode,  errorMessage);
+                    // TODO: Show error notification to user
+                    // TODO: Should we remove the shadow account so that it doesn't keep trying?
+                }
+
+            }.bind();
+        } finally {
+            restoreCallingIdentity(id);
+        }
+    }
+
     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
-            Bundle extras) {
+            Bundle extras, boolean restricted) {
         if (account == null) {
             return false;
         }
@@ -768,6 +897,21 @@
             removeAccountFromCacheLocked(accounts, account);
             sendAccountsChangedBroadcast(accounts.userId);
         }
+        if (accounts.userId == UserHandle.USER_OWNER) {
+            // Owner's account was removed, remove from any users that are sharing
+            // this account.
+            long id = Binder.clearCallingIdentity();
+            try {
+                List<UserInfo> users = mUserManager.getUsers(true);
+                for (UserInfo user : users) {
+                    if (!user.isPrimary() && user.isRestricted()) {
+                        removeSharedAccountAsUser(account, user.id);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(id);
+            }
+        }
     }
 
     public void invalidateAuthToken(String accountType, String authToken) {
@@ -1606,6 +1750,65 @@
     }
 
     @Override
+    public boolean addSharedAccountAsUser(Account account, int userId) {
+        userId = handleIncomingUser(userId);
+        SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                new String[] {account.name, account.type});
+        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
+        if (accountId < 0) {
+            Log.w(TAG, "insertAccountIntoDatabase: " + account
+                    + ", skipping the DB insert failed");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeSharedAccountAsUser(Account account, int userId) {
+        userId = handleIncomingUser(userId);
+        UserAccounts accounts = getUserAccounts(userId);
+        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+                new String[] {account.name, account.type});
+        if (r > 0) {
+            removeAccountInternal(accounts, account);
+        }
+        return r > 0;
+    }
+
+    @Override
+    public Account[] getSharedAccountsAsUser(int userId) {
+        userId = handleIncomingUser(userId);
+        UserAccounts accounts = getUserAccounts(userId);
+        ArrayList<Account> accountList = new ArrayList<Account>();
+        Cursor cursor = null;
+        try {
+            cursor = accounts.openHelper.getReadableDatabase()
+                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
+                    null, null, null, null, null);
+            if (cursor != null && cursor.moveToFirst()) {
+                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
+                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
+                do {
+                    accountList.add(new Account(cursor.getString(nameIndex),
+                            cursor.getString(typeIndex)));
+                } while (cursor.moveToNext());
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        Account[] accountArray = new Account[accountList.size()];
+        accountList.toArray(accountArray);
+        return accountArray;
+    }
+
+    @Override
     public Account[] getAccounts(String type) {
         return getAccountsAsUser(type, UserHandle.getCallingUserId());
     }
@@ -1679,7 +1882,6 @@
         private int mNumRequestContinued = 0;
         private int mNumErrors = 0;
 
-
         IAccountAuthenticator mAuthenticator = null;
 
         private final boolean mStripAuthTokenFromResult;
@@ -1688,7 +1890,7 @@
         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
             super();
-            if (response == null) throw new IllegalArgumentException("response is null");
+            //if (response == null) throw new IllegalArgumentException("response is null");
             if (accountType == null) throw new IllegalArgumentException("accountType is null");
             mAccounts = accounts;
             mStripAuthTokenFromResult = stripAuthTokenFromResult;
@@ -1699,11 +1901,13 @@
             synchronized (mSessions) {
                 mSessions.put(toString(), this);
             }
-            try {
-                response.asBinder().linkToDeath(this, 0 /* flags */);
-            } catch (RemoteException e) {
-                mResponse = null;
-                binderDied();
+            if (response != null) {
+                try {
+                    response.asBinder().linkToDeath(this, 0 /* flags */);
+                } catch (RemoteException e) {
+                    mResponse = null;
+                    binderDied();
+                }
             }
         }
 
@@ -2011,9 +2215,19 @@
                     + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
                     + META_VALUE + " TEXT)");
 
+            createSharedAccountsTable(db);
+
             createAccountsDeletionTrigger(db);
         }
 
+        private void createSharedAccountsTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
+                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
+                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+        }
+
         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
             db.execSQL(""
                     + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
@@ -2058,6 +2272,15 @@
                         " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
                 oldVersion++;
             }
+
+            if (oldVersion == 4) {
+                createSharedAccountsTable(db);
+                oldVersion++;
+            }
+
+            if (oldVersion != newVersion) {
+                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+            }
         }
 
         @Override
@@ -2216,6 +2439,16 @@
         throw new SecurityException(msg);
     }
 
+    private int handleIncomingUser(int userId) {
+        try {
+            return ActivityManagerNative.getDefault().handleIncomingUser(
+                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
+        } catch (RemoteException re) {
+            // Shouldn't happen, local.
+        }
+        return userId;
+    }
+
     private boolean inSystemImage(int callingUid) {
         final int callingUserId = UserHandle.getUserId(callingUid);
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a766bad..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(
@@ -7419,29 +7426,63 @@
         SystemProperties.set("ctl.start", "bugreport");
     }
 
+    public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
+        return r != null ? getInputDispatchingTimeoutLocked(r.app) : KEY_DISPATCHING_TIMEOUT;
+    }
+
+    public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
+        if (r != null && (r.instrumentationClass != null || r.usingWrapper)) {
+            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
+        }
+        return KEY_DISPATCHING_TIMEOUT;
+    }
+
+
     public long inputDispatchingTimedOut(int pid, final boolean aboveSystem) {
         if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires permission "
                     + android.Manifest.permission.FILTER_EVENTS);
         }
-
         ProcessRecord proc;
-
-        // TODO: Unify this code with ActivityRecord.keyDispatchingTimedOut().
+        long timeout;
         synchronized (this) {
             synchronized (mPidsSelfLocked) {
                 proc = mPidsSelfLocked.get(pid);
             }
-            if (proc != null) {
+            timeout = getInputDispatchingTimeoutLocked(proc);
+        }
+
+        if (!inputDispatchingTimedOut(proc, null, null, aboveSystem)) {
+            return -1;
+        }
+
+        return timeout;
+    }
+
+    /**
+     * Handle input dispatching timeouts.
+     * Returns whether input dispatching should be aborted or not.
+     */
+    public boolean inputDispatchingTimedOut(final ProcessRecord proc,
+            final ActivityRecord activity, final ActivityRecord parent,
+            final boolean aboveSystem) {
+        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.FILTER_EVENTS);
+        }
+
+        if (proc != null) {
+            synchronized (this) {
                 if (proc.debugging) {
-                    return -1;
+                    return false;
                 }
 
                 if (mDidDexOpt) {
                     // Give more time since we were dexopting.
                     mDidDexOpt = false;
-                    return -1;
+                    return false;
                 }
 
                 if (proc.instrumentationClass != null) {
@@ -7449,25 +7490,18 @@
                     info.putString("shortMsg", "keyDispatchingTimedOut");
                     info.putString("longMsg", "Timed out while dispatching key event");
                     finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
-                    proc = null;
+                    return true;
                 }
             }
-        }
-
-        if (proc != null) {
-            final ProcessRecord pr = proc;
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    appNotResponding(pr, null, null, aboveSystem, "keyDispatchingTimedOut");
+                    appNotResponding(proc, activity, parent, aboveSystem, "keyDispatchingTimedOut");
                 }
             });
-            if (proc.instrumentationClass != null || proc.usingWrapper) {
-                return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
-            }
         }
 
-        return KEY_DISPATCHING_TIMEOUT;
+        return true;
     }
 
     public Bundle getTopActivityExtras(int requestType) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index cde17c9..054d213 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -867,51 +867,20 @@
     }
 
     public boolean keyDispatchingTimedOut() {
-        // TODO: Unify this code with ActivityManagerService.inputDispatchingTimedOut().
         ActivityRecord r;
-        ProcessRecord anrApp = null;
+        ProcessRecord anrApp;
         synchronized(service) {
             r = getWaitingHistoryRecordLocked();
-            if (r != null && r.app != null) {
-                if (r.app.debugging) {
-                    return false;
-                }
-                
-                if (service.mDidDexOpt) {
-                    // Give more time since we were dexopting.
-                    service.mDidDexOpt = false;
-                    return false;
-                }
-                
-                if (r.app.instrumentationClass == null) { 
-                    anrApp = r.app;
-                } else {
-                    Bundle info = new Bundle();
-                    info.putString("shortMsg", "keyDispatchingTimedOut");
-                    info.putString("longMsg", "Timed out while dispatching key event");
-                    service.finishInstrumentationLocked(
-                            r.app, Activity.RESULT_CANCELED, info);
-                }
-            }
+            anrApp = r != null ? r.app : null;
         }
-        
-        if (anrApp != null) {
-            service.appNotResponding(anrApp, r, this, false, "keyDispatchingTimedOut");
-        }
-        
-        return true;
+        return service.inputDispatchingTimedOut(anrApp, r, this, false);
     }
     
     /** Returns the key dispatching timeout for this application token. */
     public long getKeyDispatchingTimeout() {
         synchronized(service) {
             ActivityRecord r = getWaitingHistoryRecordLocked();
-            if (r != null && r.app != null
-                    && (r.app.instrumentationClass != null || r.app.usingWrapper)) {
-                return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
-            }
-
-            return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
+            return ActivityManagerService.getInputDispatchingTimeoutLocked(r);
         }
     }
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7fb8902..2d12a77 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -170,7 +170,7 @@
 public class PackageManagerService extends IPackageManager.Stub {
     static final String TAG = "PackageManager";
     static final boolean DEBUG_SETTINGS = false;
-    static final boolean DEBUG_PREFERRED = true;
+    static final boolean DEBUG_PREFERRED = false;
     static final boolean DEBUG_UPGRADE = false;
     private static final boolean DEBUG_INSTALL = false;
     private static final boolean DEBUG_REMOVE = false;
@@ -339,9 +339,20 @@
     final SparseArray<HashSet<String>> mSystemPermissions =
             new SparseArray<HashSet<String>>();
 
+    static final class SharedLibraryEntry {
+        final String path;
+        final String apk;
+
+        SharedLibraryEntry(String _path, String _apk) {
+            path = _path;
+            apk = _apk;
+        }
+    }
+
     // These are the built-in shared libraries that were read from the
     // etc/permissions.xml file.
-    final HashMap<String, String> mSharedLibraries = new HashMap<String, String>();
+    final HashMap<String, SharedLibraryEntry> mSharedLibraries
+            = new HashMap<String, SharedLibraryEntry>();
 
     // Temporary for building the final shared libraries for an .apk.
     String[] mTmpSharedLibraries = null;
@@ -390,8 +401,7 @@
     final SparseArray<PackageVerificationState> mPendingVerification
             = new SparseArray<PackageVerificationState>();
 
-    final ArrayList<PackageParser.Package> mDeferredDexOpt =
-            new ArrayList<PackageParser.Package>();
+    HashSet<PackageParser.Package> mDeferredDexOpt = null;
 
     /** Token for keys in mPendingVerification. */
     private int mPendingVerificationToken = 0;
@@ -514,10 +524,9 @@
         void doHandleMessage(Message msg) {
             switch (msg.what) {
                 case INIT_COPY: {
-                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");
                     HandlerParams params = (HandlerParams) msg.obj;
                     int idx = mPendingInstalls.size();
-                    if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);
+                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                     // If a bind was already initiated we dont really
                     // need to do anything. The pending install
                     // will be processed later on.
@@ -1071,9 +1080,12 @@
              * Also ensure all external libraries have had dexopt run on them.
              */
             if (mSharedLibraries.size() > 0) {
-                Iterator<String> libs = mSharedLibraries.values().iterator();
+                Iterator<SharedLibraryEntry> libs = mSharedLibraries.values().iterator();
                 while (libs.hasNext()) {
-                    String lib = libs.next();
+                    String lib = libs.next().path;
+                    if (lib == null) {
+                        continue;
+                    }
                     try {
                         if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
                             libFiles.add(lib);
@@ -1277,6 +1289,10 @@
                 mDrmAppInstallObserver = null;
             }
 
+            // Now that we know all of the shared libraries, update all clients to have
+            // the correct library paths.
+            updateAllSharedLibrariesLPw();
+
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
             Slog.i(TAG, "Time to scan packages: "
@@ -1517,7 +1533,7 @@
                                 + parser.getPositionDescription());
                     } else {
                         //Log.i(TAG, "Got library " + lname + " in " + lfile);
-                        mSharedLibraries.put(lname, lfile);
+                        mSharedLibraries.put(lname, new SharedLibraryEntry(lfile, null));
                     }
                     XmlUtils.skipCurrentTag(parser);
                     continue;
@@ -1614,16 +1630,9 @@
         }
         final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
         final PackageUserState state = ps.readUserState(userId);
-        pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+        return PackageParser.generatePackageInfo(p, gp.gids, flags,
                 ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
                 state, userId);
-        if (pi != null) {
-            pi.applicationInfo.enabledSetting = state.enabled;
-            pi.applicationInfo.enabled =
-                    pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
-                    || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
-        }
-        return pi;
     }
 
     @Override
@@ -3256,6 +3265,7 @@
             int parseFlags, int scanMode, long currentTime, UserHandle user) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         String scanPath = scanFile.getPath();
+        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser(scanPath);
         pp.setSeparateProcesses(mSeparateProcesses);
@@ -3285,6 +3295,7 @@
             // package.  Must look for it either under the original or real
             // package name depending on our state.
             updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
+            if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
         }
         // First check if this is a system package that may involve an update
         if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
@@ -3292,6 +3303,7 @@
                 // The path has changed from what was last scanned...  check the
                 // version of the new path against what we have stored to determine
                 // what to do.
+                if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
                 if (pkg.mVersionCode < ps.versionCode) {
                     // The system package has been updated and the code path does not match
                     // Ignore entry. Skip it.
@@ -3305,6 +3317,7 @@
                         updatedPkg.codePath = scanFile;
                         updatedPkg.codePathString = scanFile.toString();                        
                     }
+                    updatedPkg.pkg = pkg;
                     mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                     return null;
                 } else {
@@ -3360,6 +3373,7 @@
              */
             if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Signature mismatch!");
                 deletePackageLI(pkg.packageName, null, true, 0, null, false);
                 ps = null;
             } else {
@@ -3491,28 +3505,28 @@
     }
 
     public void performBootDexOpt() {
-        ArrayList<PackageParser.Package> pkgs = null;
+        HashSet<PackageParser.Package> pkgs = null;
         synchronized (mPackages) {
-            if (mDeferredDexOpt.size() > 0) {
-                pkgs = new ArrayList<PackageParser.Package>(mDeferredDexOpt);
-                mDeferredDexOpt.clear();
-            }
+            pkgs = mDeferredDexOpt;
+            mDeferredDexOpt = null;
         }
         if (pkgs != null) {
-            for (int i=0; i<pkgs.size(); i++) {
+            int i = 0;
+            for (PackageParser.Package pkg : pkgs) {
                 if (!isFirstBoot()) {
+                    i++;
                     try {
                         ActivityManagerNative.getDefault().showBootMessage(
                                 mContext.getResources().getString(
                                         com.android.internal.R.string.android_upgrading_apk,
-                                        i+1, pkgs.size()), true);
+                                        i, pkgs.size()), true);
                     } catch (RemoteException e) {
                     }
                 }
-                PackageParser.Package p = pkgs.get(i);
+                PackageParser.Package p = pkg;
                 synchronized (mInstallLock) {
                     if (!p.mDidDexOpt) {
-                        performDexOptLI(p, false, false);
+                        performDexOptLI(p, false, false, true);
                     }
                 }
             }
@@ -3534,7 +3548,27 @@
             }
         }
         synchronized (mInstallLock) {
-            return performDexOptLI(p, false, false) == DEX_OPT_PERFORMED;
+            return performDexOptLI(p, false, false, true) == DEX_OPT_PERFORMED;
+        }
+    }
+
+    private void performDexOptLibsLI(ArrayList<String> libs, boolean forceDex, boolean defer,
+            HashSet<String> done) {
+        for (int i=0; i<libs.size(); i++) {
+            PackageParser.Package libPkg;
+            String libName;
+            synchronized (mPackages) {
+                libName = libs.get(i);
+                SharedLibraryEntry lib = mSharedLibraries.get(libName);
+                if (lib != null && lib.apk != null) {
+                    libPkg = mPackages.get(lib.apk);
+                } else {
+                    libPkg = null;
+                }
+            }
+            if (libPkg != null && !done.contains(libName)) {
+                performDexOptLI(libPkg, forceDex, defer, done);
+            }
         }
     }
 
@@ -3543,14 +3577,27 @@
     static final int DEX_OPT_DEFERRED = 2;
     static final int DEX_OPT_FAILED = -1;
 
-    private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer) {
+    private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
+            HashSet<String> done) {
         boolean performed = false;
+        if (done != null) {
+            done.add(pkg.packageName);
+            if (pkg.usesLibraries != null) {
+                performDexOptLibsLI(pkg.usesLibraries, forceDex, defer, done);
+            }
+            if (pkg.usesOptionalLibraries != null) {
+                performDexOptLibsLI(pkg.usesOptionalLibraries, forceDex, defer, done);
+            }
+        }
         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
             String path = pkg.mScanPath;
             int ret = 0;
             try {
                 if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
                     if (!forceDex && defer) {
+                        if (mDeferredDexOpt == null) {
+                            mDeferredDexOpt = new HashSet<PackageParser.Package>();
+                        }
                         mDeferredDexOpt.add(pkg);
                         return DEX_OPT_DEFERRED;
                     } else {
@@ -3583,6 +3630,19 @@
         return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
     }
 
+    private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
+            boolean inclDependencies) {
+        HashSet<String> done;
+        boolean performed = false;
+        if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
+            done = new HashSet<String>();
+            done.add(pkg.packageName);
+        } else {
+            done = null;
+        }
+        return performDexOptLI(pkg, forceDex, defer, done);
+    }
+
     private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
         if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             Slog.w(TAG, "Unable to update from " + oldPkg.name
@@ -3653,6 +3713,113 @@
         return res;
     }
 
+    private int addSharedLibraryLPw(final SharedLibraryEntry file, int num,
+            PackageParser.Package changingLib) {
+        if (file.path != null) {
+            mTmpSharedLibraries[num] = file.path;
+            return num+1;
+        }
+        PackageParser.Package p = mPackages.get(file.apk);
+        if (changingLib != null && changingLib.packageName.equals(file.apk)) {
+            // If we are doing this while in the middle of updating a library apk,
+            // then we need to make sure to use that new apk for determining the
+            // dependencies here.  (We haven't yet finished committing the new apk
+            // to the package manager state.)
+            if (p == null || p.packageName.equals(changingLib.packageName)) {
+                p = changingLib;
+            }
+        }
+        if (p != null) {
+            String path = p.mPath;
+            for (int i=0; i<num; i++) {
+                if (mTmpSharedLibraries[i].equals(path)) {
+                    return num;
+                }
+            }
+            mTmpSharedLibraries[num] = p.mPath;
+            return num+1;
+        }
+        return num;
+    }
+
+    private boolean updateSharedLibrariesLPw(PackageParser.Package pkg,
+            PackageParser.Package changingLib) {
+        if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
+            if (mTmpSharedLibraries == null ||
+                    mTmpSharedLibraries.length < mSharedLibraries.size()) {
+                mTmpSharedLibraries = new String[mSharedLibraries.size()];
+            }
+            int num = 0;
+            int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
+            for (int i=0; i<N; i++) {
+                final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
+                if (file == null) {
+                    Slog.e(TAG, "Package " + pkg.packageName
+                            + " requires unavailable shared library "
+                            + pkg.usesLibraries.get(i) + "; failing!");
+                    mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+                    return false;
+                }
+                num = addSharedLibraryLPw(file, num, changingLib);
+            }
+            N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
+            for (int i=0; i<N; i++) {
+                final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+                if (file == null) {
+                    Slog.w(TAG, "Package " + pkg.packageName
+                            + " desires unavailable shared library "
+                            + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
+                } else {
+                    num = addSharedLibraryLPw(file, num, changingLib);
+                }
+            }
+            if (num > 0) {
+                pkg.usesLibraryFiles = new String[num];
+                System.arraycopy(mTmpSharedLibraries, 0,
+                        pkg.usesLibraryFiles, 0, num);
+            } else {
+                pkg.usesLibraryFiles = null;
+            }
+        }
+        return true;
+    }
+
+    private static boolean hasString(List<String> list, List<String> which) {
+        if (list == null) {
+            return false;
+        }
+        for (int i=list.size()-1; i>=0; i--) {
+            for (int j=which.size()-1; j>=0; j--) {
+                if (which.get(j).equals(list.get(i))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void updateAllSharedLibrariesLPw() {
+        for (PackageParser.Package pkg : mPackages.values()) {
+            updateSharedLibrariesLPw(pkg, null);
+        }
+    }
+
+    private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(
+            PackageParser.Package changingPkg) {
+        ArrayList<PackageParser.Package> res = null;
+        for (PackageParser.Package pkg : mPackages.values()) {
+            if (hasString(pkg.usesLibraries, changingPkg.libraryNames)
+                    || hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)) {
+                if (res == null) {
+                    res = new ArrayList<PackageParser.Package>();
+                }
+                res.add(pkg);
+                updateSharedLibrariesLPw(pkg, changingPkg);
+            }
+        }
+        return res;
+    }
+
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
             int parseFlags, int scanMode, long currentTime, UserHandle user) {
         File scanFile = new File(pkg.mScanPath);
@@ -3732,42 +3899,14 @@
 
         // writer
         synchronized (mPackages) {
-            // Check all shared libraries and map to their actual file path.
-            if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
-                if (mTmpSharedLibraries == null ||
-                        mTmpSharedLibraries.length < mSharedLibraries.size()) {
-                    mTmpSharedLibraries = new String[mSharedLibraries.size()];
-                }
-                int num = 0;
-                int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
-                for (int i=0; i<N; i++) {
-                    final String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
-                    if (file == null) {
-                        Slog.e(TAG, "Package " + pkg.packageName
-                                + " requires unavailable shared library "
-                                + pkg.usesLibraries.get(i) + "; failing!");
-                        mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-                        return null;
-                    }
-                    mTmpSharedLibraries[num] = file;
-                    num++;
-                }
-                N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
-                for (int i=0; i<N; i++) {
-                    final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
-                    if (file == null) {
-                        Slog.w(TAG, "Package " + pkg.packageName
-                                + " desires unavailable shared library "
-                                + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
-                    } else {
-                        mTmpSharedLibraries[num] = file;
-                        num++;
-                    }
-                }
-                if (num > 0) {
-                    pkg.usesLibraryFiles = new String[num];
-                    System.arraycopy(mTmpSharedLibraries, 0,
-                            pkg.usesLibraryFiles, 0, num);
+            if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                // Check all shared libraries and map to their actual file path.
+                // We only do this here for apps not on a system dir, because those
+                // are the only ones that can fail an install due to this.  We
+                // will take care of the system apps by updating all of their
+                // library paths after the scan is done.
+                if (!updateSharedLibrariesLPw(pkg, null)) {
+                    return null;
                 }
             }
 
@@ -4173,7 +4312,7 @@
         pkg.mScanPath = path;
 
         if ((scanMode&SCAN_NO_DEX) == 0) {
-            if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)
+            if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
                     == DEX_OPT_FAILED) {
                 mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
                 return null;
@@ -4185,6 +4324,80 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
         }
 
+        ArrayList<PackageParser.Package> clientLibPkgs = null;
+
+        // writer
+        synchronized (mPackages) {
+            if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+                // Only system apps can add new shared libraries.
+                if (pkg.libraryNames != null) {
+                    for (int i=0; i<pkg.libraryNames.size(); i++) {
+                        String name = pkg.libraryNames.get(i);
+                        boolean allowed = false;
+                        if (isUpdatedSystemApp(pkg)) {
+                            // New library entries can only be added through the
+                            // system image.  This is important to get rid of a lot
+                            // of nasty edge cases: for example if we allowed a non-
+                            // system update of the app to add a library, then uninstalling
+                            // the update would make the library go away, and assumptions
+                            // we made such as through app install filtering would now
+                            // have allowed apps on the device which aren't compatible
+                            // with it.  Better to just have the restriction here, be
+                            // conservative, and create many fewer cases that can negatively
+                            // impact the user experience.
+                            final PackageSetting sysPs = mSettings
+                                    .getDisabledSystemPkgLPr(pkg.packageName);
+                            if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
+                                for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {
+                                    if (name.equals(sysPs.pkg.libraryNames.get(j))) {
+                                        allowed = true;
+                                        allowed = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        } else {
+                            allowed = true;
+                        }
+                        if (allowed) {
+                            if (!mSharedLibraries.containsKey(name)) {
+                                mSharedLibraries.put(name, new SharedLibraryEntry(null,
+                                        pkg.packageName));
+                            } else if (!name.equals(pkg.packageName)) {
+                                Slog.w(TAG, "Package " + pkg.packageName + " library "
+                                        + name + " already exists; skipping");
+                            }
+                        } else {
+                            Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
+                                    + name + " that is not declared on system image; skipping");
+                        }
+                    }
+                    if ((scanMode&SCAN_BOOTING) == 0) {
+                        // If we are not booting, we need to update any applications
+                        // that are clients of our shared library.  If we are booting,
+                        // this will all be done once the scan is complete.
+                        clientLibPkgs = updateAllSharedLibrariesLPw(pkg);
+                    }
+                }
+            }
+        }
+
+        // We also need to dexopt any apps that are dependent on this library.  Note that
+        // if these fail, we should abort the install since installing the library will
+        // result in some apps being broken.
+        if (clientLibPkgs != null) {
+            if ((scanMode&SCAN_NO_DEX) == 0) {
+                for (int i=0; i<clientLibPkgs.size(); i++) {
+                    PackageParser.Package clientPkg = clientLibPkgs.get(i);
+                    if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
+                            == DEX_OPT_FAILED) {
+                        mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
+                        return null;
+                    }
+                }
+            }
+        }
+
         // Request the ActivityManager to kill the process(only for existing packages)
         // so that we do not end up in a confused state while the user is still using the older
         // version of the application while the new one gets installed.
@@ -4193,6 +4406,15 @@
                         pkg.applicationInfo.uid);
         }
 
+        // Also need to kill any apps that are dependent on the library.
+        if (clientLibPkgs != null) {
+            for (int i=0; i<clientLibPkgs.size(); i++) {
+                PackageParser.Package clientPkg = clientLibPkgs.get(i);
+                killApplication(clientPkg.applicationInfo.packageName,
+                        clientPkg.applicationInfo.uid);
+            }
+        }
+
         // writer
         synchronized (mPackages) {
             // We don't expect installation to fail beyond this point,
@@ -4598,7 +4820,7 @@
                     }
                 }
             }
-            if (chatty) {
+            if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
                 } else {
@@ -4634,7 +4856,7 @@
         for (i=0; i<N; i++) {
             PackageParser.Activity a = pkg.receivers.get(i);
             mReceivers.removeActivity(a, "receiver");
-            if (chatty) {
+            if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
                 } else {
@@ -4652,7 +4874,7 @@
         for (i=0; i<N; i++) {
             PackageParser.Activity a = pkg.activities.get(i);
             mActivities.removeActivity(a, "activity");
-            if (chatty) {
+            if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
                 } else {
@@ -4675,7 +4897,7 @@
             }
             if (bp != null && bp.perm == p) {
                 bp.perm = null;
-                if (chatty) {
+                if (DEBUG_REMOVE && chatty) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
@@ -4694,7 +4916,7 @@
         for (i=0; i<N; i++) {
             PackageParser.Instrumentation a = pkg.instrumentation.get(i);
             mInstrumentation.remove(a.getComponentName());
-            if (chatty) {
+            if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
                 } else {
@@ -4706,6 +4928,31 @@
         if (r != null) {
             if (DEBUG_REMOVE) Log.d(TAG, "  Instrumentation: " + r);
         }
+
+        r = null;
+        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+            // Only system apps can hold shared libraries.
+            if (pkg.libraryNames != null) {
+                for (i=0; i<pkg.libraryNames.size(); i++) {
+                    String name = pkg.libraryNames.get(i);
+                    SharedLibraryEntry cur = mSharedLibraries.get(name);
+                    if (cur != null && cur.apk != null && cur.apk.equals(pkg.packageName)) {
+                        mSharedLibraries.remove(name);
+                        if (DEBUG_REMOVE && chatty) {
+                            if (r == null) {
+                                r = new StringBuilder(256);
+                            } else {
+                                r.append(' ');
+                            }
+                            r.append(name);
+                        }
+                    }
+                }
+            }
+        }
+        if (r != null) {
+            if (DEBUG_REMOVE) Log.d(TAG, "  Libraries: " + r);
+        }
     }
 
     private static final boolean isPackageFilename(String name) {
@@ -4867,7 +5114,23 @@
                                 if (origGp.grantedPermissions.contains(perm)) {
                                     allowed = true;
                                 } else {
+                                    // The system apk may have been updated with an older
+                                    // version of the one on the data partition, but which
+                                    // granted a new system permission that it didn't have
+                                    // before.  In this case we do want to allow the app to
+                                    // now get the new permission, because it is allowed by
+                                    // the system image.
                                     allowed = false;
+                                    if (sysPs.pkg != null) {
+                                        for (int j=0;
+                                                j<sysPs.pkg.requestedPermissions.size(); j++) {
+                                            if (perm.equals(
+                                                    sysPs.pkg.requestedPermissions.get(j))) {
+                                                allowed = true;
+                                                break;
+                                            }
+                                        }
+                                    }
                                 }
                             } else {
                                 allowed = true;
@@ -5564,6 +5827,7 @@
                 }
                 if ((event&REMOVE_EVENTS) != 0) {
                     if (ps != null) {
+                        if (DEBUG_REMOVE) Slog.d(TAG, "Package disappeared: " + ps);
                         removePackageLI(ps, true);
                         removedPackage = ps.name;
                         removedAppId = ps.appId;
@@ -5572,6 +5836,7 @@
 
                 if ((event&ADD_EVENTS) != 0) {
                     if (p == null) {
+                        if (DEBUG_INSTALL) Slog.d(TAG, "New file appeared: " + fullPath);
                         p = scanPackageLI(fullPath,
                                 (mIsRom ? PackageParser.PARSE_IS_SYSTEM
                                         | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
@@ -5652,7 +5917,7 @@
                 null);
 
         final int uid = Binder.getCallingUid();
-        if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+        if (!isUserAllowed(UserHandle.getUserId(uid), UserManager.ALLOW_INSTALL_APPS)) {
             try {
                 observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -5690,13 +5955,17 @@
      * @hide
      */
     @Override
-    public int installExistingPackage(String packageName) {
+    public int installExistingPackageAsUser(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
                 null);
         PackageSetting pkgSetting;
         final int uid = Binder.getCallingUid();
-        final int userId = UserHandle.getUserId(uid);
-        if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+        if (UserHandle.getUserId(uid) != userId) {
+            mContext.enforceCallingPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "installExistingPackage for user " + userId);
+        }
+        if (!isUserAllowed(userId, UserManager.ALLOW_INSTALL_APPS)) {
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
 
@@ -5730,14 +5999,11 @@
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
-    private boolean isUserAllowed(int callingUid, String restrictionKey) {
-        if (callingUid != android.os.Process.myUid()) {
-            Bundle restrictions = sUserManager.getUserRestrictions(
-                    UserHandle.getUserId(callingUid));
-            if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
-                Log.w(TAG, "User does not have permission to: " + restrictionKey);
-                return false;
-            }
+    private boolean isUserAllowed(int userId, String restrictionKey) {
+        Bundle restrictions = sUserManager.getUserRestrictions(userId);
+        if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
+            Log.w(TAG, "User does not have permission to: " + restrictionKey);
+            return false;
         }
         return true;
     }
@@ -6132,7 +6398,7 @@
         final boolean startCopy() {
             boolean res;
             try {
-                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");
+                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
 
                 if (++mRetries > MAX_RETRIES) {
                     Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
@@ -6176,6 +6442,13 @@
         }
 
         @Override
+        public String toString() {
+            return "MeasureParams{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + mStats.packageName + "}";
+        }
+
+        @Override
         void handleStartCopy() throws RemoteException {
             synchronized (mInstallLock) {
                 mSuccess = getPackageSizeInfoLI(mStats.packageName, mStats.userHandle, mStats);
@@ -6264,6 +6537,13 @@
             this.encryptionParams = encryptionParams;
         }
 
+        @Override
+        public String toString() {
+            return "InstallParams{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + mPackageURI + "}";
+        }
+
         public ManifestDigest getManifestDigest() {
             if (verificationParams == null) {
                 return null;
@@ -6677,6 +6957,13 @@
             }
         }
 
+        @Override
+        public String toString() {
+            return "MoveParams{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + packageName + "}";
+        }
+
         public void handleStartCopy() throws RemoteException {
             mRet = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             // Check for storage space on target medium
@@ -7588,6 +7875,7 @@
         // Remember this for later, in case we need to rollback this install
         String pkgName = pkg.packageName;
 
+        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
         boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
         synchronized(mPackages) {
             if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -7644,6 +7932,7 @@
         // First find the old package info and check signatures
         synchronized(mPackages) {
             oldPackage = mPackages.get(pkgName);
+            if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
             if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
                 Slog.w(TAG, "New package has a different signature: " + pkgName);
@@ -7669,6 +7958,8 @@
         boolean deletedPkg = true;
         boolean updatedSettings = false;
 
+        if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
+                + deletedPackage);
         long origUpdateTime;
         if (pkg.mExtras != null) {
             origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
@@ -7706,6 +7997,7 @@
             // scanPackageLocked, unless those directories existed before we even tried to
             // install.
             if(updatedSettings) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
                 deletePackageLI(
                         pkgName, null, true,
                         PackageManager.DELETE_KEEP_DATA,
@@ -7714,6 +8006,7 @@
             // Since we failed to install the new package we need to restore the old
             // package that we deleted.
             if(deletedPkg) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
                 File restoreFile = new File(deletedPackage.mPath);
                 // Parse old package
                 boolean oldOnSd = isExternal(deletedPackage);
@@ -7743,6 +8036,8 @@
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
+        if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+                + ", old=" + deletedPackage);
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
         parseFlags |= PackageManager.INSTALL_REPLACE_EXISTING |
@@ -7865,7 +8160,7 @@
             return;
         }
 
-        Log.d(TAG, "New package installed in " + newPackage.mPath);
+        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath);
 
         synchronized (mPackages) {
             updatePermissionsLPw(newPackage.packageName, newPackage,
@@ -7895,6 +8190,7 @@
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
+        if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
         // Retrieve PackageSettings and parse package
         int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
@@ -7956,14 +8252,18 @@
                     pkg.setPackageName(oldName);
                     pkgName = pkg.packageName;
                     replace = true;
+                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+                            + oldName + " pkgName=" + pkgName);
                 } else if (mPackages.containsKey(pkgName)) {
                     // This package, under its official name, already exists
                     // on the device; we should replace it.
                     replace = true;
+                    if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                 }
             }
             PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (ps != null) {
+                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
                 oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
                 if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                     systemApp = (ps.pkg.applicationInfo.flags &
@@ -8090,14 +8390,19 @@
         return tmpPackageFile;
     }
 
-    public void deletePackage(final String packageName,
-                              final IPackageDeleteObserver observer,
-                              final int flags) {
+    @Override
+    public void deletePackageAsUser(final String packageName,
+                                    final IPackageDeleteObserver observer,
+                                    final int userId, final int flags) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
-        // Queue up an async operation since the package deletion may take a little while.
         final int uid = Binder.getCallingUid();
-        if (!isUserAllowed(uid, UserManager.ALLOW_UNINSTALL_APPS)) {
+        if (UserHandle.getUserId(uid) != userId) {
+            mContext.enforceCallingPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "deletePackage for user " + userId);
+        }
+        if (!isUserAllowed(userId, UserManager.ALLOW_UNINSTALL_APPS)) {
             try {
                 observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -8105,10 +8410,12 @@
             return;
         }
 
+        if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId);
+        // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                final int returnCode = deletePackageX(packageName, uid, flags);
+                final int returnCode = deletePackageX(packageName, userId, flags);
                 if (observer != null) {
                     try {
                         observer.packageDeleted(packageName, returnCode);
@@ -8134,14 +8441,14 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    private int deletePackageX(String packageName, int uid, int flags) {
+    private int deletePackageX(String packageName, int userId, int flags) {
         final PackageRemovedInfo info = new PackageRemovedInfo();
         final boolean res;
 
         IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
         try {
-            if (dpm != null && dpm.packageHasActiveAdmins(packageName, UserHandle.getUserId(uid))) {
+            if (dpm != null && dpm.packageHasActiveAdmins(packageName, userId)) {
                 Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
                 return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
             }
@@ -8151,14 +8458,17 @@
         boolean removedForAllUsers = false;
         boolean systemUpdate = false;
         synchronized (mInstallLock) {
+            if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
             res = deletePackageLI(packageName,
                     (flags & PackageManager.DELETE_ALL_USERS) != 0
-                            ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
+                            ? UserHandle.ALL : new UserHandle(userId),
                     true, flags | REMOVE_CHATTY, info, true);
             systemUpdate = info.isRemovedPackageSystemUpdate;
             if (res && !systemUpdate && mPackages.get(packageName) == null) {
                 removedForAllUsers = true;
             }
+            if (DEBUG_REMOVE) Slog.d(TAG, "delete res: systemUpdate=" + systemUpdate
+                    + " removedForAllUsers=" + removedForAllUsers);
         }
 
         if (res) {
@@ -8234,6 +8544,7 @@
     private void removePackageDataLI(PackageSetting ps, PackageRemovedInfo outInfo,
             int flags, boolean writeSettings) {
         String packageName = ps.name;
+        if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
         removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
         // Retrieve object to delete permissions for shared user later on
         final PackageSetting deletedPs;
@@ -8289,11 +8600,13 @@
         synchronized (mPackages) {
             disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
         }
+        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + newPs
+                + " disabledPs=" + disabledPs);
         if (disabledPs == null) {
             Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
             return false;
-        } else {
-            Log.i(TAG, "Deleting system pkg from data partition");
+        } else if (DEBUG_REMOVE) {
+            Slog.d(TAG, "Deleting system pkg from data partition");
         }
         // Delete the updated package
         outInfo.isRemovedPackageSystemUpdate = true;
@@ -8317,6 +8630,7 @@
             NativeLibraryHelper.removeNativeBinariesLI(newPs.nativeLibraryPathString);
         }
         // Install the system package
+        if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
                 PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
                 SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
@@ -8366,6 +8680,7 @@
             Slog.w(TAG, "Attempt to delete null packageName.");
             return false;
         }
+        if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
         PackageSetting ps;
         boolean dataOnly = false;
         int removeUser = -1;
@@ -8376,28 +8691,44 @@
                 Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
                 return false;
             }
-            if (!isSystemApp(ps) && user != null
+            if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
                     && user.getIdentifier() != UserHandle.USER_ALL) {
                 // The caller is asking that the package only be deleted for a single
                 // user.  To do this, we just mark its uninstalled state and delete
-                // its data.
+                // its data.  If this is a system app, we only allow this to happen if
+                // they have set the special DELETE_SYSTEM_APP which requests different
+                // semantics than normal for uninstalling system apps.
+                if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
                 ps.setUserState(user.getIdentifier(),
                         COMPONENT_ENABLED_STATE_DEFAULT,
                         false, //installed
                         true,  //stopped
                         true,  //notLaunched
                         null, null);
-                if (ps.isAnyInstalled(sUserManager.getUserIds())) {
-                    // Other user still have this package installed, so all
+                if (!isSystemApp(ps)) {
+                    if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+                        // Other user still have this package installed, so all
+                        // we need to do is clear this user's data and save that
+                        // it is uninstalled.
+                        if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
+                        removeUser = user.getIdentifier();
+                        appId = ps.appId;
+                        mSettings.writePackageRestrictionsLPr(removeUser);
+                    } else {
+                        // We need to set it back to 'installed' so the uninstall
+                        // broadcasts will be sent correctly.
+                        if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
+                        ps.setInstalled(true, user.getIdentifier());
+                    }
+                } else {
+                    // This is a system app, so we assume that the
+                    // other users still have this package installed, so all
                     // we need to do is clear this user's data and save that
                     // it is uninstalled.
+                    if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
                     removeUser = user.getIdentifier();
                     appId = ps.appId;
                     mSettings.writePackageRestrictionsLPr(removeUser);
-                } else {
-                    // We need to set it back to 'installed' so the uninstall
-                    // broadcasts will be sent correctly.
-                    ps.setInstalled(true, user.getIdentifier());
                 }
             }
         }
@@ -8405,6 +8736,7 @@
         if (removeUser >= 0) {
             // From above, we determined that we are deleting this only
             // for a single user.  Continue the work here.
+            if (DEBUG_REMOVE) Slog.d(TAG, "Updating install state for user: " + removeUser);
             if (outInfo != null) {
                 outInfo.removedPackage = packageName;
                 outInfo.removedAppId = appId;
@@ -8417,17 +8749,18 @@
 
         if (dataOnly) {
             // Delete application data first
+            if (DEBUG_REMOVE) Slog.d(TAG, "Removing package data only");
             removePackageDataLI(ps, outInfo, flags, writeSettings);
             return true;
         }
         boolean ret = false;
         if (isSystemApp(ps)) {
-            Log.i(TAG, "Removing system package:" + ps.name);
+            if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
             ret = deleteSystemPackageLI(ps, flags, outInfo, writeSettings);
         } else {
-            Log.i(TAG, "Removing non-system package:" + ps.name);
+            if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, ps.appId);
             ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, outInfo,
@@ -9398,7 +9731,15 @@
                     pw.print("  ");
                     pw.print(name);
                     pw.print(" -> ");
-                    pw.println(mSharedLibraries.get(name));
+                    SharedLibraryEntry ent = mSharedLibraries.get(name);
+                    if (ent.path != null) {
+                        pw.print("(jar) ");
+                        pw.print(ent.path);
+                    } else {
+                        pw.print("(apk) ");
+                        pw.print(ent.apk);
+                    }
+                    pw.println();
                 }
             }
 
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 13f514b..e645078 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -32,7 +32,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
-import com.android.server.IntentResolver;
 import com.android.server.pm.PackageManagerService.DumpState;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -2657,6 +2656,162 @@
         ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
     };
 
+    void dumpPackageLPr(PrintWriter pw, String prefix, PackageSetting ps, SimpleDateFormat sdf,
+            Date date, List<UserInfo> users) {
+        pw.print(prefix); pw.print("Package [");
+            pw.print(ps.realName != null ? ps.realName : ps.name);
+            pw.print("] (");
+            pw.print(Integer.toHexString(System.identityHashCode(ps)));
+            pw.println("):");
+
+        if (ps.realName != null) {
+            pw.print(prefix); pw.print("  compat name=");
+            pw.println(ps.name);
+        }
+
+        pw.print(prefix); pw.print("  userId="); pw.print(ps.appId);
+                pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+        if (ps.sharedUser != null) {
+            pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
+        }
+        pw.print(prefix); pw.print("  pkg="); pw.println(ps.pkg);
+        pw.print(prefix); pw.print("  codePath="); pw.println(ps.codePathString);
+        pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
+        pw.print(prefix); pw.print("  nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
+        pw.print(prefix); pw.print("  versionCode="); pw.print(ps.versionCode);
+        if (ps.pkg != null) {
+            pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
+        }
+        pw.println();
+        if (ps.pkg != null) {
+            pw.print(prefix); pw.print("  versionName="); pw.println(ps.pkg.mVersionName);
+            pw.print(prefix); pw.print("  applicationInfo=");
+                pw.println(ps.pkg.applicationInfo.toString());
+            pw.print(prefix); pw.print("  flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
+                    FLAG_DUMP_SPEC); pw.println();
+            pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
+            if (ps.pkg.mOperationPending) {
+                pw.print(prefix); pw.println("  mOperationPending=true");
+            }
+            pw.print(prefix); pw.print("  supportsScreens=[");
+            boolean first = true;
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("small");
+            }
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("medium");
+            }
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("large");
+            }
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("xlarge");
+            }
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("resizeable");
+            }
+            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+                if (!first)
+                    pw.print(", ");
+                first = false;
+                pw.print("anyDensity");
+            }
+            pw.println("]");
+            if (ps.pkg.libraryNames != null && ps.pkg.libraryNames.size() > 0) {
+                pw.print(prefix); pw.println("  libraries:");
+                for (int i=0; i<ps.pkg.libraryNames.size(); i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.libraryNames.get(i));
+                }
+            }
+            if (ps.pkg.usesLibraries != null && ps.pkg.usesLibraries.size() > 0) {
+                pw.print(prefix); pw.println("  usesLibraries:");
+                for (int i=0; i<ps.pkg.usesLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.usesLibraries.get(i));
+                }
+            }
+            if (ps.pkg.usesOptionalLibraries != null
+                    && ps.pkg.usesOptionalLibraries.size() > 0) {
+                pw.print(prefix); pw.println("  usesOptionalLibraries:");
+                for (int i=0; i<ps.pkg.usesOptionalLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    ");
+                        pw.println(ps.pkg.usesOptionalLibraries.get(i));
+                }
+            }
+            if (ps.pkg.usesLibraryFiles != null
+                    && ps.pkg.usesLibraryFiles.length > 0) {
+                pw.print(prefix); pw.println("  usesLibraryFiles:");
+                for (int i=0; i<ps.pkg.usesLibraryFiles.length; i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.usesLibraryFiles[i]);
+                }
+            }
+        }
+        pw.print(prefix); pw.print("  timeStamp=");
+            date.setTime(ps.timeStamp);
+            pw.println(sdf.format(date));
+        pw.print(prefix); pw.print("  firstInstallTime=");
+            date.setTime(ps.firstInstallTime);
+            pw.println(sdf.format(date));
+        pw.print(prefix); pw.print("  lastUpdateTime=");
+            date.setTime(ps.lastUpdateTime);
+            pw.println(sdf.format(date));
+        if (ps.installerPackageName != null) {
+            pw.print(prefix); pw.print("  installerPackageName=");
+                    pw.println(ps.installerPackageName);
+        }
+        pw.print(prefix); pw.print("  signatures="); pw.println(ps.signatures);
+        pw.print(prefix); pw.print("  permissionsFixed="); pw.print(ps.permissionsFixed);
+                pw.print(" haveGids="); pw.print(ps.haveGids);
+                pw.print(" installStatus="); pw.println(ps.installStatus);
+        pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
+                pw.println();
+        for (UserInfo user : users) {
+            pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
+            pw.print(" installed=");
+            pw.print(ps.getInstalled(user.id));
+            pw.print(" stopped=");
+            pw.print(ps.getStopped(user.id));
+            pw.print(" notLaunched=");
+            pw.print(ps.getNotLaunched(user.id));
+            pw.print(" enabled=");
+            pw.println(ps.getEnabled(user.id));
+            HashSet<String> cmp = ps.getDisabledComponents(user.id);
+            if (cmp != null && cmp.size() > 0) {
+                pw.print(prefix); pw.println("    disabledComponents:");
+                for (String s : cmp) {
+                    pw.print(prefix); pw.print("    "); pw.println(s);
+                }
+            }
+            cmp = ps.getEnabledComponents(user.id);
+            if (cmp != null && cmp.size() > 0) {
+                pw.print(prefix); pw.println("    enabledComponents:");
+                for (String s : cmp) {
+                    pw.print(prefix); pw.print("    "); pw.println(s);
+                }
+            }
+        }
+        if (ps.grantedPermissions.size() > 0) {
+            pw.print(prefix); pw.println("  grantedPermissions:");
+            for (String s : ps.grantedPermissions) {
+                pw.print(prefix); pw.print("    "); pw.println(s);
+            }
+        }
+    }
+
     void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         final Date date = new Date();
@@ -2678,123 +2833,7 @@
                 pw.println("Packages:");
                 printedSomething = true;
             }
-            pw.print("  Package [");
-                pw.print(ps.realName != null ? ps.realName : ps.name);
-                pw.print("] (");
-                pw.print(Integer.toHexString(System.identityHashCode(ps)));
-                pw.println("):");
-
-            if (ps.realName != null) {
-                pw.print("    compat name=");
-                pw.println(ps.name);
-            }
-
-            pw.print("    userId="); pw.print(ps.appId);
-            pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
-            pw.print("    sharedUser="); pw.println(ps.sharedUser);
-            pw.print("    pkg="); pw.println(ps.pkg);
-            pw.print("    codePath="); pw.println(ps.codePathString);
-            pw.print("    resourcePath="); pw.println(ps.resourcePathString);
-            pw.print("    nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
-            pw.print("    versionCode="); pw.println(ps.versionCode);
-            if (ps.pkg != null) {
-                pw.print("    applicationInfo="); pw.println(ps.pkg.applicationInfo.toString());
-                pw.print("    flags="); printFlags(pw, ps.pkg.applicationInfo.flags, FLAG_DUMP_SPEC); pw.println();
-                pw.print("    versionName="); pw.println(ps.pkg.mVersionName);
-                pw.print("    dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
-                pw.print("    targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
-                if (ps.pkg.mOperationPending) {
-                    pw.println("    mOperationPending=true");
-                }
-                pw.print("    supportsScreens=[");
-                boolean first = true;
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("small");
-                }
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("medium");
-                }
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("large");
-                }
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("xlarge");
-                }
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("resizeable");
-                }
-                if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
-                    if (!first)
-                        pw.print(", ");
-                    first = false;
-                    pw.print("anyDensity");
-                }
-                pw.println("]");
-            }
-            pw.print("    timeStamp=");
-                date.setTime(ps.timeStamp);
-                pw.println(sdf.format(date));
-            pw.print("    firstInstallTime=");
-                date.setTime(ps.firstInstallTime);
-                pw.println(sdf.format(date));
-            pw.print("    lastUpdateTime=");
-                date.setTime(ps.lastUpdateTime);
-                pw.println(sdf.format(date));
-            if (ps.installerPackageName != null) {
-                pw.print("    installerPackageName="); pw.println(ps.installerPackageName);
-            }
-            pw.print("    signatures="); pw.println(ps.signatures);
-            pw.print("    permissionsFixed="); pw.print(ps.permissionsFixed);
-                    pw.print(" haveGids="); pw.print(ps.haveGids);
-                    pw.print(" installStatus="); pw.println(ps.installStatus);
-            pw.print("    pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
-                    pw.println();
-            for (UserInfo user : users) {
-                pw.print("    User "); pw.print(user.id); pw.print(": ");
-                pw.print(" installed=");
-                pw.print(ps.getInstalled(user.id));
-                pw.print(" stopped=");
-                pw.print(ps.getStopped(user.id));
-                pw.print(" notLaunched=");
-                pw.print(ps.getNotLaunched(user.id));
-                pw.print(" enabled=");
-                pw.println(ps.getEnabled(user.id));
-                HashSet<String> cmp = ps.getDisabledComponents(user.id);
-                if (cmp != null && cmp.size() > 0) {
-                    pw.println("      disabledComponents:");
-                    for (String s : cmp) {
-                        pw.print("      "); pw.println(s);
-                    }
-                }
-                cmp = ps.getEnabledComponents(user.id);
-                if (cmp != null && cmp.size() > 0) {
-                    pw.println("      enabledComponents:");
-                    for (String s : cmp) {
-                        pw.print("      "); pw.println(s);
-                    }
-                }
-            }
-            if (ps.grantedPermissions.size() > 0) {
-                pw.println("    grantedPermissions:");
-                for (String s : ps.grantedPermissions) {
-                    pw.print("      "); pw.println(s);
-                }
-            }
+            dumpPackageLPr(pw, "  ", ps, sdf, date, users);
         }
 
         printedSomething = false;
@@ -2830,27 +2869,7 @@
                     pw.println("Hidden system packages:");
                     printedSomething = true;
                 }
-                pw.print("  Package [");
-                pw.print(ps.realName != null ? ps.realName : ps.name);
-                pw.print("] (");
-                pw.print(Integer.toHexString(System.identityHashCode(ps)));
-                pw.println("):");
-                if (ps.realName != null) {
-                    pw.print("    compat name=");
-                    pw.println(ps.name);
-                }
-                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
-                    pw.print("    applicationInfo=");
-                    pw.println(ps.pkg.applicationInfo.toString());
-                }
-                pw.print("    userId=");
-                pw.println(ps.appId);
-                pw.print("    sharedUser=");
-                pw.println(ps.sharedUser);
-                pw.print("    codePath=");
-                pw.println(ps.codePathString);
-                pw.print("    resourcePath=");
-                pw.println(ps.resourcePathString);
+                dumpPackageLPr(pw, "  ", ps, sdf, date, users);
             }
         }
     }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index c3f4256..1414cbd 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -542,16 +542,16 @@
 
     private void fallbackToSingleUserLocked() {
         // Create the primary user
-        UserInfo primary = new UserInfo(0,
+        UserInfo primary = new UserInfo(UserHandle.USER_OWNER,
                 mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
         mUsers.put(0, primary);
         mNextSerialNumber = MIN_USER_ID;
-        
+
         Bundle restrictions = new Bundle();
         initRestrictionsToDefaults(restrictions);
-        mUserRestrictions.append(0, restrictions);
-        
+        mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
+
         updateUserIdsLocked();
 
         writeUserListLocked();
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
index e302e836..457e92d 100644
--- a/services/java/com/android/server/power/ElectronBeam.java
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -16,18 +16,20 @@
 
 package com.android.server.power;
 
-import com.android.server.display.DisplayManagerService;
-import com.android.server.display.DisplayTransactionListener;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
 
-import android.graphics.Bitmap;
 import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
 import android.opengl.EGL14;
 import android.opengl.EGLConfig;
 import android.opengl.EGLContext;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSurface;
 import android.opengl.GLES10;
-import android.opengl.GLUtils;
+import android.opengl.GLES11Ext;
 import android.os.Looper;
 import android.util.FloatMath;
 import android.util.Slog;
@@ -37,10 +39,8 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
+import com.android.server.display.DisplayManagerService;
+import com.android.server.display.DisplayTransactionListener;
 
 /**
  * Bzzzoooop!  *crackle*
@@ -94,6 +94,7 @@
     // Texture names.  We only use one texture, which contains the screenshot.
     private final int[] mTexNames = new int[1];
     private boolean mTexNamesGenerated;
+    private float mTexMatrix[] = new float[16];
 
     // Vertex and corresponding texture coordinates.
     // We have 4 2D vertices, so 8 elements.  The vertices form a quad.
@@ -115,6 +116,7 @@
      */
     public static final int MODE_FADE = 2;
 
+
     public ElectronBeam(DisplayManagerService displayManager) {
         mDisplayManager = displayManager;
     }
@@ -264,19 +266,23 @@
         GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
         GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
 
+        // set-up texturing
+        GLES10.glDisable(GLES10.GL_TEXTURE_2D);
+        GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
+
         // bind texture and set blending for drawing planes
-        GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
+        GLES10.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
         GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
                 mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
-        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+        GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                 GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
-        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+        GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                 GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
-        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+        GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                 GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
-        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
+        GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                 GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
-        GLES10.glEnable(GLES10.GL_TEXTURE_2D);
+        GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
         GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
         GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
 
@@ -296,7 +302,7 @@
         GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
 
         // clean up after drawing planes
-        GLES10.glDisable(GLES10.GL_TEXTURE_2D);
+        GLES10.glDisable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
         GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
         GLES10.glColorMask(true, true, true, true);
 
@@ -371,81 +377,46 @@
     }
 
     private boolean captureScreenshotTextureAndSetViewport() {
-        // TODO: Use a SurfaceTexture to avoid the extra texture upload.
-        Bitmap bitmap = SurfaceControl.screenshot(mDisplayWidth, mDisplayHeight,
-                0, ELECTRON_BEAM_LAYER - 1);
-        if (bitmap == null) {
-            Slog.e(TAG, "Could not take a screenshot!");
+        if (!attachEglContext()) {
             return false;
         }
         try {
-            if (!attachEglContext()) {
-                return false;
-            }
-            try {
-                if (!mTexNamesGenerated) {
-                    GLES10.glGenTextures(1, mTexNames, 0);
-                    if (checkGlErrors("glGenTextures")) {
-                        return false;
-                    }
-                    mTexNamesGenerated = true;
-                }
-
-                GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
-                if (checkGlErrors("glBindTexture")) {
+            if (!mTexNamesGenerated) {
+                GLES10.glGenTextures(1, mTexNames, 0);
+                if (checkGlErrors("glGenTextures")) {
                     return false;
                 }
-
-                float u = 1.0f;
-                float v = 1.0f;
-                GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
-                if (checkGlErrors("glTexImage2D, first try", false)) {
-                    // Try a power of two size texture instead.
-                    int tw = nextPowerOfTwo(mDisplayWidth);
-                    int th = nextPowerOfTwo(mDisplayHeight);
-                    int format = GLUtils.getInternalFormat(bitmap);
-                    GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
-                            format, tw, th, 0,
-                            format, GLES10.GL_UNSIGNED_BYTE, null);
-                    if (checkGlErrors("glTexImage2D, second try")) {
-                        return false;
-                    }
-
-                    GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
-                    if (checkGlErrors("glTexSubImage2D")) {
-                        return false;
-                    }
-
-                    u = (float)mDisplayWidth / tw;
-                    v = (float)mDisplayHeight / th;
-                }
-
-                // Set up texture coordinates for a quad.
-                // We might need to change this if the texture ends up being
-                // a different size from the display for some reason.
-                mTexCoordBuffer.put(0, 0f);
-                mTexCoordBuffer.put(1, v);
-                mTexCoordBuffer.put(2, 0f);
-                mTexCoordBuffer.put(3, 0f);
-                mTexCoordBuffer.put(4, u);
-                mTexCoordBuffer.put(5, 0f);
-                mTexCoordBuffer.put(6, u);
-                mTexCoordBuffer.put(7, v);
-
-                // Set up our viewport.
-                GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
-                GLES10.glMatrixMode(GLES10.GL_PROJECTION);
-                GLES10.glLoadIdentity();
-                GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
-                GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
-                GLES10.glLoadIdentity();
-                GLES10.glMatrixMode(GLES10.GL_TEXTURE);
-                GLES10.glLoadIdentity();
-            } finally {
-                detachEglContext();
+                mTexNamesGenerated = true;
             }
+
+            SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
+            SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
+                    SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN),
+                    new Surface(st));
+
+            st.updateTexImage();
+            st.getTransformMatrix(mTexMatrix);
+
+            // Set up texture coordinates for a quad.
+            // We might need to change this if the texture ends up being
+            // a different size from the display for some reason.
+            mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f);
+            mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f);
+            mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f);
+            mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f);
+
+            // Set up our viewport.
+            GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
+            GLES10.glMatrixMode(GLES10.GL_PROJECTION);
+            GLES10.glLoadIdentity();
+            GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
+            GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
+            GLES10.glLoadIdentity();
+            GLES10.glMatrixMode(GLES10.GL_TEXTURE);
+            GLES10.glLoadIdentity();
+            GLES10.glLoadMatrixf(mTexMatrix, 0);
         } finally {
-            bitmap.recycle();
+            detachEglContext();
         }
         return true;
     }
@@ -662,10 +633,6 @@
         return 1.0f / (1.0f + FloatMath.exp(-x * s));
     }
 
-    private static int nextPowerOfTwo(int value) {
-        return 1 << (32 - Integer.numberOfLeadingZeros(value));
-    }
-
     private static FloatBuffer createNativeFloatBuffer(int size) {
         ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
         bb.order(ByteOrder.nativeOrder());
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index bd5a3fd..d675822 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -147,11 +147,6 @@
     private AsyncChannel mWifiStateMachineChannel;
 
     /**
-     * Clients receiving asynchronous messages
-     */
-    private List<Messenger> mClients = new ArrayList<Messenger>();
-
-    /**
      * Handles client connections
      */
     private class ClientHandler extends Handler {
@@ -168,7 +163,7 @@
                         if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
                         // We track the clients by the Messenger
                         // since it is expected to be always available
-                        mClients.add(msg.replyTo);
+                        mTrafficPoller.addClient(msg.replyTo);
                     } else {
                         Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
                     }
@@ -180,7 +175,7 @@
                     } else {
                         if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
                     }
-                    mClients.remove(msg.replyTo);
+                    mTrafficPoller.removeClient(msg.replyTo);
                     break;
                 }
                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
@@ -270,7 +265,7 @@
         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
 
         mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
-        mTrafficPoller = new WifiTrafficPoller(mContext, mClients, mInterfaceName);
+        mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);
         mSettingsStore = new WifiSettingsStore(mContext);
 
         mContext.registerReceiver(
diff --git a/services/java/com/android/server/wifi/WifiTrafficPoller.java b/services/java/com/android/server/wifi/WifiTrafficPoller.java
index 9d1e5b6..b498550 100644
--- a/services/java/com/android/server/wifi/WifiTrafficPoller.java
+++ b/services/java/com/android/server/wifi/WifiTrafficPoller.java
@@ -32,6 +32,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -45,6 +46,11 @@
      */
     private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
 
+    private static final int ENABLE_TRAFFIC_STATS_POLL  = 1;
+    private static final int TRAFFIC_STATS_POLL         = 2;
+    private static final int ADD_CLIENT                 = 3;
+    private static final int REMOVE_CLIENT              = 4;
+
     private boolean mEnableTrafficStatsPoll = false;
     private int mTrafficStatsPollToken = 0;
     private long mTxPkts;
@@ -52,7 +58,7 @@
     /* Tracks last reported data activity */
     private int mDataActivity;
 
-    private final List<Messenger> mClients;
+    private final List<Messenger> mClients = new ArrayList<Messenger>();
     // err on the side of updating at boot since screen on broadcast may be missed
     // the first time
     private AtomicBoolean mScreenOn = new AtomicBoolean(true);
@@ -60,8 +66,7 @@
     private NetworkInfo mNetworkInfo;
     private final String mInterface;
 
-    WifiTrafficPoller(Context context, List<Messenger> clients, String iface) {
-        mClients = clients;
+    WifiTrafficPoller(Context context, String iface) {
         mInterface = iface;
         mTrafficHandler = new TrafficHandler();
 
@@ -88,28 +93,40 @@
                 }, filter);
     }
 
+    void addClient(Messenger client) {
+        Message.obtain(mTrafficHandler, ADD_CLIENT, client).sendToTarget();
+    }
+
+    void removeClient(Messenger client) {
+        Message.obtain(mTrafficHandler, REMOVE_CLIENT, client).sendToTarget();
+    }
+
 
     private class TrafficHandler extends Handler {
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
+                case ENABLE_TRAFFIC_STATS_POLL:
                     mEnableTrafficStatsPoll = (msg.arg1 == 1);
                     mTrafficStatsPollToken++;
                     if (mEnableTrafficStatsPoll) {
                         notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
+                        sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
                     }
                     break;
-                }
-                case WifiManager.TRAFFIC_STATS_POLL: {
+                case TRAFFIC_STATS_POLL:
                     if (msg.arg1 == mTrafficStatsPollToken) {
                         notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
+                        sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
                     }
                     break;
-                }
+                case ADD_CLIENT:
+                    mClients.add((Messenger) msg.obj);
+                    break;
+                case REMOVE_CLIENT:
+                    mClients.remove(msg.obj);
+                    break;
             }
 
         }
@@ -120,10 +137,10 @@
         if (mNetworkInfo == null) return;
         if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) {
             msg = Message.obtain(mTrafficHandler,
-                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
+                    ENABLE_TRAFFIC_STATS_POLL, 1, 0);
         } else {
             msg = Message.obtain(mTrafficHandler,
-                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
+                    ENABLE_TRAFFIC_STATS_POLL, 0, 0);
         }
         msg.sendToTarget();
     }
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index ae110dd..3708df2 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -216,14 +216,20 @@
         try {
             try {
                 if (WindowManagerService.DEBUG_SURFACE_TRACE) {
-                    mSurfaceControl = new SurfaceTrace(session, "FreezeSurface",
+                    mSurfaceControl = new SurfaceTrace(session, "ScreenshotSurface",
                             mWidth, mHeight,
-                            PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_SCREENSHOT | SurfaceControl.HIDDEN);
+                            PixelFormat.OPAQUE, SurfaceControl.HIDDEN);
                 } else {
-                    mSurfaceControl = new SurfaceControl(session, "FreezeSurface",
+                    mSurfaceControl = new SurfaceControl(session, "ScreenshotSurface",
                             mWidth, mHeight,
-                            PixelFormat.OPAQUE, SurfaceControl.FX_SURFACE_SCREENSHOT | SurfaceControl.HIDDEN);
+                            PixelFormat.OPAQUE, SurfaceControl.HIDDEN);
                 }
+                // capture a screenshot into the surface we just created
+                Surface sur = new Surface();
+                sur.copyFrom(mSurfaceControl);
+                // FIXME: we should use the proper display
+                SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
+                        SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
                 mSurfaceControl.setLayerStack(mDisplay.getLayerStack());
                 mSurfaceControl.setLayer(FREEZE_LAYER + 1);
                 mSurfaceControl.setAlpha(0);
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 3611f3e..1d95c44 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.view.IWindowId;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -447,6 +448,10 @@
         }
     }
 
+    public IWindowId getWindowId(IBinder window) {
+        return mService.getWindowId(window);
+    }
+
     void windowAddedLocked() {
         if (mSurfaceSession == null) {
             if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a6ce54c..cfefadd 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -43,6 +43,7 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 
 import android.app.AppOpsManager;
+import android.view.IWindowId;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
@@ -2662,6 +2663,13 @@
         }
     }
 
+    public IWindowId getWindowId(IBinder token) {
+        synchronized (mWindowMap) {
+            WindowState window = mWindowMap.get(token);
+            return window != null ? window.mWindowId : null;
+        }
+    }
+
     public int relayoutWindow(Session session, IWindow client, int seq,
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
@@ -6739,22 +6747,14 @@
                         //System.out.println("Changing focus from " + lastFocus
                         //                   + " to " + newFocus);
                         if (newFocus != null) {
-                            try {
-                                //Slog.i(TAG, "Gaining focus: " + newFocus);
-                                newFocus.mClient.windowFocusChanged(true, mInTouchMode);
-                            } catch (RemoteException e) {
-                                // Ignore if process has died.
-                            }
+                            //Slog.i(TAG, "Gaining focus: " + newFocus);
+                            newFocus.reportFocusChangedSerialized(true, mInTouchMode);
                             notifyFocusChanged();
                         }
 
                         if (lastFocus != null) {
-                            try {
-                                //Slog.i(TAG, "Losing focus: " + lastFocus);
-                                lastFocus.mClient.windowFocusChanged(false, mInTouchMode);
-                            } catch (RemoteException e) {
-                                // Ignore if process has died.
-                            }
+                            //Slog.i(TAG, "Losing focus: " + lastFocus);
+                            lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
                         }
                     }
                 } break;
@@ -6769,12 +6769,8 @@
 
                     final int N = losers.size();
                     for (int i=0; i<N; i++) {
-                        try {
-                            //Slog.i(TAG, "Losing delayed focus: " + losers.get(i));
-                            losers.get(i).mClient.windowFocusChanged(false, mInTouchMode);
-                        } catch (RemoteException e) {
-                             // Ignore if process has died.
-                        }
+                        //Slog.i(TAG, "Losing delayed focus: " + losers.get(i));
+                        losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
                     }
                 } break;
 
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a600623..506fcec 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,9 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import android.app.AppOpsManager;
+import android.os.RemoteCallbackList;
+import android.view.IWindowFocusObserver;
+import android.view.IWindowId;
 import com.android.server.input.InputWindowHandle;
 
 import android.content.Context;
@@ -73,6 +76,7 @@
     final int mAppOp;
     // UserId and appId of the owner. Don't display windows of non-current user.
     final int mOwnerUid;
+    final IWindowId mWindowId;
     WindowToken mToken;
     WindowToken mRootToken;
     AppWindowToken mAppToken;
@@ -101,6 +105,8 @@
     boolean mAttachedHidden;    // is our parent window hidden?
     boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
 
+    RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
+
     /**
      * The window size that was requested by the application.  These are in
      * the application's coordinate space (without compatibility scale applied).
@@ -296,6 +302,20 @@
         mAppOp = appOp;
         mToken = token;
         mOwnerUid = s.mUid;
+        mWindowId = new IWindowId.Stub() {
+            @Override
+            public void registerFocusObserver(IWindowFocusObserver observer) {
+                WindowState.this.registerFocusObserver(observer);
+            }
+            @Override
+            public void unregisterFocusObserver(IWindowFocusObserver observer) {
+                WindowState.this.unregisterFocusObserver(observer);
+            }
+            @Override
+            public boolean isFocused() {
+                return WindowState.this.isFocused();
+            }
+        };
         mAttrs.copyFrom(a);
         mViewVisibility = viewVisibility;
         mDisplayContent = displayContent;
@@ -977,6 +997,9 @@
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
                         mService.removeWindowLocked(mSession, win);
+                    } else if (WindowState.this.mHasSurface) {
+                        Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
+                        mService.removeWindowLocked(mSession, WindowState.this);
                     }
                 }
             } catch (IllegalArgumentException ex) {
@@ -1178,6 +1201,55 @@
         return mDisplayContent.getWindowList();
     }
 
+    /**
+     * Report a focus change.  Must be called with no locks held, and consistently
+     * from the same serialized thread (such as dispatched from a handler).
+     */
+    public void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
+        try {
+            mClient.windowFocusChanged(focused, inTouchMode);
+        } catch (RemoteException e) {
+        }
+        if (mFocusCallbacks != null) {
+            final int N = mFocusCallbacks.beginBroadcast();
+            for (int i=0; i<N; i++) {
+                IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
+                try {
+                    if (focused) {
+                        obs.focusGained(mWindowId.asBinder());
+                    } else {
+                        obs.focusLost(mWindowId.asBinder());
+                    }
+                } catch (RemoteException e) {
+                }
+            }
+            mFocusCallbacks.finishBroadcast();
+        }
+    }
+
+    public void registerFocusObserver(IWindowFocusObserver observer) {
+        synchronized(mService.mWindowMap) {
+            if (mFocusCallbacks == null) {
+                mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
+            }
+            mFocusCallbacks.register(observer);
+        }
+    }
+
+    public void unregisterFocusObserver(IWindowFocusObserver observer) {
+        synchronized(mService.mWindowMap) {
+            if (mFocusCallbacks != null) {
+                mFocusCallbacks.unregister(observer);
+            }
+        }
+    }
+
+    public boolean isFocused() {
+        synchronized(mService.mWindowMap) {
+            return mService.mCurrentFocus == this;
+        }
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         pw.print(prefix); pw.print("mDisplayId="); pw.print(mDisplayContent.getDisplayId());
                 pw.print(" mSession="); pw.print(mSession);
diff --git a/services/jni/com_android_server_input_InputApplicationHandle.cpp b/services/jni/com_android_server_input_InputApplicationHandle.cpp
index 0109430..b9681ab 100644
--- a/services/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/services/jni/com_android_server_input_InputApplicationHandle.cpp
@@ -97,7 +97,7 @@
     } else {
         jweak objWeak = env->NewWeakGlobalRef(inputApplicationHandleObj);
         handle = new NativeInputApplicationHandle(objWeak);
-        handle->incStrong(inputApplicationHandleObj);
+        handle->incStrong((void*)android_server_InputApplicationHandle_getHandle);
         env->SetIntField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr,
                 reinterpret_cast<int>(handle));
     }
@@ -115,7 +115,7 @@
         env->SetIntField(obj, gInputApplicationHandleClassInfo.ptr, 0);
 
         NativeInputApplicationHandle* handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr);
-        handle->decStrong(obj);
+        handle->decStrong((void*)android_server_InputApplicationHandle_getHandle);
     }
 }
 
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index 57803e3..09e5be4 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -982,7 +982,7 @@
 
     NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
             messageQueue->getLooper());
-    im->incStrong(serviceObj);
+    im->incStrong(0);
     return reinterpret_cast<jint>(im);
 }
 
diff --git a/services/jni/com_android_server_input_InputWindowHandle.cpp b/services/jni/com_android_server_input_InputWindowHandle.cpp
index 6692994..bbb27d3 100644
--- a/services/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_input_InputWindowHandle.cpp
@@ -183,7 +183,7 @@
 
         jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
         handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
-        handle->incStrong(inputWindowHandleObj);
+        handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
         env->SetIntField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
                 reinterpret_cast<int>(handle));
     }
@@ -201,7 +201,7 @@
         env->SetIntField(obj, gInputWindowHandleClassInfo.ptr, 0);
 
         NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
-        handle->decStrong(obj);
+        handle->decStrong((void*)android_server_InputWindowHandle_getHandle);
     }
 }
 
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/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 21a8ec2..58d6dae 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -34,7 +34,7 @@
         assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
 
         // The constructor has the side effect of writing to file
-        new EntropyMixer("/dev/null", file.getCanonicalPath());
+        new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
 
         assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
     }
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/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
index 93b8705..0368b2f 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
@@ -22,6 +22,8 @@
 import android.graphics.Paint;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class Rotate3dTextActivity extends Activity {
@@ -30,8 +32,28 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        final Rotate3dTextView view = new Rotate3dTextView(this);
-        setContentView(view);
+        final LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+        Rotate3dTextView view = new Rotate3dTextView(this);
+        layout.addView(view, makeLayoutParams());
+
+        view = new Rotate3dTextView(this);
+
+        FrameLayout container = new FrameLayout(this);
+        container.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        container.addView(view);
+
+        layout.addView(container, makeLayoutParams());
+
+        setContentView(layout);
+    }
+
+    private static LinearLayout.LayoutParams makeLayoutParams() {
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.MATCH_PARENT, 0);
+        lp.weight = 1.0f;
+        return lp;
     }
 
     public static class Rotate3dTextView extends View {
@@ -54,11 +76,7 @@
 
         @Override
         protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-
             canvas.drawText(TEXT, getWidth() / 2.0f, getHeight() / 2.0f, mPaint);
-
-            invalidate();
         }
     }
 }
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 87b56f77..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..de0bd002c 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/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
similarity index 61%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
index 64d27ed..353c56d 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Shadows.java
@@ -14,9 +14,19 @@
  * 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;
 
-#include "fisheye_approx.rsh"
 
+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/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/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
similarity index 66%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
index 64d27ed..0f05cb9 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs
+++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/exposure.rs
@@ -14,9 +14,18 @@
  * 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"
+static float bright = 0.f;
+
+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/ImageProcessing2/src/com/android/rs/image/greyscale.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.fs
similarity index 66%
rename from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.rs
rename to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/greyscale.fs
index b5abf3f0..4e13072 100644
--- a/tests/RenderScriptTests/ImageProcessing2/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.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
-void root(const uchar4 *v_in, uchar4 *v_out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    float4 f4 = rsUnpackColor8888(v_in);
 
     float3 mono = dot(f4.rgb, gMonoMult);
-    *v_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/vignette_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/vignette_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh
index 430b685..34e213c 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_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 "vignette.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..de0bd002c 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/vignette_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/vignette_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.fs
index 430b685..6595874 100644
--- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_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 "vignette.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/exposure.rs
similarity index 66%
rename from tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.rs
rename to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/exposure.rs
index 17f7cff..0f05cb9 100644
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/copy.rs
+++ b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/exposure.rs
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
-#pragma version(1)
-#pragma rs java_package_name(com.android.rs.imagejb)
+#include "ip.rsh"
 
-void root(const uchar4 *in, uchar4 *out) {
-    *out = *in;
+static float bright = 0.f;
+
+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/ImageProcessing2/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/ImageProcessing2/src/com/android/rs/image/greyscale.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.fs
index b5abf3f0..4e13072 100644
--- a/tests/RenderScriptTests/ImageProcessing2/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.image2)
-#pragma rs_fp_relaxed
+#include "ip.rsh"
 
 const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
 
-void root(const uchar4 *v_in, uchar4 *v_out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
+uchar4 __attribute__((kernel)) root(uchar4 v_in) {
+    float4 f4 = rsUnpackColor8888(v_in);
 
     float3 mono = dot(f4.rgb, gMonoMult);
-    *v_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/greyscale.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/greyscale.rs
deleted file mode 100644
index b3a7ef0..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/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.imagejb)
-#pragma rs_fp_relaxed
-
-const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
-
-void root(const uchar4 *v_in, uchar4 *out) {
-    float4 f4 = rsUnpackColor8888(*v_in);
-
-    float3 mono = dot(f4.rgb, gMonoMult);
-    *out = rsPackColorTo8888(mono);
-}
-
-
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..de0bd002c 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 3fef0c360..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/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs b/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.fs
similarity index 86%
copy from tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_relaxed.rs
copy to tests/RenderScriptTests/ImageProcessing_jb/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/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.image2)
-#pragma rs_fp_relaxed
+#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.rs
deleted file mode 100644
index 339b747..0000000
--- a/tests/RenderScriptTests/ImageProcessing_jb/src/com/android/rs/image/vignette_relaxed.rs
+++ /dev/null
@@ -1,22 +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
-
-#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/tests/SharedLibrary/client/Android.mk b/tests/SharedLibrary/client/Android.mk
new file mode 100644
index 0000000..60ef92a
--- /dev/null
+++ b/tests/SharedLibrary/client/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_APK_LIBRARIES := SharedLibrary
+
+LOCAL_PACKAGE_NAME := SharedLibraryClient
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/SharedLibrary/client/AndroidManifest.xml b/tests/SharedLibrary/client/AndroidManifest.xml
new file mode 100644
index 0000000..c6a43c0
--- /dev/null
+++ b/tests/SharedLibrary/client/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test.lib_client">
+    <application android:label="@string/app_title">
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="com.google.android.test.shared_library" />
+        <activity android:name="ActivityMain">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/SharedLibrary/client/res/values/strings.xml b/tests/SharedLibrary/client/res/values/strings.xml
new file mode 100644
index 0000000..3757a25
--- /dev/null
+++ b/tests/SharedLibrary/client/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="app_title">SharedLibrary client</string>
+</resources>
diff --git a/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java
new file mode 100644
index 0000000..d6121a5
--- /dev/null
+++ b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/ActivityMain.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.lib_client;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.google.android.test.shared_library.SharedLibraryMain;
+
+public class ActivityMain extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        TextView content = new TextView(this);
+        content.setText("Library version: " + SharedLibraryMain.getVersion(this) + "!");
+
+        SharedLibraryMain.ensureVersion(this, SharedLibraryMain.VERSION_BASE);
+        setContentView(content);
+    }
+}
diff --git a/tests/SharedLibrary/lib/Android.mk b/tests/SharedLibrary/lib/Android.mk
new file mode 100644
index 0000000..c19e23a
--- /dev/null
+++ b/tests/SharedLibrary/lib/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := SharedLibrary
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/SharedLibrary/lib/AndroidManifest.xml b/tests/SharedLibrary/lib/AndroidManifest.xml
new file mode 100644
index 0000000..31fac20
--- /dev/null
+++ b/tests/SharedLibrary/lib/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test.shared_library"
+    android:versionCode="2">
+    <application android:label="SharedLibrary">
+        <library android:name="com.google.android.test.shared_library" />
+        <activity android:name="ActivityMain">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/SharedLibrary/lib/res/values/strings.xml b/tests/SharedLibrary/lib/res/values/strings.xml
new file mode 100644
index 0000000..bbfb0b4
--- /dev/null
+++ b/tests/SharedLibrary/lib/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="upgrade_title">Upgrade required</string>
+    <string name="upgrade_body"><xliff:g id="app">%1$s</xliff:g> requires a newer version
+            of <xliff:g id="lib">%2$s</xliff:g> to run.</string>
+    <string name="upgrade_button">Upgrade</string>
+</resources>
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/ActivityMain.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/ActivityMain.java
new file mode 100644
index 0000000..895aced
--- /dev/null
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/ActivityMain.java
@@ -0,0 +1,32 @@
+/*
+ * 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.google.android.test.shared_library;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        TextView content = new TextView(this);
+        content.setText("Dummy main entry for this apk; not really needed...");
+        setContentView(content);
+    }
+}
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
new file mode 100644
index 0000000..c1cd925
--- /dev/null
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
@@ -0,0 +1,87 @@
+/*
+ * 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.google.android.test.shared_library;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+
+public class SharedLibraryMain {
+    private static String LIBRARY_PACKAGE = "com.google.android.test.shared_library";
+
+    /**
+     * Base version of the library.
+     */
+    public static int VERSION_BASE = 1;
+
+    /**
+     * The second version of the library.
+     */
+    public static int VERSION_SECOND = 2;
+
+    public static int getVersion(Context context) {
+        PackageInfo pi = null;
+        try {
+            pi = context.getPackageManager().getPackageInfo(LIBRARY_PACKAGE, 0);
+            return pi.versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Can't find my package!", e);
+        }
+    }
+
+    public static void ensureVersion(Activity activity, int minVersion) {
+        if (getVersion(activity) >= minVersion) {
+            return;
+        }
+
+        // The current version of the library does not meet the required version.  Show
+        // a dialog to inform the user and have them update to the current version.
+        // Note that updating the library will be necessity mean killing the current
+        // application (so it can be re-started with the new version, so there is no
+        // reason to return a result here.
+        final Context context;
+        try {
+            context = activity.createPackageContext(LIBRARY_PACKAGE, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Can't find my package!", e);
+        }
+
+        // Display the dialog.  Note that we don't need to deal with activity lifecycle
+        // stuff because if the activity gets recreated, it will first call through to
+        // ensureVersion(), causing us to either re-display the dialog if needed or let
+        // it now proceed.
+        final Resources res = context.getResources();
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setTitle(res.getText(R.string.upgrade_title));
+        builder.setMessage(res.getString(R.string.upgrade_body,
+                activity.getApplicationInfo().loadLabel(activity.getPackageManager()),
+                context.getApplicationInfo().loadLabel(context.getPackageManager())));
+        builder.setPositiveButton(res.getText(R.string.upgrade_button),
+                new Dialog.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // Launch play store.
+                    }
+                });
+        builder.show();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index f7ddbc6..09e6878 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.IWindow;
+import android.view.IWindowId;
 import android.view.IWindowSession;
 import android.view.InputChannel;
 import android.view.Surface;
@@ -199,4 +200,10 @@
     public void onRectangleOnScreenRequested(IBinder window, Rect rectangle, boolean immediate) {
         // pass for now.
     }
+
+    @Override
+    public IWindowId getWindowId(IBinder window) throws RemoteException {
+        // pass for now.
+        return null;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 32de9a2..a2df64b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1145,12 +1145,6 @@
     /** @hide */
     public static final int RSSI_PKTCNT_FETCH_FAILED        = BASE + 22;
 
-    /* For system use only */
-    /** @hide */
-    public static final int ENABLE_TRAFFIC_STATS_POLL       = BASE + 31;
-    /** @hide */
-    public static final int TRAFFIC_STATS_POLL              = BASE + 32;
-
 
     /**
      * Passed with {@link ActionListener#onFailure}.
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 95d3041..32cd2f6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -18,14 +18,10 @@
 
 import android.net.wifi.p2p.WifiP2pConfig;
 import android.net.wifi.p2p.WifiP2pGroup;
-import android.net.wifi.p2p.WifiP2pDevice;
 import android.text.TextUtils;
 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
-import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
 import android.util.Log;
 
-import java.io.InputStream;
-import java.lang.Process;
 import java.util.ArrayList;
 import java.util.List;
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 28f6bb2..ed5d22c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -51,11 +51,9 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
-import android.net.wifi.RssiPacketCountInfo;
 import android.net.wifi.WpsResult.Status;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.net.wifi.p2p.WifiP2pService;
-import android.net.wifi.StateChangeResult;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -65,12 +63,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
-import android.util.Log;
 import android.util.LruCache;
 import android.text.TextUtils;
 
@@ -217,24 +213,11 @@
 
     private WifiP2pManager mWifiP2pManager;
     //Used to initiate a connection with WifiP2pService
-    private AsyncChannel mWifiP2pChannel = new AsyncChannel();
-    private AsyncChannel mWifiApConfigChannel = new AsyncChannel();
+    private AsyncChannel mWifiP2pChannel;
+    private AsyncChannel mWifiApConfigChannel;
 
     /* The base for wifi message types */
     static final int BASE = Protocol.BASE_WIFI;
-    /* Load the driver */
-    static final int CMD_LOAD_DRIVER                      = BASE + 1;
-    /* Unload the driver */
-    static final int CMD_UNLOAD_DRIVER                    = BASE + 2;
-    /* Indicates driver load succeeded */
-    static final int CMD_LOAD_DRIVER_SUCCESS              = BASE + 3;
-    /* Indicates driver load failed */
-    static final int CMD_LOAD_DRIVER_FAILURE              = BASE + 4;
-    /* Indicates driver unload succeeded */
-    static final int CMD_UNLOAD_DRIVER_SUCCESS            = BASE + 5;
-    /* Indicates driver unload failed */
-    static final int CMD_UNLOAD_DRIVER_FAILURE            = BASE + 6;
-
     /* Start the supplicant */
     static final int CMD_START_SUPPLICANT                 = BASE + 11;
     /* Stop the supplicant */
@@ -432,16 +415,6 @@
     private State mDefaultState = new DefaultState();
     /* Temporary initial state */
     private State mInitialState = new InitialState();
-    /* Unloading the driver */
-    private State mDriverUnloadingState = new DriverUnloadingState();
-    /* Loading the driver */
-    private State mDriverUnloadedState = new DriverUnloadedState();
-    /* Driver load/unload failed */
-    private State mDriverFailedState = new DriverFailedState();
-    /* Driver loading */
-    private State mDriverLoadingState = new DriverLoadingState();
-    /* Driver loaded */
-    private State mDriverLoadedState = new DriverLoadedState();
     /* Driver loaded, waiting for supplicant to start */
     private State mSupplicantStartingState = new SupplicantStartingState();
     /* Driver loaded and supplicant ready */
@@ -490,8 +463,8 @@
     private State mTetheringState = new TetheringState();
     /* Soft ap is running and we are tethered through connectivity service */
     private State mTetheredState = new TetheredState();
-    /* Waiting for untether confirmation to stop soft Ap */
-    private State mSoftApStoppingState = new SoftApStoppingState();
+    /* Waiting for untether confirmation before stopping soft Ap */
+    private State mUntetheringState = new UntetheringState();
 
     private class TetherStateChange {
         ArrayList<String> available;
@@ -580,10 +553,7 @@
                 getHandler());
         mLinkProperties = new LinkProperties();
 
-        WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
-                context, getHandler());
-        wifiApConfigStore.loadApConfiguration();
-        mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger());
+        mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
 
         mNetworkInfo.setIsAvailable(false);
         mLinkProperties.clear();
@@ -678,11 +648,6 @@
 
         addState(mDefaultState);
             addState(mInitialState, mDefaultState);
-            addState(mDriverUnloadingState, mDefaultState);
-            addState(mDriverUnloadedState, mDefaultState);
-                addState(mDriverFailedState, mDriverUnloadedState);
-            addState(mDriverLoadingState, mDefaultState);
-            addState(mDriverLoadedState, mDefaultState);
             addState(mSupplicantStartingState, mDefaultState);
             addState(mSupplicantStartedState, mDefaultState);
                 addState(mDriverStartingState, mSupplicantStartedState);
@@ -705,7 +670,7 @@
             addState(mSoftApStartedState, mDefaultState);
                 addState(mTetheringState, mSoftApStartedState);
                 addState(mTetheredState, mSoftApStartedState);
-            addState(mSoftApStoppingState, mDefaultState);
+                addState(mUntetheringState, mSoftApStartedState);
 
         setInitialState(mInitialState);
 
@@ -752,13 +717,9 @@
     public void setWifiEnabled(boolean enable) {
         mLastEnableUid.set(Binder.getCallingUid());
         if (enable) {
-            /* Argument is the state that is entered prior to load */
-            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
             sendMessage(CMD_START_SUPPLICANT);
         } else {
             sendMessage(CMD_STOP_SUPPLICANT);
-            /* Argument is the state that is entered upon success */
-            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
         }
     }
 
@@ -768,13 +729,9 @@
     public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
         mLastApEnableUid.set(Binder.getCallingUid());
         if (enable) {
-            /* Argument is the state that is entered prior to load */
-            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
             sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
         } else {
             sendMessage(CMD_STOP_AP);
-            /* Argument is the state that is entered upon success */
-            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
         }
     }
 
@@ -1378,7 +1335,7 @@
     private static final String FLAGS_STR = "flags=";
     private static final String SSID_STR = "ssid=";
     private static final String DELIMITER_STR = "====";
-    private static final int SCAN_BUF_RANGE = 3900;
+    private static final String END_STR = "####";
 
     /**
      * Format:
@@ -1417,11 +1374,12 @@
             if (TextUtils.isEmpty(tmpResults)) break;
             scanResultsBuf.append(tmpResults);
             scanResultsBuf.append("\n");
-            if (tmpResults.length() < SCAN_BUF_RANGE) break;
             String[] lines = tmpResults.split("\n");
             sid = -1;
             for (int i=lines.length - 1; i >= 0; i--) {
-                if (lines[i].startsWith(ID_STR)) {
+                if (lines[i].startsWith(END_STR)) {
+                    break;
+                } else if (lines[i].startsWith(ID_STR)) {
                     try {
                         sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
                     } catch (NumberFormatException e) {
@@ -1472,7 +1430,7 @@
                 } else if (line.startsWith(SSID_STR)) {
                     wifiSsid = WifiSsid.createFromAsciiEncoded(
                             line.substring(SSID_STR.length()));
-                } else if (line.startsWith(DELIMITER_STR)) {
+                } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
                     if (bssid != null) {
                         String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
                         String key = bssid + ssid;
@@ -1741,6 +1699,16 @@
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
     }
 
+    private void handleSupplicantConnectionLoss() {
+        /* Socket connection can be lost when we do a graceful shutdown
+        * or when the driver is hung. Ensure supplicant is stopped here.
+        */
+        mWifiNative.killSupplicant(mP2pSupported);
+        mWifiNative.closeSupplicantConnection();
+        sendSupplicantConnectionChangedBroadcast(false);
+        setWifiState(WIFI_STATE_DISABLED);
+    }
+
     void handlePreDhcpSetup() {
         if (!mBluetoothConnectionActive) {
             /*
@@ -1876,7 +1844,6 @@
     class DefaultState extends State {
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -1919,8 +1886,6 @@
                     }
                     break;
                     /* Discard */
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT_FAILED:
@@ -2026,240 +1991,78 @@
 
     class InitialState extends State {
         @Override
-        //TODO: could move logging into a common class
         public void enter() {
-            if (DBG) log(getName() + "\n");
-            if (mWifiNative.isDriverLoaded()) {
-                transitionTo(mDriverLoadedState);
-            }
-            else {
-                transitionTo(mDriverUnloadedState);
+            mWifiNative.unloadDriver();
+
+            if (mWifiP2pChannel == null) {
+                mWifiP2pChannel = new AsyncChannel();
+                mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
             }
 
-            //Connect to WifiP2pService
-            mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
-            mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
-
-            /* IPv6 is disabled at boot time and is controlled by framework
-             * to be enabled only as long as we are connected to an access point
-             *
-             * This fixes issues, a few being:
-             * - IPv6 addresses and routes stick around after disconnection
-             * - When connected, the kernel is unaware and can fail to start IPv6 negotiation
-             * - The kernel sometimes starts autoconfiguration when 802.1x is not complete
-             */
-            try {
-                mNwService.disableIpv6(mInterfaceName);
-            } catch (RemoteException re) {
-                loge("Failed to disable IPv6: " + re);
-            } catch (IllegalStateException e) {
-                loge("Failed to disable IPv6: " + e);
+            if (mWifiApConfigChannel == null) {
+                mWifiApConfigChannel = new AsyncChannel();
+                WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
+                        mContext, getHandler());
+                wifiApConfigStore.loadApConfiguration();
+                mWifiApConfigChannel.connectSync(mContext, getHandler(),
+                        wifiApConfigStore.getMessenger());
             }
         }
-    }
-
-    class DriverLoadingState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-            final Message message = new Message();
-            message.copyFrom(getCurrentMessage());
-            /* TODO: add a timeout to fail when driver load is hung.
-             * Similarly for driver unload.
-             */
-            new Thread(new Runnable() {
-                public void run() {
-                    mWakeLock.acquire();
-                    //enabling state
-                    switch(message.arg1) {
-                        case WIFI_STATE_ENABLING:
+        public boolean processMessage(Message message) {
+            switch (message.what) {
+                case CMD_START_SUPPLICANT:
+                    if (mWifiNative.loadDriver()) {
+                        try {
+                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
+                        } catch (Exception e) {
+                            loge("Failed to reload STA firmware " + e);
+                            // continue
+                        }
+
+                        try {
+                            // A runtime crash can leave the interface up and
+                            // this affects connectivity when supplicant starts up.
+                            // Ensure interface is down before a supplicant start.
+                            mNwService.setInterfaceDown(mInterfaceName);
+                            // Set privacy extensions
+                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+
+                           // IPv6 is enabled only as long as access point is connected since:
+                           // - IPv6 addresses and routes stick around after disconnection
+                           // - kernel is unaware when connected and fails to start IPv6 negotiation
+                           // - kernel can start autoconfiguration when 802.1x is not complete
+                            mNwService.disableIpv6(mInterfaceName);
+                        } catch (RemoteException re) {
+                            loge("Unable to change interface settings: " + re);
+                        } catch (IllegalStateException ie) {
+                            loge("Unable to change interface settings: " + ie);
+                        }
+
+                       /* Stop a running supplicant after a runtime restart
+                        * Avoids issues with drivers that do not handle interface down
+                        * on a running supplicant properly.
+                        */
+                        mWifiNative.killSupplicant(mP2pSupported);
+                        if(mWifiNative.startSupplicant(mP2pSupported)) {
                             setWifiState(WIFI_STATE_ENABLING);
-                            break;
-                        case WIFI_AP_STATE_ENABLING:
-                            setWifiApState(WIFI_AP_STATE_ENABLING);
-                            break;
-                    }
-
-                    if(mWifiNative.loadDriver()) {
-                        if (DBG) log("Driver load successful");
-                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
-                    } else {
-                        loge("Failed to load driver!");
-                        switch(message.arg1) {
-                            case WIFI_STATE_ENABLING:
-                                setWifiState(WIFI_STATE_UNKNOWN);
-                                break;
-                            case WIFI_AP_STATE_ENABLING:
-                                setWifiApState(WIFI_AP_STATE_FAILED);
-                                break;
+                            if (DBG) log("Supplicant start successful");
+                            mWifiMonitor.startMonitoring();
+                            transitionTo(mSupplicantStartingState);
+                        } else {
+                            loge("Failed to start supplicant!");
                         }
-                        sendMessage(CMD_LOAD_DRIVER_FAILURE);
-                    }
-                    mWakeLock.release();
-                }
-            }).start();
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch (message.what) {
-                case CMD_LOAD_DRIVER_SUCCESS:
-                    transitionTo(mDriverLoadedState);
-                    break;
-                case CMD_LOAD_DRIVER_FAILURE:
-                    transitionTo(mDriverFailedState);
-                    break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
-                case CMD_START_SUPPLICANT:
-                case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
-                case CMD_STOP_AP:
-                case CMD_START_DRIVER:
-                case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
-                case CMD_SET_COUNTRY_CODE:
-                case CMD_SET_FREQUENCY_BAND:
-                case CMD_START_PACKET_FILTERING:
-                case CMD_STOP_PACKET_FILTERING:
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class DriverLoadedState extends State {
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch(message.what) {
-                case CMD_UNLOAD_DRIVER:
-                    transitionTo(mDriverUnloadingState);
-                    break;
-                case CMD_START_SUPPLICANT:
-                    try {
-                        mNwService.wifiFirmwareReload(mInterfaceName, "STA");
-                    } catch (Exception e) {
-                        loge("Failed to reload STA firmware " + e);
-                        // continue
-                    }
-                   try {
-                       //A runtime crash can leave the interface up and
-                       //this affects connectivity when supplicant starts up.
-                       //Ensure interface is down before a supplicant start.
-                        mNwService.setInterfaceDown(mInterfaceName);
-                        //Set privacy extensions
-                        mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
-                    } catch (RemoteException re) {
-                        loge("Unable to change interface settings: " + re);
-                    } catch (IllegalStateException ie) {
-                        loge("Unable to change interface settings: " + ie);
-                    }
-
-                    /* Stop a running supplicant after a runtime restart
-                     * Avoids issues with drivers that do not handle interface down
-                     * on a running supplicant properly.
-                     */
-                    if (DBG) log("Kill any running supplicant");
-                    mWifiNative.killSupplicant(mP2pSupported);
-
-                    if(mWifiNative.startSupplicant(mP2pSupported)) {
-                        if (DBG) log("Supplicant start successful");
-                        mWifiMonitor.startMonitoring();
-                        transitionTo(mSupplicantStartingState);
                     } else {
-                        loge("Failed to start supplicant!");
-                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+                        loge("Failed to load driver");
                     }
                     break;
                 case CMD_START_AP:
-                    transitionTo(mSoftApStartingState);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class DriverUnloadingState extends State {
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-
-            final Message message = new Message();
-            message.copyFrom(getCurrentMessage());
-            new Thread(new Runnable() {
-                public void run() {
-                    if (DBG) log(getName() + message.toString() + "\n");
-                    mWakeLock.acquire();
-                    if(mWifiNative.unloadDriver()) {
-                        if (DBG) log("Driver unload successful");
-                        sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
-
-                        switch(message.arg1) {
-                            case WIFI_STATE_DISABLED:
-                            case WIFI_STATE_UNKNOWN:
-                                setWifiState(message.arg1);
-                                break;
-                            case WIFI_AP_STATE_DISABLED:
-                            case WIFI_AP_STATE_FAILED:
-                                setWifiApState(message.arg1);
-                                break;
-                        }
+                    if (mWifiNative.loadDriver()) {
+                        setWifiApState(WIFI_AP_STATE_ENABLING);
+                        transitionTo(mSoftApStartingState);
                     } else {
-                        loge("Failed to unload driver!");
-                        sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
-
-                        switch(message.arg1) {
-                            case WIFI_STATE_DISABLED:
-                            case WIFI_STATE_UNKNOWN:
-                                setWifiState(WIFI_STATE_UNKNOWN);
-                                break;
-                            case WIFI_AP_STATE_DISABLED:
-                            case WIFI_AP_STATE_FAILED:
-                                setWifiApState(WIFI_AP_STATE_FAILED);
-                                break;
-                        }
+                        loge("Failed to load driver for softap");
                     }
-                    mWakeLock.release();
-                }
-            }).start();
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch (message.what) {
-                case CMD_UNLOAD_DRIVER_SUCCESS:
-                    transitionTo(mDriverUnloadedState);
-                    break;
-                case CMD_UNLOAD_DRIVER_FAILURE:
-                    transitionTo(mDriverFailedState);
-                    break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
-                case CMD_START_SUPPLICANT:
-                case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
-                case CMD_STOP_AP:
-                case CMD_START_DRIVER:
-                case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
-                case CMD_SET_COUNTRY_CODE:
-                case CMD_SET_FREQUENCY_BAND:
-                case CMD_START_PACKET_FILTERING:
-                case CMD_STOP_PACKET_FILTERING:
-                    deferMessage(message);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -2267,44 +2070,7 @@
         }
     }
 
-    class DriverUnloadedState extends State {
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch (message.what) {
-                case CMD_LOAD_DRIVER:
-                    transitionTo(mDriverLoadingState);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class DriverFailedState extends State {
-        @Override
-        public void enter() {
-            loge(getName() + "\n");
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            return NOT_HANDLED;
-        }
-    }
-
-
     class SupplicantStartingState extends State {
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-
         private void initializeWpsDetails() {
             String detail;
             detail = SystemProperties.get("ro.product.name", "");
@@ -2337,7 +2103,6 @@
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
                     if (DBG) log("Supplicant connection established");
@@ -2362,18 +2127,16 @@
                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                         loge("Failed to setup control channel, restart supplicant");
                         mWifiNative.killSupplicant(mP2pSupported);
-                        transitionTo(mDriverLoadedState);
+                        transitionTo(mInitialState);
                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     } else {
                         loge("Failed " + mSupplicantRestartCount +
                                 " times to start supplicant, unload driver");
                         mSupplicantRestartCount = 0;
-                        transitionTo(mDriverLoadedState);
-                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+                        setWifiState(WIFI_STATE_UNKNOWN);
+                        transitionTo(mInitialState);
                     }
                     break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
@@ -2397,7 +2160,6 @@
     class SupplicantStartedState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             /* Initialize for connect mode operation at start */
             mIsScanMode = false;
             /* Wifi is available as long as we have a connection to supplicant */
@@ -2414,7 +2176,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             WifiConfiguration config;
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
@@ -2426,16 +2187,13 @@
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     loge("Connection lost, restart supplicant");
-                    mWifiNative.killSupplicant(mP2pSupported);
-                    mWifiNative.closeSupplicantConnection();
-                    mNetworkInfo.setIsAvailable(false);
+                    handleSupplicantConnectionLoss();
                     handleNetworkDisconnect();
-                    sendSupplicantConnectionChangedBroadcast(false);
                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
                     if (mP2pSupported) {
                         transitionTo(mWaitForP2pDisableState);
                     } else {
-                        transitionTo(mDriverLoadedState);
+                        transitionTo(mInitialState);
                     }
                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     break;
@@ -2545,8 +2303,6 @@
     class SupplicantStoppingState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             /* Send any reset commands to supplicant before shutting it down */
             handleNetworkDisconnect();
             if (mDhcpStateMachine != null) {
@@ -2561,38 +2317,27 @@
             /* Send ourselves a delayed message to indicate failure after a wait time */
             sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
                     ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
-
-            mNetworkInfo.setIsAvailable(false);
             setWifiState(WIFI_STATE_DISABLING);
-            sendSupplicantConnectionChangedBroadcast(false);
             mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
                     loge("Supplicant connection received while stopping");
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
                     if (DBG) log("Supplicant connection lost");
-                    /* Socket connection can be lost when we do a graceful shutdown
-                     * or when the driver is hung. Ensure supplicant is stopped here.
-                     */
-                    mWifiNative.killSupplicant(mP2pSupported);
-                    mWifiNative.closeSupplicantConnection();
-                    transitionTo(mDriverLoadedState);
+                    handleSupplicantConnectionLoss();
+                    transitionTo(mInitialState);
                     break;
                 case CMD_STOP_SUPPLICANT_FAILED:
                     if (message.arg1 == mSupplicantStopFailureToken) {
                         loge("Timed out on a supplicant stop, kill and proceed");
-                        mWifiNative.killSupplicant(mP2pSupported);
-                        mWifiNative.closeSupplicantConnection();
-                        transitionTo(mDriverLoadedState);
+                        handleSupplicantConnectionLoss();
+                        transitionTo(mInitialState);
                     }
                     break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
@@ -2617,8 +2362,6 @@
         private int mTries;
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             mTries = 1;
             /* Send ourselves a delayed message to start driver a second time */
             sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
@@ -2626,7 +2369,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     SupplicantState state = handleSupplicantStateChange(message);
@@ -2683,8 +2425,6 @@
     class DriverStartedState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             mIsRunning = true;
             mInDelayedStop = false;
             updateBatteryWorkSource(null);
@@ -2740,7 +2480,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_START_SCAN:
                     startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
@@ -2859,7 +2598,6 @@
         }
         @Override
         public void exit() {
-            if (DBG) log(getName() + "\n");
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = new ArrayList<ScanResult>();
@@ -2870,10 +2608,9 @@
         private State mTransitionToState;
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             switch (getCurrentMessage().what) {
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    mTransitionToState = mDriverLoadedState;
+                    mTransitionToState = mInitialState;
                     break;
                 case CMD_DELAYED_STOP_DRIVER:
                     mTransitionToState = mDriverStoppingState;
@@ -2889,15 +2626,12 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case WifiStateMachine.CMD_DISABLE_P2P_RSP:
                     transitionTo(mTransitionToState);
                     break;
                 /* Defer wifi start/shut and driver commands */
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
@@ -2924,12 +2658,7 @@
 
     class DriverStoppingState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     SupplicantState state = handleSupplicantStateChange(message);
@@ -2959,12 +2688,7 @@
 
     class DriverStoppedState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
@@ -2990,12 +2714,7 @@
 
     class ScanModeState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_SET_SCAN_MODE:
                     if (message.arg1 == SCAN_ONLY_MODE) {
@@ -3027,12 +2746,7 @@
 
     class ConnectModeState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             StateChangeResult stateChangeResult;
             switch(message.what) {
                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
@@ -3165,7 +2879,6 @@
     class L2ConnectedState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             mRssiPollToken++;
             if (mEnableRssiPolling) {
                 sendMessage(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0));
@@ -3174,7 +2887,6 @@
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
               case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                   handlePreDhcpSetup();
@@ -3284,8 +2996,6 @@
     class ObtainingIpState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
                 //start DHCP
                 if (mDhcpStateMachine == null) {
@@ -3355,7 +3065,6 @@
     class VerifyingLinkState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
             mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
             sendNetworkStateChangeBroadcast(mLastBssid);
@@ -3408,12 +3117,7 @@
 
     class ConnectedState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-       }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
                     if (DBG) log("Watchdog reports poor link");
@@ -3446,12 +3150,7 @@
 
     class DisconnectingState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case CMD_SET_SCAN_MODE:
                     if (message.arg1 == SCAN_ONLY_MODE) {
@@ -3499,8 +3198,6 @@
 
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             // We dont scan frequently if this is a temporary disconnect
             // due to p2p
             if (mTemporarilyDisconnectWifi) {
@@ -3542,7 +3239,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             boolean ret = HANDLED;
             switch (message.what) {
                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
@@ -3623,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;
@@ -3648,12 +3349,10 @@
         private Message mSourceMessage;
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
             mSourceMessage = Message.obtain(getCurrentMessage());
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case WifiMonitor.WPS_SUCCESS_EVENT:
                     replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
@@ -3737,8 +3436,6 @@
     class SoftApStartingState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             final Message message = getCurrentMessage();
             if (message.what == CMD_START_AP) {
                 final WifiConfiguration config = (WifiConfiguration) message.obj;
@@ -3755,10 +3452,7 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
@@ -3787,8 +3481,8 @@
                     transitionTo(mSoftApStartedState);
                     break;
                 case CMD_START_AP_FAILURE:
-                    // initiate driver unload
-                    sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+                    setWifiApState(WIFI_AP_STATE_FAILED);
+                    transitionTo(mInitialState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -3799,31 +3493,25 @@
 
     class SoftApStartedState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_STOP_AP:
                     if (DBG) log("Stopping Soft AP");
-                    setWifiApState(WIFI_AP_STATE_DISABLING);
-
                     /* We have not tethered at this point, so we just shutdown soft Ap */
                     try {
                         mNwService.stopAccessPoint(mInterfaceName);
                     } catch(Exception e) {
                         loge("Exception in stopAccessPoint()");
                     }
-                    transitionTo(mDriverLoadedState);
+                    setWifiApState(WIFI_AP_STATE_DISABLED);
+                    transitionTo(mInitialState);
                     break;
                 case CMD_START_AP:
                     // Ignore a start on a running access point
                     break;
                     /* Fail client mode operation when soft AP is enabled */
                 case CMD_START_SUPPLICANT:
-                   loge("Cannot start supplicant with a running soft AP");
+                    loge("Cannot start supplicant with a running soft AP");
                     setWifiState(WIFI_STATE_UNKNOWN);
                     break;
                 case CMD_TETHER_STATE_CHANGE:
@@ -3842,15 +3530,12 @@
     class TetheringState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             /* Send ourselves a delayed message to shut down if tethering fails to notify */
             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_TETHER_STATE_CHANGE:
                     TetherStateChange stateChange = (TetherStateChange) message.obj;
@@ -3861,11 +3546,10 @@
                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
                     if (message.arg1 == mTetherToken) {
                         loge("Failed to get tether update, shutdown soft access point");
-                        setWifiApEnabled(null, false);
+                        transitionTo(mSoftApStartedState);
+                        sendMessage(CMD_STOP_AP);
                     }
                     break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
@@ -3888,12 +3572,7 @@
 
     class TetheredState extends State {
         @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-        }
-        @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_TETHER_STATE_CHANGE:
                     TetherStateChange stateChange = (TetherStateChange) message.obj;
@@ -3906,7 +3585,7 @@
                     if (DBG) log("Untethering before stopping AP");
                     setWifiApState(WIFI_AP_STATE_DISABLING);
                     stopTethering();
-                    transitionTo(mSoftApStoppingState);
+                    transitionTo(mUntetheringState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -3915,11 +3594,9 @@
         }
     }
 
-    class SoftApStoppingState extends State {
+    class UntetheringState extends State {
         @Override
         public void enter() {
-            if (DBG) log(getName() + "\n");
-
             /* Send ourselves a delayed message to shut down if tethering fails to notify */
             sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT,
                     ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS);
@@ -3927,7 +3604,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
             switch(message.what) {
                 case CMD_TETHER_STATE_CHANGE:
                     TetherStateChange stateChange = (TetherStateChange) message.obj;
@@ -3935,26 +3611,16 @@
                     /* Wait till wifi is untethered */
                     if (isWifiTethered(stateChange.active)) break;
 
-                    try {
-                        mNwService.stopAccessPoint(mInterfaceName);
-                    } catch(Exception e) {
-                        loge("Exception in stopAccessPoint()");
-                    }
-                    transitionTo(mDriverLoadedState);
+                    transitionTo(mSoftApStartedState);
+                    sendMessage(CMD_STOP_AP);
                     break;
                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
                     if (message.arg1 == mTetherToken) {
                         loge("Failed to get tether update, force stop access point");
-                        try {
-                            mNwService.stopAccessPoint(mInterfaceName);
-                        } catch(Exception e) {
-                            loge("Exception in stopAccessPoint()");
-                        }
-                        transitionTo(mDriverLoadedState);
+                        transitionTo(mSoftApStartedState);
+                        sendMessage(CMD_STOP_AP);
                     }
                     break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_START_AP:
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 4a489d5..e6a1df1 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -1745,6 +1745,8 @@
                     //Ignore more client requests
                     break;
                 case PEER_CONNECTION_USER_ACCEPT:
+                    //Stop discovery to avoid failure due to channel switch
+                    mWifiNative.p2pStopFind();
                     if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
                         mWifiNative.startWpsPbc(mGroup.getInterface(), null);
                     } else {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index b6bbfc4..1ba991e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -155,6 +155,7 @@
     /** copy constructor */
     public WifiP2pWfdInfo(WifiP2pWfdInfo source) {
         if (source != null) {
+            mWfdEnabled = source.mWfdEnabled;
             mDeviceInfo = source.mDeviceInfo;
             mCtrlPort = source.mCtrlPort;
             mMaxThroughput = source.mMaxThroughput;