Merge "Enable assertions in LayoutLib create" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 8a50ae84..e2fa7d1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,9 +118,7 @@
 	core/java/android/content/IIntentReceiver.aidl \
 	core/java/android/content/IIntentSender.aidl \
 	core/java/android/content/IOnPrimaryClipChangedListener.aidl \
-        core/java/android/content/IPermissionResponseCallback.aidl \
 	core/java/android/content/IRestrictionsManager.aidl \
-        core/java/android/content/IRestrictionsProvider.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
 	core/java/android/content/ISyncContext.aidl \
 	core/java/android/content/ISyncServiceAdapter.aidl \
diff --git a/api/current.txt b/api/current.txt
index 8076d47..111869d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -742,6 +742,8 @@
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
     field public static final int launchMode = 16842781; // 0x101001d
+    field public static final int launchTaskBehindBackgroundAnimation = 16843920; // 0x1010490
+    field public static final int launchTaskBehindSourceAnimation = 16843921; // 0x1010491
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
@@ -854,7 +856,7 @@
     field public static final int mirrorForRtl = 16843726; // 0x10103ce
     field public static final int mode = 16843134; // 0x101017e
     field public static final int moreIcon = 16843061; // 0x1010135
-    field public static final int multiArch = 16843920; // 0x1010490
+    field public static final int multiArch = 16843922; // 0x1010492
     field public static final int multiprocess = 16842771; // 0x1010013
     field public static final int name = 16842755; // 0x1010003
     field public static final int navigationBarColor = 16843860; // 0x1010454
@@ -1284,7 +1286,7 @@
     field public static final int topLeftRadius = 16843177; // 0x10101a9
     field public static final int topOffset = 16843352; // 0x1010258
     field public static final int topRightRadius = 16843178; // 0x10101aa
-    field public static final int touchscreenBlocksFocus = 16843921; // 0x1010491
+    field public static final int touchscreenBlocksFocus = 16843923; // 0x1010493
     field public static final int track = 16843631; // 0x101036f
     field public static final int transcriptMode = 16843008; // 0x1010100
     field public static final int transformPivotX = 16843552; // 0x1010320
@@ -3664,6 +3666,7 @@
 
   public class ActivityOptions {
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+    method public static android.app.ActivityOptions makeLaunchTaskBehindAnimation();
     method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
     method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...);
@@ -6575,11 +6578,10 @@
 
 package android.content {
 
-  public abstract class AbstractRestrictionsProvider extends android.app.Service {
+  public abstract class AbstractRestrictionsProvider extends android.content.BroadcastReceiver {
     ctor public AbstractRestrictionsProvider();
-    method public abstract android.os.Bundle getPermissionResponse(java.lang.String, java.lang.String);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void requestPermission(java.lang.String, java.lang.String, android.os.Bundle);
+    method public void onReceive(android.content.Context, android.content.Intent);
+    method public abstract void requestPermission(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle);
   }
 
   public abstract class AbstractThreadedSyncAdapter {
@@ -7696,7 +7698,6 @@
     field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
     field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
     field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
-    field public static final int FLAG_ACTIVITY_LAUNCH_BEHIND = 4096; // 0x1000
     field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
     field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
     field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -7963,24 +7964,25 @@
   public class RestrictionsManager {
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
-    method public void getPermissionResponse(java.lang.String, android.content.RestrictionsManager.PermissionResponseCallback);
     method public boolean hasRestrictionsProvider();
     method public void notifyPermissionResponse(java.lang.String, android.os.Bundle);
     method public void requestPermission(java.lang.String, android.os.Bundle);
     field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
-    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name";
-    field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response";
+    field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.content.action.REQUEST_PERMISSION";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
+    field public static final java.lang.String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
+    field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
     field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.request.approve_label";
     field public static final java.lang.String REQUEST_KEY_DATA = "android.request.data";
     field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
-    field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.request.device";
     field public static final java.lang.String REQUEST_KEY_ICON = "android.request.icon";
     field public static final java.lang.String REQUEST_KEY_ID = "android.request.id";
     field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.request.mesg";
-    field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor";
+    field public static final java.lang.String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
     field public static final java.lang.String REQUEST_KEY_TITLE = "android.request.title";
+    field public static final java.lang.String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
     field public static final java.lang.String REQUEST_TYPE_LOCAL_APPROVAL = "android.request.type.local_approval";
-    field public static final java.lang.String REQUEST_TYPE_QUESTION = "android.request.type.question";
     field public static final java.lang.String RESPONSE_KEY_ERROR_CODE = "android.response.errorcode";
     field public static final java.lang.String RESPONSE_KEY_ERROR_MESSAGE = "android.response.errormsg";
     field public static final java.lang.String RESPONSE_KEY_RESPONSE_TIMESTAMP = "android.response.timestamp";
@@ -7995,11 +7997,6 @@
     field public static final int RESULT_UNKNOWN_REQUEST = 4; // 0x4
   }
 
-  public static abstract class RestrictionsManager.PermissionResponseCallback {
-    ctor public RestrictionsManager.PermissionResponseCallback();
-    method public abstract void onResponse(android.os.Bundle);
-  }
-
   public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
     ctor public SearchRecentSuggestionsProvider();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -28222,6 +28219,7 @@
     method public java.lang.String getId();
     method public android.telecomm.CallState getState();
     method public android.telecomm.StatusHints getStatusHints();
+    method public int getVideoState();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4a70e15..e2b5a84 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -30,7 +30,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
@@ -2196,13 +2195,21 @@
             return true;
         }
 
-        case MEDIA_RESOURCES_RELEASED: {
+        case MEDIA_RESOURCES_RELEASED_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
             mediaResourcesReleased(token);
             reply.writeNoException();
             return true;
         }
+
+        case NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            notifyLaunchTaskBehindComplete(token);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5069,7 +5076,20 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(MEDIA_RESOURCES_RELEASED, data, reply, IBinder.FLAG_ONEWAY);
+        mRemote.transact(MEDIA_RESOURCES_RELEASED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply,
+                IBinder.FLAG_ONEWAY);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fafa948..4c02314 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -126,6 +126,8 @@
     public static final int ANIM_SCENE_TRANSITION = 5;
     /** @hide */
     public static final int ANIM_DEFAULT = 6;
+    /** @hide */
+    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
 
     private String mPackageName;
     private int mAnimationType = ANIM_NONE;
@@ -432,6 +434,27 @@
         return opts;
     }
 
+    /**
+     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
+     * presented to the user but will instead be only available through the recents task list.
+     * In addition, the new task wil be affiliated with the launching activity's task.
+     * Affiliated tasks are grouped together in the recents task list.
+     *
+     * <p>This behavior is not supported for activities with {@link
+     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
+     * <code>singleInstance</code> or <code>singleTask</code>.
+     */
+    public static ActivityOptions makeLaunchTaskBehindAnimation() {
+        final ActivityOptions opts = new ActivityOptions();
+        opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND;
+        return opts;
+    }
+
+    /** @hide */
+    public boolean getLaunchTaskBehind() {
+        return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
+    }
+
     private ActivityOptions() {
     }
 
@@ -647,16 +670,15 @@
         if (mPackageName != null) {
             b.putString(KEY_PACKAGE_NAME, mPackageName);
         }
+        b.putInt(KEY_ANIM_TYPE, mAnimationType);
         switch (mAnimationType) {
             case ANIM_CUSTOM:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
                 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
                 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
             case ANIM_SCALE_UP:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
                 b.putInt(KEY_ANIM_START_WIDTH, mStartWidth);
@@ -664,7 +686,6 @@
                 break;
             case ANIM_THUMBNAIL_SCALE_UP:
             case ANIM_THUMBNAIL_SCALE_DOWN:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
                 b.putInt(KEY_ANIM_START_X, mStartX);
                 b.putInt(KEY_ANIM_START_Y, mStartY);
@@ -672,7 +693,6 @@
                         != null ? mAnimationStartedListener.asBinder() : null);
                 break;
             case ANIM_SCENE_TRANSITION:
-                b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 if (mTransitionReceiver != null) {
                     b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
                 }
@@ -683,6 +703,7 @@
                 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
+
         return b;
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index b3a8a8a..c6921a2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -444,6 +444,8 @@
     public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException;
     public void mediaResourcesReleased(IBinder token) throws RemoteException;
 
+    public void notifyLaunchTaskBehindComplete(IBinder token) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -750,5 +752,6 @@
     int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
     int SET_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
     int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
-    int MEDIA_RESOURCES_RELEASED = IBinder.FIRST_CALL_TRANSACTION+227;
+    int MEDIA_RESOURCES_RELEASED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+227;
+    int NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+228;
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a3ffc00..5b92538 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -44,6 +44,9 @@
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
 
+    void setPackagePriority(String pkg, int uid, int priority);
+    int getPackagePriority(String pkg, int uid);
+
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
     StatusBarNotification[] getActiveNotifications(String callingPkg);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 97e3fc5..faf8645 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -219,22 +219,6 @@
             "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
 
     /**
-     * Broadcast Action: Indicate BLE Advertising is started.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
-            "android.bluetooth.adapter.action.ADVERTISING_STARTED";
-
-    /**
-     * Broadcast Action: Indicated BLE Advertising is stopped.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
-            "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
-
-    /**
      * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
      * intents to request the current scan mode. Possible values are:
      * {@link #SCAN_MODE_NONE},
@@ -403,8 +387,6 @@
     private IBluetooth mService;
 
     private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
-    private BluetoothAdvScanData mBluetoothAdvScanData = null;
-    private GattCallbackWrapper mAdvertisingGattCallback;
     private final Handler mHandler;  // Handler to post the advertise callback to run on main thread.
     private final Object mLock = new Object();
 
@@ -481,29 +463,6 @@
     }
 
     /**
-     * Returns a {@link BluetoothAdvScanData} object representing advertising data.
-     * Data will be reset when bluetooth service is turned off.
-     * @hide
-     */
-    public BluetoothAdvScanData getAdvScanData() {
-      try {
-          IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-          if (iGatt == null) {
-              // BLE is not supported
-              Log.e(TAG, "failed to start, iGatt null");
-              return null;
-          }
-          if (mBluetoothAdvScanData == null) {
-              mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD);
-          }
-          return mBluetoothAdvScanData;
-      } catch (RemoteException e) {
-          Log.e(TAG, "failed to get advScanData, error: " + e);
-          return null;
-      }
-    }
-
-    /**
      * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
@@ -520,106 +479,6 @@
     }
 
     /**
-     * Interface for BLE advertising callback.
-     *
-     * @hide
-     */
-    public interface AdvertiseCallback {
-        /**
-         * Callback when advertise starts.
-         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
-         */
-        void onAdvertiseStart(int status);
-        /**
-         * Callback when advertise stops.
-         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
-         */
-        void onAdvertiseStop(int status);
-    }
-
-    /**
-     * Start BLE advertising using current {@link BluetoothAdvScanData}.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
-     *
-     * @param callback - {@link AdvertiseCallback}
-     * @return true if BLE advertising succeeds, false otherwise.
-     * @hide
-     */
-    public boolean startAdvertising(final AdvertiseCallback callback) {
-        if (getState() != STATE_ON) return false;
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported.
-                return false;
-            }
-            // Restart/reset advertising packets if advertising is in progress.
-            if (isAdvertising()) {
-                // Invalid advertising callback.
-                if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
-                    Log.e(TAG, "failed to restart advertising, invalid callback");
-                    return false;
-                }
-                iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
-                // Run the callback from main thread.
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        // callback with status success.
-                        callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
-                    }
-                });
-                return true;
-            }
-            UUID uuid = UUID.randomUUID();
-            GattCallbackWrapper wrapper =
-                new GattCallbackWrapper(this, null, null, callback);
-            iGatt.registerClient(new ParcelUuid(uuid), wrapper);
-            if (!wrapper.advertiseStarted()) {
-                return false;
-            }
-            synchronized (mLock) {
-                mAdvertisingGattCallback = wrapper;
-            }
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
-     * Stop BLE advertising. The callback has to be the same one used for start advertising.
-     *
-     * @param callback - {@link AdvertiseCallback}
-     * @return true if BLE advertising stops, false otherwise.
-     * @hide
-     */
-    public boolean stopAdvertising(AdvertiseCallback callback) {
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            if (iGatt == null) {
-                // BLE is not supported
-                return false;
-            }
-            if (mAdvertisingGattCallback == null) {
-                // no callback.
-                return false;
-            }
-            // Make sure same callback is used for start and stop advertising.
-            if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
-                Log.e(TAG, "must use the same callback for star/stop advertising");
-                return false;
-            }
-            mAdvertisingGattCallback.stopAdvertising();
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
-        }
-    }
-
-    /**
      * Return true if Bluetooth is currently enabled and ready for use.
      * <p>Equivalent to:
      * <code>getBluetoothState() == STATE_ON</code>
@@ -1076,23 +935,6 @@
     }
 
     /**
-     * Returns whether BLE is currently advertising.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
-     *
-     * @hide
-     */
-    public boolean isAdvertising() {
-        if (getState() != STATE_ON) return false;
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            return iGatt.isAdvertising();
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        }
-        return false;
-    }
-
-    /**
      * Return the set of {@link BluetoothDevice} objects that are bonded
      * (paired) to the local adapter.
      * <p>If Bluetooth state is not {@link #STATE_ON}, this API
@@ -1537,8 +1379,6 @@
                 if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
                 synchronized (mManagerCallback) {
                     mService = null;
-                    // Reset bluetooth adv scan data when Gatt service is down.
-                    mBluetoothAdvScanData = null;
                     for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
                         try {
                             if (cb != null) {
@@ -1822,7 +1662,6 @@
         private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
         private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
 
-        private final AdvertiseCallback mAdvertiseCallback;
         private final LeScanCallback mLeScanCb;
 
         // mLeHandle 0: not registered
@@ -1838,27 +1677,12 @@
             mLeScanCb = leScanCb;
             mScanFilter = uuid;
             mLeHandle = 0;
-            mAdvertiseCallback = null;
-        }
-
-        public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
-            UUID[] uuid, AdvertiseCallback callback) {
-          mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
-          mLeScanCb = leScanCb;
-          mScanFilter = uuid;
-          mLeHandle = 0;
-          mAdvertiseCallback = callback;
         }
 
         public boolean scanStarted() {
             return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
         }
 
-        public boolean advertiseStarted() {
-            // Wait for registeration callback.
-            return waitForRegisteration(1);
-        }
-
         private boolean waitForRegisteration(int maxWaitCount) {
             boolean started = false;
             synchronized(this) {
@@ -1878,27 +1702,6 @@
             return started;
         }
 
-        public void stopAdvertising() {
-            synchronized (this) {
-                if (mLeHandle <= 0) {
-                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
-                    return;
-                }
-                BluetoothAdapter adapter = mBluetoothAdapter.get();
-                if (adapter != null) {
-                    try {
-                        IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
-                        iGatt.stopAdvertising();
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to stop advertising" + e);
-                    }
-                } else {
-                    Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
-                }
-                notifyAll();
-            }
-        }
-
         public void stopLeScan() {
             synchronized(this) {
                 if (mLeHandle <= 0) {
@@ -1940,18 +1743,14 @@
                         BluetoothAdapter adapter = mBluetoothAdapter.get();
                         if (adapter != null) {
                             iGatt = adapter.getBluetoothManager().getBluetoothGatt();
-                            if (mAdvertiseCallback != null) {
-                                iGatt.startAdvertising(mLeHandle);
+                            if (mScanFilter == null) {
+                                iGatt.startScan(mLeHandle, false);
                             } else {
-                              if (mScanFilter == null) {
-                                  iGatt.startScan(mLeHandle, false);
-                              } else {
-                                  ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
-                                  for(int i = 0; i != uuids.length; ++i) {
-                                      uuids[i] = new ParcelUuid(mScanFilter[i]);
-                                  }
-                                  iGatt.startScanWithUuids(mLeHandle, false, uuids);
-                              }
+                                ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
+                                for(int i = 0; i != uuids.length; ++i) {
+                                    uuids[i] = new ParcelUuid(mScanFilter[i]);
+                                }
+                                iGatt.startScanWithUuids(mLeHandle, false, uuids);
                             }
                         } else {
                             Log.e(TAG, "onClientRegistered, BluetoothAdapter null");
@@ -2080,44 +1879,6 @@
         }
 
         public void onAdvertiseStateChange(int advertiseState, int status) {
-            Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
-            if (advertiseState == STATE_ADVERTISE_STARTED) {
-                if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                    mAdvertiseCallback.onAdvertiseStart(status);
-                } else {
-                    // If status is unsuccessful and advertise state is started, it means stop
-                    // advertising fails.
-                    mAdvertiseCallback.onAdvertiseStop(status);
-                }
-            } else {
-                synchronized (this) {
-                    if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                        BluetoothAdapter adapter = mBluetoothAdapter.get();
-                        if (adapter != null) {
-                            try {
-                                IBluetoothGatt iGatt =
-                                        adapter.getBluetoothManager().getBluetoothGatt();
-                                Log.d(TAG, "unregistering client " + mLeHandle);
-                                iGatt.unregisterClient(mLeHandle);
-                                // Reset advertise app handle.
-                                mLeHandle = -1;
-                                adapter.mAdvertisingGattCallback = null;
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Failed to unregister client" + e);
-                            }
-                        } else {
-                            Log.e(TAG, "cannot unregister client, BluetoothAdapter is null");
-                        }
-                    }
-                }
-                if (status == ADVERTISE_CALLBACK_SUCCESS) {
-                    mAdvertiseCallback.onAdvertiseStop(status);
-                } else{
-                    // If status is unsuccesful and advertise state is stopped, it means start
-                    // advertising fails.
-                    mAdvertiseCallback.onAdvertiseStart(status);
-                }
-            }
         }
 
         @Override
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
deleted file mode 100644
index df2c256..0000000
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.bluetooth;
-import android.os.ParcelUuid;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class provides the public APIs to set advertising and scan response data when BLE device
- * operates in peripheral mode. <br>
- * The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
- * @hide
- */
-public final class BluetoothAdvScanData {
-
-  /**
-   * Available data types of {@link BluetoothAdvScanData}.
-   */
-  public static final int AD = 0;  // Advertising Data
-  public static final int SCAN_RESPONSE = 1;  // Scan Response
-  public static final int EIR = 2;  // Extended Inquiry Response
-
-  private static final String TAG = "BluetoothAdvScanData";
-
-  /**
-   * Data type of BluetoothAdvScanData.
-   */
-  private final int mDataType;
-  /**
-   * Bluetooth Gatt Service.
-   */
-  private IBluetoothGatt mBluetoothGatt;
-
-  /**
-   * @param mBluetoothGatt
-   * @param dataType
-   */
-  public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) {
-    this.mBluetoothGatt = mBluetoothGatt;
-    this.mDataType = dataType;
-  }
-
-  /**
-   * @return advertising data type.
-   */
-  public int getDataType() {
-    return mDataType;
-  }
-
-  /**
-   * Set manufactureCode and manufactureData.
-   * Returns true if manufacturer data is set, false if there is no enough room to set
-   * manufacturer data or the data is already set.
-   * @param manufacturerCode - unique identifier for the manufacturer
-   * @param manufacturerData - data associated with the specific manufacturer.
-   */
-  public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) {
-    if (mDataType != AD) return false;
-    try {
-      return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to set manufacturer id and data.", e);
-      return false;
-    }
-  }
-
-  /**
-   * Set service data.  Note the service data can only be set when the data type is {@code AD};
-   * @param serviceData
-   */
-  public boolean setServiceData(byte[] serviceData) {
-
-    if (mDataType != AD) return false;
-    if (serviceData == null) return false;
-    try {
-      return mBluetoothGatt.setAdvServiceData(serviceData);
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to set service data.", e);
-      return false;
-    }
-  }
-
-  /**
-   * Returns an immutable list of service uuids that will be advertised.
-   */
-  public List<ParcelUuid> getServiceUuids() {
-    try {
-      return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get service uuids.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Returns manufacturer data.
-   */
-  public byte[] getManufacturerData() {
-    if (mBluetoothGatt == null) return null;
-    try {
-      return mBluetoothGatt.getAdvManufacturerData();
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get manufacturer data.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Returns service data.
-   */
-  public byte[] getServiceData() {
-    if (mBluetoothGatt == null) return null;
-    try {
-      return mBluetoothGatt.getAdvServiceData();
-    } catch (RemoteException e) {
-      Log.e(TAG, "Unable to get service data.", e);
-      return null;
-    }
-  }
-
-  /**
-   * Remove manufacturer data based on given manufacturer code.
-   * @param manufacturerCode
-   */
-  public void removeManufacturerCodeAndData(int manufacturerCode) {
-    if (mBluetoothGatt != null) {
-      try {
-        mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
-      } catch (RemoteException e) {
-        Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
-      }
-    }
-  }
-}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 0f0eee6..6d4b9cd 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -48,15 +48,6 @@
     void unregisterClient(in int clientIf);
     void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
     void clientDisconnect(in int clientIf, in String address);
-    void startAdvertising(in int appIf);
-    void stopAdvertising();
-    boolean setAdvServiceData(in byte[] serviceData);
-    byte[] getAdvServiceData();
-    boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData);
-    byte[] getAdvManufacturerData();
-    List<ParcelUuid> getAdvServiceUuids();
-    void removeAdvManufacturerCodeAndData(int manufacturerCode);
-    boolean isAdvertising();
     void refreshDevice(in int clientIf, in String address);
     void discoverServices(in int clientIf, in String address);
     void readCharacteristic(in int clientIf, in String address, in int srvcType,
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index af218eb..18e3f54 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -64,7 +64,6 @@
                              in int charInstId, in ParcelUuid charUuid,
                              in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
-    void onAdvertiseStateChange(in int advertiseState, in int status);
     void onMultiAdvertiseCallback(in int status);
     void onConfigureMTU(in String address, in int mtu, in int status);
     void onConnectionCongested(in String address, in boolean congested);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index e232512..af79fcc 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -332,11 +332,6 @@
         }
 
         @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
         public void onMultiAdvertiseCallback(int status) {
             // TODO: This logic needs to be re-visited to account
             //       for whether the scan has actually been started
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 7e87edc..220aa77 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -409,11 +409,6 @@
         }
 
         @Override
-        public void onAdvertiseStateChange(int advertiseState, int status) {
-            // no op
-        }
-
-        @Override
         public void onMultiAdvertiseCallback(int status) {
             // no op
         }
diff --git a/core/java/android/content/AbstractRestrictionsProvider.java b/core/java/android/content/AbstractRestrictionsProvider.java
index 1119478..3272970 100644
--- a/core/java/android/content/AbstractRestrictionsProvider.java
+++ b/core/java/android/content/AbstractRestrictionsProvider.java
@@ -16,78 +16,65 @@
 
 package android.content;
 
-import android.app.Service;
 import android.app.admin.DevicePolicyManager;
 import android.os.Bundle;
 import android.os.IBinder;
 
 /**
- * Abstract implementation of a Restrictions Provider Service. To implement a Restrictions Provider,
- * extend from this class and implement the abstract methods. Export this service in the
- * manifest. A profile owner device admin can then register this component as a Restrictions
- * Provider using {@link DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)}.
+ * Abstract implementation of a Restrictions Provider BroadcastReceiver. To implement a
+ * Restrictions Provider, extend from this class and implement the abstract methods.
+ * Export this receiver in the manifest. A profile owner device admin can then register this
+ * component as a Restrictions Provider using
+ * {@link DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName)}.
  * <p>
  * The function of a Restrictions Provider is to transport permission requests from apps on this
  * device to an administrator (most likely on a remote device or computer) and deliver back
  * responses. The response should be sent back to the app via
  * {@link RestrictionsManager#notifyPermissionResponse(String, Bundle)}.
- * <p>
- * Apps can also query previously received responses using
- * {@link #getPermissionResponse(String, String)}. The period for which previously received
- * responses are available is left to the implementation of the Restrictions Provider.
+ *
+ * @see RestrictionsManager
  */
-public abstract class AbstractRestrictionsProvider extends Service {
+public abstract class AbstractRestrictionsProvider extends BroadcastReceiver {
 
     private static final String TAG = "AbstractRestrictionsProvider";
 
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return new RestrictionsProviderWrapper().asBinder();
-    }
-
-    /**
-     * Checks to see if there is a response for a prior request and returns the response bundle if
-     * it exists. If there is no response yet or if the request is not known, the returned bundle
-     * should contain the response code in {@link RestrictionsManager#RESPONSE_KEY_RESULT}.
-     *
-     * @param packageName the application that is requesting a permission response.
-     * @param requestId the id of the request for which the response is needed.
-     * @return a bundle containing at a minimum the result of the request. It could contain other
-     * optional information such as error codes and cookies.
-     *
-     * @see RestrictionsManager#RESPONSE_KEY_RESULT
-     */
-    public abstract Bundle getPermissionResponse(String packageName, String requestId);
-
     /**
      * An asynchronous permission request made by an application for an operation that requires
      * authorization by a local or remote administrator other than the user. The Restrictions
-     * Provider must transfer the request to the administrator and deliver back a response, when
+     * Provider should transfer the request to the administrator and deliver back a response, when
      * available. The calling application is aware that the response could take an indefinite
      * amount of time.
+     * <p>
+     * If the request bundle contains the key {@link RestrictionsManager#REQUEST_KEY_NEW_REQUEST},
+     * then a new request must be sent. Otherwise the provider can look up any previous response
+     * to the same requestId and return the cached response.
      *
      * @param packageName the application requesting permission.
      * @param requestType the type of request, which determines the content and presentation of
      * the request data.
      * @param request the request data bundle containing at a minimum a request id.
      *
-     * @see RestrictionsManager#REQUEST_TYPE_QUESTION
+     * @see RestrictionsManager#REQUEST_TYPE_APPROVAL
      * @see RestrictionsManager#REQUEST_TYPE_LOCAL_APPROVAL
      * @see RestrictionsManager#REQUEST_KEY_ID
      */
-    public abstract void requestPermission(String packageName, String requestType, Bundle request);
+    public abstract void requestPermission(Context context,
+            String packageName, String requestType, Bundle request);
 
-    private class RestrictionsProviderWrapper extends IRestrictionsProvider.Stub {
+    /**
+     * Intercept standard Restrictions Provider broadcasts.  Implementations
+     * should not override this method; it is better to implement the
+     * convenience callbacks for each action.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
 
-        @Override
-        public Bundle getPermissionResponse(String packageName, String requestId) {
-            return AbstractRestrictionsProvider.this
-                    .getPermissionResponse(packageName, requestId);
-        }
-
-        @Override
-        public void requestPermission(String packageName, String templateId, Bundle request) {
-            AbstractRestrictionsProvider.this.requestPermission(packageName, templateId, request);
+        if (RestrictionsManager.ACTION_REQUEST_PERMISSION.equals(action)) {
+            String packageName = intent.getStringExtra(RestrictionsManager.EXTRA_PACKAGE_NAME);
+            String requestType = intent.getStringExtra(RestrictionsManager.EXTRA_REQUEST_TYPE);
+            Bundle request = intent.getBundleExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE);
+            requestPermission(context, packageName, requestType, request);
         }
     }
 }
diff --git a/core/java/android/content/IPermissionResponseCallback.aidl b/core/java/android/content/IPermissionResponseCallback.aidl
deleted file mode 100644
index 8309768..0000000
--- a/core/java/android/content/IPermissionResponseCallback.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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.content;
-
-import android.os.Bundle;
-
-/**
- * Callback for permission response queries.
- *
- * @hide
- */
- interface IPermissionResponseCallback {
-
-    void onResponse(in Bundle response);
-
-}
diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl
index 49eb65b..b1c0a3a 100644
--- a/core/java/android/content/IRestrictionsManager.aidl
+++ b/core/java/android/content/IRestrictionsManager.aidl
@@ -17,7 +17,6 @@
 package android.content;
 
 import android.os.Bundle;
-import android.content.IPermissionResponseCallback;
 
 /**
  * Interface used by the RestrictionsManager
@@ -28,6 +27,4 @@
     boolean hasRestrictionsProvider();
     void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData);
     void notifyPermissionResponse(in String packageName, in Bundle response);
-    void getPermissionResponse(in String packageName, in String requestId,
-            in IPermissionResponseCallback callback);
 }
diff --git a/core/java/android/content/IRestrictionsProvider.aidl b/core/java/android/content/IRestrictionsProvider.aidl
deleted file mode 100644
index 4506b72..0000000
--- a/core/java/android/content/IRestrictionsProvider.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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.content;
-
-import android.os.Bundle;
-
-/**
- * Interface to a restrictions provider service component.
- *
- * @hide
- */
- interface IRestrictionsProvider {
-
-    void requestPermission(in String packageName, in String requestType, in Bundle requestBundle);
-    Bundle getPermissionResponse(in String packageName, in String requestId);
-
-}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4153a02..5bf8a97 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3809,17 +3809,6 @@
      * {@link android.R.styleable#AndroidManifestActivity_autoRemoveFromRecents}.
      */
     public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 0x00002000;
-    /**
-     * If set along with FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
-     * presented to the user but will instead be only available through the recents task list.
-     * In addition, the new task wil be affiliated with the launching activity's task.
-     * Affiliated tasks are grouped together in the recents task list.
-     *
-     * <p>This behavior is not supported for activities with {@link
-     * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of
-     * <code>singleInstance</code> or <code>singleTask</code>.
-     */
-    public static final int FLAG_ACTIVITY_LAUNCH_BEHIND = 0x00001000;
 
     /**
      * If set, when sending a broadcast only registered receivers will be
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 5ef2dbc..fa069a2 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -32,21 +32,17 @@
  * <p>
  * Apps can expose a set of restrictions via an XML file specified in the manifest.
  * <p>
- * If the user has an active restrictions provider, dynamic requests can be made in
+ * If the user has an active Restrictions Provider, dynamic requests can be made in
  * addition to the statically imposed restrictions. Dynamic requests are app-specific
  * and can be expressed via a predefined set of request types.
  * <p>
  * The RestrictionsManager forwards the dynamic requests to the active
- * restrictions provider. The restrictions provider can respond back to requests by calling
+ * Restrictions Provider. The Restrictions Provider can respond back to requests by calling
  * {@link #notifyPermissionResponse(String, Bundle)}, when
  * a response is received from the administrator of the device or user.
  * The response is relayed back to the application via a protected broadcast,
  * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}.
  * <p>
- * Prior responses to requests can also be queried through calls to
- * {@link #getPermissionResponse(String, PermissionResponseCallback)}, if the provider
- * saves old responses.
- * <p>
  * Static restrictions are specified by an XML file referenced by a meta-data attribute
  * in the manifest. This enables applications as well as any web administration consoles
  * to be able to read the list of available restrictions from the apk.
@@ -72,10 +68,8 @@
     private static final String TAG = "RestrictionsManager";
 
     /**
-     * Broadcast intent delivered when a response is received for a permission
-     * request. The response is not available for later query, so the receiver
-     * must persist and/or immediately act upon the response. The application
-     * should not interrupt the user by coming to the foreground if it isn't
+     * Broadcast intent delivered when a response is received for a permission request. The
+     * application should not interrupt the user by coming to the foreground if it isn't
      * currently in the foreground. It can either post a notification informing
      * the user of the response or wait until the next time the user launches the app.
      * <p>
@@ -89,9 +83,32 @@
             "android.intent.action.PERMISSION_RESPONSE_RECEIVED";
 
     /**
+     * Broadcast intent sent to the Restrictions Provider to handle a permission request from
+     * an app. It will have the following extras: {@link #EXTRA_PACKAGE_NAME},
+     * {@link #EXTRA_REQUEST_TYPE} and {@link #EXTRA_REQUEST_BUNDLE}. The Restrictions Provider
+     * will handle the request and respond back to the RestrictionsManager, when a response is
+     * available, by calling {@link #notifyPermissionResponse}.
+     * <p>
+     * The BroadcastReceiver must require the {@link android.Manifest.permission#BIND_DEVICE_ADMIN}
+     * permission to ensure that only the system can send the broadcast.
+     */
+    public static final String ACTION_REQUEST_PERMISSION =
+            "android.content.action.REQUEST_PERMISSION";
+
+    /**
      * The package name of the application making the request.
      */
-    public static final String EXTRA_PACKAGE_NAME = "package_name";
+    public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+
+    /**
+     * The request type passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     */
+    public static final String EXTRA_REQUEST_TYPE = "android.content.extra.REQUEST_TYPE";
+
+    /**
+     * The request bundle passed in the {@link #ACTION_REQUEST_PERMISSION} broadcast.
+     */
+    public static final String EXTRA_REQUEST_BUNDLE = "android.content.extra.REQUEST_BUNDLE";
 
     /**
      * Contains a response from the administrator for specific request.
@@ -101,7 +118,7 @@
      * <li>{@link #RESPONSE_KEY_RESULT}: The response result.</li>
      * </ul>
      */
-    public static final String EXTRA_RESPONSE_BUNDLE = "response";
+    public static final String EXTRA_RESPONSE_BUNDLE = "android.content.extra.RESPONSE_BUNDLE";
 
     /**
      * Request type for a simple question, with a possible title and icon.
@@ -113,7 +130,7 @@
      * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE},
      * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}.
      */
-    public static final String REQUEST_TYPE_QUESTION = "android.request.type.question";
+    public static final String REQUEST_TYPE_APPROVAL = "android.request.type.approval";
 
     /**
      * Request type for a local password challenge. This is a way for an app to ask
@@ -204,22 +221,14 @@
     public static final String REQUEST_KEY_DENY_LABEL = "android.request.deny_label";
 
     /**
-     * Key for requestor's name contained in the request bundle. This value is not specified by
-     * the application. It is automatically inserted into the Bundle by the Restrictions Provider
-     * before it is sent to the administrator.
+     * Key for issuing a new request, contained in the request bundle. If this is set to true,
+     * the Restrictions Provider must make a new request. If it is false or not specified, then
+     * the Restrictions Provider can return a cached response that has the same requestId, if
+     * available. If there's no cached response, it will issue a new one to the administrator.
      * <p>
-     * Type: String
+     * Type: boolean
      */
-    public static final String REQUEST_KEY_REQUESTOR_NAME = "android.request.requestor";
-
-    /**
-     * Key for requestor's device name contained in the request bundle. This value is not specified
-     * by the application. It is automatically inserted into the Bundle by the Restrictions Provider
-     * before it is sent to the administrator.
-     * <p>
-     * Type: String
-     */
-    public static final String REQUEST_KEY_DEVICE_NAME = "android.request.device";
+    public static final String REQUEST_KEY_NEW_REQUEST = "android.request.new_request";
 
     /**
      * Key for the response in the response bundle sent to the application, for a permission
@@ -249,8 +258,8 @@
     public static final int RESULT_NO_RESPONSE = 3;
 
     /**
-     * Response result value indicating that the request is unknown, when returned through a
-     * call to #getPendingResponse
+     * Response result value indicating that the request is unknown, when it's not a new
+     * request.
      */
     public static final int RESULT_UNKNOWN_REQUEST = 4;
 
@@ -312,19 +321,6 @@
     private final IRestrictionsManager mService;
 
     /**
-     * Callback object for returning a response for a request.
-     *
-     * @see #getPermissionResponse
-     */
-    public static abstract class PermissionResponseCallback {
-        /**
-         * Contains the response
-         * @param response
-         */
-        public abstract void onResponse(Bundle response);
-    }
-
-    /**
      * @hide
      */
     public RestrictionsManager(Context context, IRestrictionsManager service) {
@@ -350,11 +346,10 @@
     }
 
     /**
-     * Called by an application to check if there is an active restrictions provider. If
-     * there isn't, {@link #getPermissionResponse(String, PermissionResponseCallback)}
-     * and {@link #requestPermission(String, Bundle)} are not available.
+     * Called by an application to check if there is an active Restrictions Provider. If
+     * there isn't, {@link #requestPermission(String, Bundle)} is not available.
      *
-     * @return whether there is an active restrictions provider.
+     * @return whether there is an active Restrictions Provider.
      */
     public boolean hasRestrictionsProvider() {
         try {
@@ -374,13 +369,24 @@
      *
      * @param requestType The type of request. The type could be one of the
      * predefined types specified here or a custom type that the specific
-     * restrictions provider might understand. For custom types, the type name should be
+     * Restrictions Provider might understand. For custom types, the type name should be
      * namespaced to avoid collisions with predefined types and types specified by
-     * other restrictions providers.
+     * other Restrictions Providers.
      * @param request A Bundle containing the data corresponding to the specified request
      * type. The keys for the data in the bundle depend on the request type.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
      */
     public void requestPermission(String requestType, Bundle request) {
+        if (requestType == null) {
+            throw new NullPointerException("requestType cannot be null");
+        }
+        if (request == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        if (!request.containsKey(REQUEST_KEY_ID)) {
+            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
+        }
         try {
             if (mService != null) {
                 mService.requestPermission(mContext.getPackageName(), requestType, request);
@@ -391,41 +397,27 @@
     }
 
     /**
-     * Called by an application to query for any available response from the restrictions provider
-     * for the given requestId. The call returns immediately and the response will be returned
-     * via the provided callback. This does not initiate a new request and does not wait
-     * for a response to be received. It merely returns any previously received response
-     * or indicates if there was no available response. If there are multiple responses
-     * available for the same request ID, the most recent one is returned.
+     * Called by the Restrictions Provider to deliver a response to an application.
      *
-     * @param requestId The ID of the original request made via
-     * {@link #requestPermission(String, Bundle)}. It's possible to also query for responses
-     * to requests made on a different device with the same requestId, if the Restrictions
-     * Provider happens to sync responses across devices with the same account managed by the
-     * restrictions provider.
-     * @param callback The response is returned via the callback object. Cannot be null.
-     */
-    public void getPermissionResponse(String requestId, PermissionResponseCallback callback) {
-        if (requestId == null || callback == null) {
-            throw new NullPointerException("requestId or callback cannot be null");
-        }
-        try {
-            if (mService != null) {
-                mService.getPermissionResponse(mContext.getPackageName(), requestId,
-                        new PermissionResponseCallbackWrapper(callback));
-            }
-        } catch (RemoteException re) {
-            Log.w(TAG, "Couldn't reach service");
-        }
-    }
-
-    /**
-     * Called by the restrictions provider to deliver a response to an application.
-     *
-     * @param packageName the application to deliver the response to.
+     * @param packageName the application to deliver the response to. Cannot be null.
      * @param response the Bundle containing the response status, request ID and other information.
+     *                 Cannot be null.
+     *
+     * @throws IllegalArgumentException if any of the required parameters are missing.
      */
     public void notifyPermissionResponse(String packageName, Bundle response) {
+        if (packageName == null) {
+            throw new NullPointerException("packageName cannot be null");
+        }
+        if (response == null) {
+            throw new NullPointerException("request cannot be null");
+        }
+        if (!response.containsKey(REQUEST_KEY_ID)) {
+            throw new IllegalArgumentException("REQUEST_KEY_ID must be specified");
+        }
+        if (!response.containsKey(RESPONSE_KEY_RESULT)) {
+            throw new IllegalArgumentException("RESPONSE_KEY_RESULT must be specified");
+        }
         try {
             if (mService != null) {
                 mService.notifyPermissionResponse(packageName, response);
@@ -447,19 +439,4 @@
         // TODO:
         return null;
     }
-
-    private static class PermissionResponseCallbackWrapper
-            extends IPermissionResponseCallback.Stub {
-
-        private PermissionResponseCallback mCallback;
-
-        PermissionResponseCallbackWrapper(PermissionResponseCallback callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onResponse(Bundle response) {
-            mCallback.onResponse(response);
-        }
-    }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ae59bbc..a61d771 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -80,7 +80,7 @@
     void removeWindowToken(IBinder token);
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges, boolean voiceInteraction);
+            int configChanges, boolean voiceInteraction, boolean launchTaskBehind);
     void setAppGroupId(IBinder token, int groupId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 0e22174..f65aab5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -90,12 +90,12 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
+	android_graphics_Canvas.cpp \
 	android_graphics_Picture.cpp \
 	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
 	android/graphics/Camera.cpp \
-	android/graphics/Canvas.cpp \
 	android/graphics/CanvasProperty.cpp \
 	android/graphics/ColorFilter.cpp \
 	android/graphics/DrawFilter.cpp \
@@ -122,6 +122,7 @@
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
+	android/graphics/SkiaCanvas.cpp \
 	android/graphics/SurfaceTexture.cpp \
 	android/graphics/Typeface.cpp \
 	android/graphics/TypefaceImpl.cpp \
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index d17f46c..9f832b0 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,6 +3,7 @@
 
 #include "SkCamera.h"
 
+#include "Canvas.h"
 #include "GraphicsJNI.h"
 
 static jfieldID gNativeInstanceFieldID;
@@ -95,10 +96,10 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas((SkCanvas*)native_canvas);
+    v->applyToCanvas(canvas);
 }
 
 static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
deleted file mode 100644
index 6254f3d..0000000
--- a/core/jni/android/graphics/Canvas.cpp
+++ /dev/null
@@ -1,1330 +0,0 @@
-/*
- * Copyright (C) 2006-2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "SkCanvas.h"
-#include "SkClipStack.h"
-#include "SkDevice.h"
-#include "SkDeque.h"
-#include "SkDrawFilter.h"
-#include "SkGraphics.h"
-#include <SkImageInfo.h>
-#include "SkPorterDuff.h"
-#include "SkShader.h"
-#include "SkTArray.h"
-#include "SkTemplates.h"
-
-#include <minikin/Layout.h>
-#include "MinikinSkia.h"
-#include "MinikinUtils.h"
-
-#include "TypefaceImpl.h"
-
-#include "unicode/ubidi.h"
-#include "unicode/ushape.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-class ClipCopier : public SkCanvas::ClipVisitor {
-public:
-    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
-
-    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRect(rect, op, antialias);
-    }
-    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipRRect(rrect, op, antialias);
-    }
-    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
-        m_dstCanvas->clipPath(path, op, antialias);
-    }
-
-private:
-    SkCanvas* m_dstCanvas;
-};
-
-// Holds an SkCanvas reference plus additional native data.
-class NativeCanvasWrapper {
-private:
-    struct SaveRec {
-        int                 saveCount;
-        SkCanvas::SaveFlags saveFlags;
-    };
-
-public:
-    NativeCanvasWrapper(SkCanvas* canvas)
-        : mCanvas(canvas)
-        , mSaveStack(NULL) {
-        SkASSERT(canvas);
-    }
-
-    ~NativeCanvasWrapper() {
-        delete mSaveStack;
-    }
-
-    SkCanvas* getCanvas() const {
-        return mCanvas.get();
-    }
-
-    void setCanvas(SkCanvas* canvas) {
-        SkASSERT(canvas);
-        mCanvas.reset(canvas);
-
-        delete mSaveStack;
-        mSaveStack = NULL;
-    }
-
-    int save(SkCanvas::SaveFlags flags) {
-        int count = mCanvas->save();
-        recordPartialSave(flags);
-        return count;
-    }
-
-    int saveLayer(const SkRect* bounds, const SkPaint* paint,
-                            SkCanvas::SaveFlags flags) {
-        int count = mCanvas->saveLayer(bounds, paint,
-                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
-        recordPartialSave(flags);
-        return count;
-    }
-
-    int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
-                       SkCanvas::SaveFlags flags) {
-        int count = mCanvas->saveLayerAlpha(bounds, alpha,
-                static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
-        recordPartialSave(flags);
-        return count;
-    }
-
-    void restore() {
-        const SaveRec* rec = (NULL == mSaveStack)
-                ? NULL
-                : static_cast<SaveRec*>(mSaveStack->back());
-        int currentSaveCount = mCanvas->getSaveCount() - 1;
-        SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
-        if (NULL == rec || rec->saveCount != currentSaveCount) {
-            // Fast path - no record for this frame.
-            mCanvas->restore();
-            return;
-        }
-
-        bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
-        bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
-
-        SkMatrix savedMatrix;
-        if (preserveMatrix) {
-            savedMatrix = mCanvas->getTotalMatrix();
-        }
-
-        SkTArray<SkClipStack::Element> savedClips;
-        if (preserveClip) {
-            saveClipsForFrame(savedClips, currentSaveCount);
-        }
-
-        mCanvas->restore();
-
-        if (preserveMatrix) {
-            mCanvas->setMatrix(savedMatrix);
-        }
-
-        if (preserveClip && !savedClips.empty()) {
-            applyClips(savedClips);
-        }
-
-        mSaveStack->pop_back();
-    }
-
-private:
-    void recordPartialSave(SkCanvas::SaveFlags flags) {
-        // A partial save is a save operation which doesn't capture the full canvas state.
-        // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
-
-        // Mask-out non canvas state bits.
-        flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
-
-        if (SkCanvas::kMatrixClip_SaveFlag == flags) {
-            // not a partial save.
-            return;
-        }
-
-        if (NULL == mSaveStack) {
-            mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
-        }
-
-        SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
-        // Store the save counter in the SkClipStack domain.
-        // (0-based, equal to the number of save ops on the stack).
-        rec->saveCount = mCanvas->getSaveCount() - 1;
-        rec->saveFlags = flags;
-    }
-
-    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
-                           int frameSaveCount) {
-        SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
-                                       SkClipStack::Iter::kTop_IterStart);
-        while (const SkClipStack::Element* elem = clipIterator.next()) {
-            if (elem->getSaveCount() < frameSaveCount) {
-                // done with the current frame.
-                break;
-            }
-            SkASSERT(elem->getSaveCount() == frameSaveCount);
-            clips.push_back(*elem);
-        }
-    }
-
-    void applyClips(const SkTArray<SkClipStack::Element>& clips) {
-        ClipCopier clipCopier(mCanvas);
-
-        // The clip stack stores clips in device space.
-        SkMatrix origMatrix = mCanvas->getTotalMatrix();
-        mCanvas->resetMatrix();
-
-        // We pushed the clips in reverse order.
-        for (int i = clips.count() - 1; i >= 0; --i) {
-            clips[i].replay(&clipCopier);
-        }
-
-        mCanvas->setMatrix(origMatrix);
-    }
-
-    SkAutoTUnref<SkCanvas> mCanvas;
-    SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
-};
-
-// Returns true if the SkCanvas's clip is non-empty.
-static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
-    bool emptyClip = canvas.isClipEmpty();
-    return emptyClip ? JNI_FALSE : JNI_TRUE;
-}
-
-class SkCanvasGlue {
-public:
-    // Get the native wrapper for a given handle.
-    static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
-        SkASSERT(nativeHandle);
-        return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
-    }
-
-    // Get the SkCanvas for a given native handle.
-    static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
-        SkCanvas* canvas = wrapper->getCanvas();
-        SkASSERT(canvas);
-
-        return canvas;
-    }
-
-    // Construct an SkCanvas from the bitmap.
-    static SkCanvas* createCanvas(SkBitmap* bitmap) {
-        if (bitmap) {
-            return SkNEW_ARGS(SkCanvas, (*bitmap));
-        }
-
-        // Create an empty bitmap device to prevent callers from crashing
-        // if they attempt to draw into this canvas.
-        SkBitmap emptyBitmap;
-        return new SkCanvas(emptyBitmap);
-    }
-
-    // Copy the canvas matrix & clip state.
-    static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
-        if (srcCanvas && dstCanvas) {
-            dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
-            if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
-                ClipCopier copier(dstCanvas);
-                srcCanvas->replayClips(&copier);
-            }
-        }
-    }
-
-    // Native JNI handlers
-    static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) {
-        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
-        delete wrapper;
-    }
-
-    // Native wrapper constructor used by Canvas(Bitmap)
-    static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
-        // No check - 0 is a valid bitmapHandle.
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkCanvas* canvas = createCanvas(bitmap);
-
-        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
-    }
-
-    // Native wrapper constructor used by Canvas(native_canvas)
-    static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle);
-        return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas));
-    }
-
-    // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
-    // optionally copying canvas matrix & clip state.
-    static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
-                          jboolean copyState) {
-        NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle);
-        SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle));
-        NPE_CHECK_RETURN_VOID(env, newCanvas);
-
-        if (copyState == JNI_TRUE) {
-            copyCanvasState(wrapper->getCanvas(), newCanvas);
-        }
-
-        // setCanvas() unrefs the old canvas.
-        wrapper->setCanvas(newCanvas);
-    }
-
-    static void freeCaches(JNIEnv* env, jobject) {
-        SkGraphics::PurgeFontCache();
-    }
-
-    static void freeTextLayoutCaches(JNIEnv* env, jobject) {
-        Layout::purgeCaches();
-    }
-
-    static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        int width = canvas->getDevice()->accessBitmap(false).width();
-        return static_cast<jint>(width);
-    }
-
-    static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        int height = canvas->getDevice()->accessBitmap(false).height();
-        return static_cast<jint>(height);
-    }
-
-    static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        return static_cast<jint>(wrapper->save(flags));
-    }
-
-    static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
-                          jfloat l, jfloat t, jfloat r, jfloat b,
-                          jlong paintHandle, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        SkRect bounds;
-        bounds.set(l, t, r, b);
-        return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
-    }
-
-    static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
-                               jfloat l, jfloat t, jfloat r, jfloat b,
-                               jint alpha, jint flagsHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
-        SkRect  bounds;
-        bounds.set(l, t, r, b);
-        return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
-    }
-
-    static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        if (wrapper->getCanvas()->getSaveCount() <= 1) {  // cannot restore anymore
-            doThrowISE(env, "Underflow in restore");
-            return;
-        }
-        wrapper->restore();
-    }
-
-    static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
-        return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
-    }
-
-    static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
-                               jint restoreCount) {
-        NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
-        if (restoreCount < 1) {
-            doThrowIAE(env, "Underflow in restoreToCount");
-            return;
-        }
-
-        while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
-            wrapper->restore();
-        }
-    }
-
-    static void translate(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat dx, jfloat dy) {
-        getNativeCanvas(canvasHandle)->translate(dx, dy);
-    }
-
-    static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat sx, jfloat sy) {
-        getNativeCanvas(canvasHandle)->scale(sx, sy);
-    }
-
-    static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
-                          jfloat degrees) {
-        getNativeCanvas(canvasHandle)->rotate(degrees);
-    }
-
-    static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
-                         jfloat sx, jfloat sy) {
-        getNativeCanvas(canvasHandle)->skew(sx, sy);
-    }
-
-    static void concat(JNIEnv* env, jobject, jlong canvasHandle,
-                       jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        canvas->concat(*matrix);
-    }
-
-    static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        if (NULL == matrix) {
-            canvas->resetMatrix();
-        } else {
-            canvas->setMatrix(*matrix);
-        }
-    }
-
-    static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
-                                  jfloat left, jfloat top, jfloat right,
-                                  jfloat bottom, jint op) {
-        SkRect  r;
-        r.set(left, top, right, bottom);
-        SkCanvas* c = getNativeCanvas(canvasHandle);
-        c->clipRect(r, static_cast<SkRegion::Op>(op));
-        return hasNonEmptyClip(*c);
-    }
-
-    static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong pathHandle, jint op) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle),
-                static_cast<SkRegion::Op>(op));
-        return hasNonEmptyClip(*canvas);
-    }
-
-    static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle,
-                               jlong deviceRgnHandle, jint op) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
-        SkPath rgnPath;
-        if (deviceRgn->getBoundaryPath(&rgnPath)) {
-            // The region is specified in device space.
-            SkMatrix savedMatrix = canvas->getTotalMatrix();
-            canvas->resetMatrix();
-            canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
-            canvas->setMatrix(savedMatrix);
-        } else {
-            canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
-        }
-        return hasNonEmptyClip(*canvas);
-    }
-
-    static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle,
-                              jlong filterHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
-    }
-
-    static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
-                                       jlong pathHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle));
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle,
-                                       jfloat left, jfloat top, jfloat right,
-                                       jfloat bottom) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRect r;
-        r.set(left, top, right, bottom);
-        bool result = canvas->quickReject(r);
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle,
-                        jint r, jint g, jint b) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawARGB(0xFF, r, g, b);
-    }
-
-    static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle,
-                         jint a, jint r, jint g, jint b) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawARGB(a, r, g, b);
-    }
-
-    static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle,
-                             jint color) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        canvas->drawColor(color);
-    }
-
-    static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle,
-                              jint color, jint modeHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
-        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
-    }
-
-    static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPaint(*paint);
-    }
-
-    static void doPoints(JNIEnv* env, jlong canvasHandle,
-                         jfloatArray jptsArray, jint offset, jint count,
-                         jlong paintHandle, jint modeHandle) {
-        NPE_CHECK_RETURN_VOID(env, jptsArray);
-        SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        AutoJavaFloatArray autoPts(env, jptsArray);
-        float* floats = autoPts.ptr();
-        const int length = autoPts.length();
-
-        if ((offset | count) < 0 || offset + count > length) {
-            doThrowAIOOBE(env);
-            return;
-        }
-
-        // now convert the floats into SkPoints
-        count >>= 1;    // now it is the number of points
-        SkAutoSTMalloc<32, SkPoint> storage(count);
-        SkPoint* pts = storage.get();
-        const float* src = floats + offset;
-        for (int i = 0; i < count; i++) {
-            pts[i].set(src[0], src[1]);
-            src += 2;
-        }
-        canvas->drawPoints(mode, count, pts, *paint);
-    }
-
-    static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
-                           jfloatArray jptsArray, jint offset,
-                           jint count, jlong paintHandle) {
-        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
-                 SkCanvas::kPoints_PointMode);
-    }
-
-    static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
-                          jfloatArray jptsArray, jint offset, jint count,
-                          jlong paintHandle) {
-        doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
-                 SkCanvas::kLines_PointMode);
-    }
-
-    static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
-                          jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPoint(x, y, *paint);
-    }
-
-    static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                    jfloat startX, jfloat startY, jfloat stopX,
-                                    jfloat stopY, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawLine(startX, startY, stopX, stopY, *paint);
-    }
-
-    static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                    jfloat left, jfloat top, jfloat right,
-                                    jfloat bottom, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawRectCoords(left, top, right, bottom, *paint);
-    }
-
-    static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-            jfloat right, jfloat bottom, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawOval(oval, *paint);
-    }
-
-    static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx,
-                           jfloat cy, jfloat radius, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawCircle(cx, cy, radius, *paint);
-    }
-
-    static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
-            jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle, jboolean useCenter,
-            jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawArc(oval, startAngle, sweepAngle, useCenter, *paint);
-    }
-
-    static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle,
-            jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry,
-            jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-        canvas->drawRoundRect(rect, rx, ry, *paint);
-    }
-
-    static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
-                         jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawPath(*path, *paint);
-    }
-
-    static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
-                                          jlong canvasHandle, jlong bitmapHandle,
-                                          jfloat left, jfloat top,
-                                          jlong paintHandle, jint canvasDensity,
-                                          jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        if (canvasDensity == bitmapDensity || canvasDensity == 0
-                || bitmapDensity == 0) {
-            if (screenDensity != 0 && screenDensity != bitmapDensity) {
-                SkPaint filteredPaint;
-                if (paint) {
-                    filteredPaint = *paint;
-                }
-                filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-                canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
-            } else {
-                canvas->drawBitmap(*bitmap, left, top, paint);
-            }
-        } else {
-            canvas->save();
-            SkScalar scale = canvasDensity / (float)bitmapDensity;
-            canvas->translate(left, top);
-            canvas->scale(scale, scale);
-
-            SkPaint filteredPaint;
-            if (paint) {
-                filteredPaint = *paint;
-            }
-            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-
-            canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
-
-            canvas->restore();
-        }
-    }
-
-    static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
-                        jobject srcIRect, const SkRect& dst, SkPaint* paint,
-                        jint screenDensity, jint bitmapDensity) {
-        SkIRect    src, *srcPtr = NULL;
-
-        if (NULL != srcIRect) {
-            GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
-            srcPtr = &src;
-        }
-
-        if (screenDensity != 0 && screenDensity != bitmapDensity) {
-            SkPaint filteredPaint;
-            if (paint) {
-                filteredPaint = *paint;
-            }
-            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
-            canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
-        } else {
-            canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
-        }
-    }
-
-    static void drawBitmapRF(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong bitmapHandle, jobject srcIRect,
-                             jobject dstRectF, jlong paintHandle,
-                             jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect      dst;
-        GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
-        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
-                screenDensity, bitmapDensity);
-    }
-
-    static void drawBitmapRR(JNIEnv* env, jobject, jlong canvasHandle,
-                             jlong bitmapHandle, jobject srcIRect,
-                             jobject dstRect, jlong paintHandle,
-                             jint screenDensity, jint bitmapDensity) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        SkRect      dst;
-        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
-        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
-                screenDensity, bitmapDensity);
-    }
-
-    static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
-                                jintArray jcolors, jint offset, jint stride,
-                                jfloat x, jfloat y, jint width, jint height,
-                                jboolean hasAlpha, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
-        // correct the alphaType to kOpaque_SkAlphaType.
-        SkImageInfo info = SkImageInfo::Make(width, height,
-                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
-                               kPremul_SkAlphaType);
-        SkBitmap    bitmap;
-        if (!bitmap.allocPixels(info)) {
-            return;
-        }
-
-        if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
-                0, 0, width, height, bitmap)) {
-            return;
-        }
-
-        canvas->drawBitmap(bitmap, x, y, paint);
-    }
-
-    static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle,
-                                 jlong bitmapHandle, jlong matrixHandle,
-                                 jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
-    }
-
-    static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle,
-                          jlong bitmapHandle, jint meshWidth, jint meshHeight,
-                          jfloatArray jverts, jint vertIndex, jintArray jcolors,
-                          jint colorIndex, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        const int ptCount = (meshWidth + 1) * (meshHeight + 1);
-        const int indexCount = meshWidth * meshHeight * 6;
-
-        AutoJavaFloatArray  vertA(env, jverts, vertIndex + (ptCount << 1));
-        AutoJavaIntArray    colorA(env, jcolors, colorIndex + ptCount);
-
-        /*  Our temp storage holds 2 or 3 arrays.
-            texture points [ptCount * sizeof(SkPoint)]
-            optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
-                copy to convert from float to fixed
-            indices [ptCount * sizeof(uint16_t)]
-        */
-        ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
-        storageSize += indexCount * sizeof(uint16_t);  // indices[]
-
-        SkAutoMalloc storage(storageSize);
-        SkPoint* texs = (SkPoint*)storage.get();
-        SkPoint* verts;
-        uint16_t* indices;
-#ifdef SK_SCALAR_IS_FLOAT
-        verts = (SkPoint*)(vertA.ptr() + vertIndex);
-        indices = (uint16_t*)(texs + ptCount);
-#else
-        SkASSERT(false);
-#endif
-
-        // cons up texture coordinates and indices
-        {
-            const SkScalar w = SkIntToScalar(bitmap->width());
-            const SkScalar h = SkIntToScalar(bitmap->height());
-            const SkScalar dx = w / meshWidth;
-            const SkScalar dy = h / meshHeight;
-
-            SkPoint* texsPtr = texs;
-            SkScalar y = 0;
-            for (int i = 0; i <= meshHeight; i++) {
-                if (i == meshHeight) {
-                    y = h;  // to ensure numerically we hit h exactly
-                }
-                SkScalar x = 0;
-                for (int j = 0; j < meshWidth; j++) {
-                    texsPtr->set(x, y);
-                    texsPtr += 1;
-                    x += dx;
-                }
-                texsPtr->set(w, y);
-                texsPtr += 1;
-                y += dy;
-            }
-            SkASSERT(texsPtr - texs == ptCount);
-        }
-
-        // cons up indices
-        {
-            uint16_t* indexPtr = indices;
-            int index = 0;
-            for (int i = 0; i < meshHeight; i++) {
-                for (int j = 0; j < meshWidth; j++) {
-                    // lower-left triangle
-                    *indexPtr++ = index;
-                    *indexPtr++ = index + meshWidth + 1;
-                    *indexPtr++ = index + meshWidth + 2;
-                    // upper-right triangle
-                    *indexPtr++ = index;
-                    *indexPtr++ = index + meshWidth + 2;
-                    *indexPtr++ = index + 1;
-                    // bump to the next cell
-                    index += 1;
-                }
-                // bump to the next row
-                index += 1;
-            }
-            SkASSERT(indexPtr - indices == indexCount);
-            SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
-        }
-
-        // double-check that we have legal indices
-#ifdef SK_DEBUG
-        {
-            for (int i = 0; i < indexCount; i++) {
-                SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
-            }
-        }
-#endif
-
-        // cons-up a shader for the bitmap
-        SkPaint tmpPaint;
-        if (paint) {
-            tmpPaint = *paint;
-        }
-        SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
-                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-        SkSafeUnref(tmpPaint.setShader(shader));
-
-        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
-                             texs, (const SkColor*)colorA.ptr(), NULL, indices,
-                             indexCount, tmpPaint);
-    }
-
-    static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
-                             jint modeHandle, jint vertexCount,
-                             jfloatArray jverts, jint vertIndex,
-                             jfloatArray jtexs, jint texIndex,
-                             jintArray jcolors, jint colorIndex,
-                             jshortArray jindices, jint indexIndex,
-                             jint indexCount, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
-        const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-
-        AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
-        AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
-        AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
-        AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
-
-        const int ptCount = vertexCount >> 1;
-
-        SkPoint* verts;
-        SkPoint* texs = NULL;
-#ifdef SK_SCALAR_IS_FLOAT
-        verts = (SkPoint*)(vertA.ptr() + vertIndex);
-        if (jtexs != NULL) {
-            texs = (SkPoint*)(texA.ptr() + texIndex);
-        }
-#else
-        SkASSERT(false);
-#endif
-
-        const SkColor* colors = NULL;
-        const uint16_t* indices = NULL;
-        if (jcolors != NULL) {
-            colors = (const SkColor*)(colorA.ptr() + colorIndex);
-        }
-        if (jindices != NULL) {
-            indices = (const uint16_t*)(indexA.ptr() + indexIndex);
-        }
-
-        canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
-                             indices, indexCount, *paint);
-    }
-
-
-    static void drawText___CIIFFIPaintTypeface(JNIEnv* env, jobject, jlong canvasHandle,
-                                               jcharArray text, jint index, jint count,
-                                               jfloat x, jfloat y, jint bidiFlags,
-                                               jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
-        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, bidiFlags, paint, typeface);
-        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
-    }
-
-    static void drawText__StringIIFFIPaintTypeface(JNIEnv* env, jobject,
-                                                   jlong canvasHandle, jstring text,
-                                                   jint start, jint end,
-                                                   jfloat x, jfloat y, jint bidiFlags,
-                                                   jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        drawTextWithGlyphs(canvas, textArray, start, end, x, y, bidiFlags, paint, typeface);
-        env->ReleaseStringChars(text, textArray);
-    }
-
-    class DrawTextFunctor {
-    public:
-        DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
-                    uint16_t* glyphs, SkPoint* pos)
-                : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
-                    pos(pos) { }
-
-        void operator()(size_t start, size_t end) {
-            for (size_t i = start; i < end; i++) {
-                glyphs[i] = layout.getGlyphId(i);
-                pos[i].fX = x + layout.getX(i);
-                pos[i].fY = y + layout.getY(i);
-            }
-            canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
-        }
-    private:
-        const Layout& layout;
-        SkCanvas* canvas;
-        jfloat x;
-        jfloat y;
-        SkPaint* paint;
-        uint16_t* glyphs;
-        SkPoint* pos;
-    };
-
-    static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
-        size_t nGlyphs = layout.nGlyphs();
-        uint16_t* glyphs = new uint16_t[nGlyphs];
-        SkPoint* pos = new SkPoint[nGlyphs];
-
-        x += MinikinUtils::xOffsetForTextAlign(paint, layout);
-        SkPaint::Align align = paint->getTextAlign();
-        paint->setTextAlign(SkPaint::kLeft_Align);
-        paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
-        MinikinUtils::forFontRun(layout, paint, f);
-        doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
-        paint->setTextAlign(align);
-        delete[] glyphs;
-        delete[] pos;
-    }
-
-    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
-            int start, int end,
-            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
-        jint count = end - start;
-        drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, bidiFlags, paint,
-                typeface);
-    }
-
-    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
-            int start, int count, int contextCount,
-            jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
-
-        Layout layout;
-        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
-        layout.doLayout(textArray, start, count, contextCount, css);
-        drawGlyphsToSkia(canvas, paint, layout, x, y);
-    }
-
-// Same values used by Skia
-#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
-#define kStdUnderline_Offset    (1.0f / 9.0f)
-#define kStdUnderline_Thickness (1.0f / 18.0f)
-
-    static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length,
-            SkPaint* paint) {
-        uint32_t flags;
-        SkDrawFilter* drawFilter = canvas->getDrawFilter();
-        if (drawFilter) {
-            SkPaint paintCopy(*paint);
-            drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
-            flags = paintCopy.getFlags();
-        } else {
-            flags = paint->getFlags();
-        }
-        if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
-            SkScalar left = x;
-            SkScalar right = x + length;
-            float textSize = paint->getTextSize();
-            float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
-            if (flags & SkPaint::kUnderlineText_Flag) {
-                SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
-                SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
-                canvas->drawRectCoords(left, top, right, bottom, *paint);
-            }
-            if (flags & SkPaint::kStrikeThruText_Flag) {
-                SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
-                SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
-                canvas->drawRectCoords(left, top, right, bottom, *paint);
-            }
-        }
-    }
-
-    static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
-            int index, int count, jfloat x, jfloat y, SkPaint* paint) {
-        SkPoint* posPtr = new SkPoint[count];
-        for (int indx = 0; indx < count; indx++) {
-            posPtr[indx].fX = x + posArray[indx * 2];
-            posPtr[indx].fY = y + posArray[indx * 2 + 1];
-        }
-        canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
-        delete[] posPtr;
-    }
-
-    static void drawTextRun___CIIIIFFZPaintTypeface(
-            JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
-            jint count, jint contextIndex, jint contextCount,
-            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-        jchar* chars = env->GetCharArrayElements(text, NULL);
-        drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
-                count, contextCount, x, y, bidiFlags, paint, typeface);
-        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
-    }
-
-    static void drawTextRun__StringIIIIFFZPaintTypeface(
-            JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start,
-            jint end, jint contextStart, jint contextEnd,
-            jfloat x, jfloat y, jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-        jint count = end - start;
-        jint contextCount = contextEnd - contextStart;
-        const jchar* chars = env->GetStringChars(text, NULL);
-        drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
-                count, contextCount, x, y, bidiFlags, paint, typeface);
-        env->ReleaseStringChars(text, chars);
-    }
-
-    static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle,
-                                         jcharArray text, jint index, jint count,
-                                         jfloatArray pos, jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
-        jsize textCount = text ? env->GetArrayLength(text) : NULL;
-        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-        int indx;
-        for (indx = 0; indx < posCount; indx++) {
-            posPtr[indx].fX = posArray[indx << 1];
-            posPtr[indx].fY = posArray[(indx << 1) + 1];
-        }
-
-        SkPaint::TextEncoding encoding = paint->getTextEncoding();
-        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-        canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
-        paint->setTextEncoding(encoding);
-
-        if (text) {
-            env->ReleaseCharArrayElements(text, textArray, 0);
-        }
-        if (pos) {
-            env->ReleaseFloatArrayElements(pos, posArray, 0);
-        }
-        delete[] posPtr;
-    }
-
-    static void drawPosText__String_FPaint(JNIEnv* env, jobject,
-                                           jlong canvasHandle, jstring text,
-                                           jfloatArray pos,
-                                           jlong paintHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
-        int byteLength = text ? env->GetStringLength(text) : 0;
-        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
-        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
-        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
-
-        for (int indx = 0; indx < posCount; indx++) {
-            posPtr[indx].fX = posArray[indx << 1];
-            posPtr[indx].fY = posArray[(indx << 1) + 1];
-        }
-
-        SkPaint::TextEncoding encoding = paint->getTextEncoding();
-        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
-        canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
-        paint->setTextEncoding(encoding);
-
-        if (text) {
-            env->ReleaseStringChars(text, (const jchar*) text_);
-        }
-        if (pos) {
-            env->ReleaseFloatArrayElements(pos, posArray, 0);
-        }
-        delete[] posPtr;
-    }
-
-    class DrawTextOnPathFunctor {
-    public:
-        DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
-                    float vOffset, SkPaint* paint, SkPath* path)
-                : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
-                    paint(paint), path(path) {
-        }
-        void operator()(size_t start, size_t end) {
-            uint16_t glyphs[1];
-            for (size_t i = start; i < end; i++) {
-                glyphs[0] = layout.getGlyphId(i);
-                float x = hOffset + layout.getX(i);
-                float y = vOffset + layout.getY(i);
-                canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
-            }
-        }
-    private:
-        const Layout& layout;
-        SkCanvas* canvas;
-        float hOffset;
-        float vOffset;
-        SkPaint* paint;
-        SkPath* path;
-    };
-
-    static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
-            float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
-        Layout layout;
-        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
-        layout.doLayout(text, 0, count, count, css);
-        hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
-        // Set align to left for drawing, as we don't want individual
-        // glyphs centered or right-aligned; the offset above takes
-        // care of all alignment.
-        SkPaint::Align align = paint->getTextAlign();
-        paint->setTextAlign(SkPaint::kLeft_Align);
-
-        DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
-        MinikinUtils::forFontRun(layout, paint, f);
-        paint->setTextAlign(align);
-    }
-
-    static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
-            jlong canvasHandle, jcharArray text, jint index, jint count,
-            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
-            jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
-        doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas, typeface);
-        env->ReleaseCharArrayElements(text, textArray, 0);
-    }
-
-    static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
-            jlong canvasHandle, jstring text, jlong pathHandle,
-            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
-            jlong typefaceHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-
-        const jchar* text_ = env->GetStringChars(text, NULL);
-        int count = env->GetStringLength(text);
-        doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas, typeface);
-        env->ReleaseStringChars(text, text_);
-    }
-
-
-    // This function is a mirror of SkCanvas::getClipBounds except that it does
-    // not outset the edge of the clip to account for anti-aliasing. There is
-    // a skia bug to investigate pushing this logic into back into skia.
-    // (see https://code.google.com/p/skia/issues/detail?id=1303)
-    static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
-        SkIRect ibounds;
-        if (!canvas->getClipDeviceBounds(&ibounds)) {
-            return false;
-        }
-
-        SkMatrix inverse;
-        // if we can't invert the CTM, we can't return local clip bounds
-        if (!canvas->getTotalMatrix().invert(&inverse)) {
-            if (bounds) {
-                bounds->setEmpty();
-            }
-            return false;
-        }
-
-        if (NULL != bounds) {
-            SkRect r = SkRect::Make(ibounds);
-            inverse.mapRect(bounds, r);
-        }
-        return true;
-    }
-
-    static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle,
-                                  jobject bounds) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkRect   r;
-        SkIRect ir;
-        bool result = getHardClipBounds(canvas, &r);
-
-        if (!result) {
-            r.setEmpty();
-        }
-        r.round(&ir);
-
-        (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
-        return result ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static void getCTM(JNIEnv* env, jobject, jlong canvasHandle,
-                       jlong matrixHandle) {
-        SkCanvas* canvas = getNativeCanvas(canvasHandle);
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        *matrix = canvas->getTotalMatrix();
-    }
-};
-
-static JNINativeMethod gCanvasMethods[] = {
-    {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer},
-    {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
-    {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
-    {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
-    {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
-    {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
-    {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
-    {"native_save","(JI)I", (void*) SkCanvasGlue::save},
-    {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
-    {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
-    {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
-    {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
-    {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
-    {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
-    {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
-    {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
-    {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
-    {"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
-    {"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
-    {"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
-    {"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
-    {"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
-    {"nativeSetDrawFilter", "(JJ)V", (void*) SkCanvasGlue::setDrawFilter},
-    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
-        (void*) SkCanvasGlue::getClipBounds},
-    {"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
-    {"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
-    {"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
-    {"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
-    {"native_drawARGB","(JIIII)V", (void*) SkCanvasGlue::drawARGB},
-    {"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
-    {"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
-    {"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
-    {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
-    {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
-    {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
-    {"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
-    {"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
-    {"native_drawOval","(JFFFFJ)V", (void*) SkCanvasGlue::drawOval},
-    {"native_drawCircle","(JFFFJ)V", (void*) SkCanvasGlue::drawCircle},
-    {"native_drawArc","(JFFFFFFZJ)V", (void*) SkCanvasGlue::drawArc},
-    {"native_drawRoundRect","(JFFFFFFJ)V",
-        (void*) SkCanvasGlue::drawRoundRect},
-    {"native_drawPath","(JJJ)V", (void*) SkCanvasGlue::drawPath},
-    {"native_drawBitmap","(JJFFJIII)V",
-        (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
-    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/RectF;JII)V",
-        (void*) SkCanvasGlue::drawBitmapRF},
-    {"native_drawBitmap","(JJLandroid/graphics/Rect;Landroid/graphics/Rect;JII)V",
-        (void*) SkCanvasGlue::drawBitmapRR},
-    {"native_drawBitmap", "(J[IIIFFIIZJ)V",
-    (void*)SkCanvasGlue::drawBitmapArray},
-    {"nativeDrawBitmapMatrix", "(JJJJ)V",
-        (void*)SkCanvasGlue::drawBitmapMatrix},
-    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V",
-        (void*)SkCanvasGlue::drawBitmapMesh},
-    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V",
-        (void*)SkCanvasGlue::drawVertices},
-    {"native_drawText","(J[CIIFFIJJ)V",
-        (void*) SkCanvasGlue::drawText___CIIFFIPaintTypeface},
-    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V",
-        (void*) SkCanvasGlue::drawText__StringIIFFIPaintTypeface},
-    {"native_drawTextRun","(J[CIIIIFFZJJ)V",
-        (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
-    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
-        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
-    {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
-        (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
-        (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
-
-    {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
-
-    {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
-    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
-                                                    SK_ARRAY_COUNT(array));  \
-    if (result < 0) return result
-
-int register_android_graphics_Canvas(JNIEnv* env) {
-    int result;
-
-    REG(env, "android/graphics/Canvas", gCanvasMethods);
-
-    return result;
-}
-
-} // namespace android
-
-// GraphicsJNI helper for external clients.
-// We keep the implementation here to avoid exposing NativeCanvasWrapper
-// externally.
-SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) {
-    return android::SkCanvasGlue::getNativeCanvas(nativeHandle);
-}
diff --git a/core/jni/android/graphics/Canvas.h b/core/jni/android/graphics/Canvas.h
new file mode 100644
index 0000000..710845d
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_GRAPHICS_CANVAS_H
+#define ANDROID_GRAPHICS_CANVAS_H
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+// TODO: move this further up the stack so that all interaction with minikin
+//       happens prior to calling into this interface
+class TypefaceImpl;
+
+class Canvas {
+public:
+    virtual ~Canvas() {};
+
+    static Canvas* create_canvas(SkBitmap* bitmap);
+    static Canvas* create_canvas(SkCanvas* skiaCanvas);
+
+    // TODO: enable HWUI to either create similar canvas wrapper or subclass
+    //       directly from Canvas
+    //static Canvas* create_canvas(uirenderer::Renderer* renderer);
+
+    // TODO: this is a temporary affordance until all necessary logic can be
+    //       moved within this interface! Further, the return value should
+    //       NOT be unref'd and is valid until this canvas is destroyed or a
+    //       new bitmap is set.
+    virtual SkCanvas* getSkCanvas() = 0;
+
+    virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
+
+    virtual bool isOpaque() = 0;
+    virtual int width() = 0;
+    virtual int height() = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+    // Save (layer)
+    virtual int getSaveCount() const = 0;
+    virtual int save(SkCanvas::SaveFlags flags) = 0;
+    virtual void restore() = 0;
+    virtual void restoreToCount(int saveCount) = 0;
+
+    virtual int saveLayer(float left, float top, float right, float bottom,
+                const SkPaint* paint, SkCanvas::SaveFlags flags) = 0;
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, SkCanvas::SaveFlags flags) = 0;
+
+    // Matrix
+    virtual void getMatrix(SkMatrix* outMatrix) const = 0;
+    virtual void setMatrix(const SkMatrix& matrix) = 0;
+
+    virtual void concat(const SkMatrix& matrix) = 0;
+    virtual void rotate(float degrees) = 0;
+    virtual void scale(float sx, float sy) = 0;
+    virtual void skew(float sx, float sy) = 0;
+    virtual void translate(float dx, float dy) = 0;
+
+    // clip
+    virtual bool getClipBounds(SkRect* outRect) const = 0;
+    virtual bool quickRejectRect(float left, float top, float right, float bottom) const = 0;
+    virtual bool quickRejectPath(const SkPath& path) const = 0;
+
+    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;
+    virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
+    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+
+    // filters
+    virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+    virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+    virtual void drawPaint(const SkPaint& paint) = 0;
+
+    // Geometry
+    virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
+    virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawLine(float startX, float startY, float stopX, float stopY,
+                const SkPaint& paint) = 0;
+    virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawRect(float left, float top, float right, float bottom,
+            const SkPaint& paint) = 0;
+    virtual void drawRoundRect(float left, float top, float right, float bottom,
+            float rx, float ry, const SkPaint& paint) = 0;
+    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
+    virtual void drawOval(float left, float top, float right, float bottom,
+            const SkPaint& paint) = 0;
+    virtual void drawArc(float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
+    virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
+    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+                              const float* verts, const float* tex, const int* colors,
+                              const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
+
+    // Bitmap-based
+    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+            const SkPaint* paint) = 0;
+    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint) = 0;
+    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint) = 0;
+    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+            const float* vertices, const int* colors, const SkPaint* paint) = 0;
+
+    // Text
+    virtual void drawText(const char* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint,
+            TypefaceImpl* typeface) = 0;
+    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
+            const SkPaint& paint) = 0;
+    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint) = 0;
+};
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5cc2b95..74be577 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,6 +4,7 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
@@ -364,7 +365,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = getNativeCanvas(canvasHandle);
+    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
     SkASSERT(c);
     return c;
 }
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8150edf..28a6edb 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -47,7 +47,6 @@
     static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point);
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
-    static SkCanvas* getNativeCanvas(jlong nativeHandle);
     static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas);
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index ab5bdb0..e82e8a6 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,6 +23,7 @@
 
 #include <Caches.h>
 
+#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
@@ -119,7 +120,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -138,7 +139,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
+        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bc0c25f..d214575 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include "Canvas.h"
 #include "Picture.h"
 
-#include "SkCanvas.h"
 #include "SkStream.h"
 
 namespace android {
@@ -36,12 +36,13 @@
     }
 }
 
-SkCanvas* Picture::beginRecording(int width, int height) {
+Canvas* Picture::beginRecording(int width, int height) {
     mPicture.reset(NULL);
     mRecorder.reset(new SkPictureRecorder);
     mWidth = width;
     mHeight = height;
-    return mRecorder->beginRecording(width, height, NULL, 0);
+    SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
+    return Canvas::create_canvas(canvas);
 }
 
 void Picture::endRecording() {
@@ -93,14 +94,14 @@
     }
 }
 
-void Picture::draw(SkCanvas* canvas) {
+void Picture::draw(Canvas* canvas) {
     if (NULL != mRecorder.get()) {
         this->endRecording();
         SkASSERT(NULL != mPicture.get());
     }
     if (NULL != mPicture.get()) {
         // TODO: remove this const_cast once pictures are immutable
-        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas->getSkCanvas());
     }
 }
 
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index abb0403..a2e5d4a 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,14 +22,13 @@
 #include "SkRefCnt.h"
 #include "SkTemplates.h"
 
-class SkCanvas;
-class SkPicture;
-class SkPictureRecorder;
 class SkStream;
 class SkWStream;
 
 namespace android {
 
+class Canvas;
+
 // Skia's SkPicture class has been split into an SkPictureRecorder
 // and an SkPicture. AndroidPicture recreates the functionality
 // of the old SkPicture interface by flip-flopping between the two
@@ -38,7 +37,7 @@
 public:
     explicit Picture(const Picture* src = NULL);
 
-    SkCanvas* beginRecording(int width, int height);
+    Canvas* beginRecording(int width, int height);
 
     void endRecording();
 
@@ -50,7 +49,7 @@
 
     void serialize(SkWStream* stream) const;
 
-    void draw(SkCanvas* canvas);
+    void draw(Canvas* canvas);
 
 private:
     int mWidth;
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
new file mode 100644
index 0000000..5e93313
--- /dev/null
+++ b/core/jni/android/graphics/SkiaCanvas.cpp
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "jni.h"
+#include "Canvas.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkDevice.h"
+#include "SkDeque.h"
+#include "SkDrawFilter.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "SkShader.h"
+#include "SkTArray.h"
+#include "SkTemplates.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+#include "TypefaceImpl.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+// Holds an SkCanvas reference plus additional native data.
+class SkiaCanvas : public Canvas {
+public:
+    SkiaCanvas(SkBitmap* bitmap);
+
+    SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
+        SkASSERT(canvas);
+    }
+
+    virtual SkCanvas* getSkCanvas() {
+        return mCanvas.get();
+    }
+
+    virtual void setBitmap(SkBitmap* bitmap, bool copyState);
+
+    virtual bool isOpaque();
+    virtual int width();
+    virtual int height();
+
+    virtual int getSaveCount() const;
+    virtual int save(SkCanvas::SaveFlags flags);
+    virtual void restore();
+    virtual void restoreToCount(int saveCount);
+
+    virtual int saveLayer(float left, float top, float right, float bottom,
+                const SkPaint* paint, SkCanvas::SaveFlags flags);
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, SkCanvas::SaveFlags flags);
+
+    virtual void getMatrix(SkMatrix* outMatrix) const;
+    virtual void setMatrix(const SkMatrix& matrix);
+    virtual void concat(const SkMatrix& matrix);
+    virtual void rotate(float degrees);
+    virtual void scale(float sx, float sy);
+    virtual void skew(float sx, float sy);
+    virtual void translate(float dx, float dy);
+
+    virtual bool getClipBounds(SkRect* outRect) const;
+    virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
+    virtual bool quickRejectPath(const SkPath& path) const;
+    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+    virtual bool clipPath(const SkPath* path, SkRegion::Op op);
+    virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+
+    virtual void setDrawFilter(SkDrawFilter* drawFilter);
+
+    virtual void drawColor(int color, SkXfermode::Mode mode);
+    virtual void drawPaint(const SkPaint& paint);
+
+    virtual void drawPoint(float x, float y, const SkPaint& paint);
+    virtual void drawPoints(const float* points, int count, const SkPaint& paint);
+    virtual void drawLine(float startX, float startY, float stopX, float stopY,
+            const SkPaint& paint);
+    virtual void drawLines(const float* points, int count, const SkPaint& paint);
+    virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
+    virtual void drawRoundRect(float left, float top, float right, float bottom,
+            float rx, float ry, const SkPaint& paint);
+    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
+    virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
+    virtual void drawArc(float left, float top, float right, float bottom,
+            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
+    virtual void drawPath(const SkPath& path, const SkPaint& paint);
+    virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+            const float* verts, const float* tex, const int* colors,
+            const uint16_t* indices, int indexCount, const SkPaint& paint);
+
+    virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
+    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, const SkPaint* paint);
+    virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+            const float* vertices, const int* colors, const SkPaint* paint);
+
+    virtual void drawText(const char* text, int start, int count, int contextCount,
+            float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface);
+    virtual void drawPosText(const char* text, const float* positions, int count, int posCount,
+            const SkPaint& paint);
+    virtual void drawTextOnPath(const char* text, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint);
+
+private:
+    struct SaveRec {
+        int                 saveCount;
+        SkCanvas::SaveFlags saveFlags;
+    };
+
+    void recordPartialSave(SkCanvas::SaveFlags flags);
+    void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
+    void applyClips(const SkTArray<SkClipStack::Element>& clips);
+
+    void drawPoints(const float* points, int count, const SkPaint& paint,
+                    SkCanvas::PointMode mode);
+    void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+    SkAutoTUnref<SkCanvas> mCanvas;
+    SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+};
+
+// Construct an SkCanvas from the bitmap.
+static SkCanvas* createCanvas(SkBitmap* bitmap) {
+    if (bitmap) {
+        return SkNEW_ARGS(SkCanvas, (*bitmap));
+    }
+
+    // Create an empty bitmap device to prevent callers from crashing
+    // if they attempt to draw into this canvas.
+    SkBitmap emptyBitmap;
+    return new SkCanvas(emptyBitmap);
+}
+
+Canvas* Canvas::create_canvas(SkBitmap* bitmap) {
+    return new SkiaCanvas(bitmap);
+}
+
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+    return new SkiaCanvas(skiaCanvas);
+}
+
+SkiaCanvas::SkiaCanvas(SkBitmap* bitmap) {
+    mCanvas.reset(createCanvas(bitmap));
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Replace Bitmap
+// ----------------------------------------------------------------------------
+
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRect(rect, op, antialias);
+    }
+    virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipRRect(rrect, op, antialias);
+    }
+    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+        m_dstCanvas->clipPath(path, op, antialias);
+    }
+
+private:
+    SkCanvas* m_dstCanvas;
+};
+
+void SkiaCanvas::setBitmap(SkBitmap* bitmap, bool copyState) {
+    SkCanvas* newCanvas = createCanvas(bitmap);
+    SkASSERT(newCanvas);
+
+    if (copyState) {
+        // Copy the canvas matrix & clip state.
+        newCanvas->setMatrix(mCanvas->getTotalMatrix());
+        if (NULL != mCanvas->getDevice() && NULL != newCanvas->getDevice()) {
+            ClipCopier copier(newCanvas);
+            mCanvas->replayClips(&copier);
+        }
+    }
+
+    // unrefs the existing canvas
+    mCanvas.reset(newCanvas);
+
+    // clean up the old save stack
+    mSaveStack.reset(NULL);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations
+// ----------------------------------------------------------------------------
+
+bool SkiaCanvas::isOpaque() {
+    return mCanvas->getDevice()->accessBitmap(false).isOpaque();
+}
+
+int SkiaCanvas::width() {
+    return mCanvas->getBaseLayerSize().width();
+}
+
+int SkiaCanvas::height() {
+    return mCanvas->getBaseLayerSize().height();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Save (layer)
+// ----------------------------------------------------------------------------
+
+int SkiaCanvas::getSaveCount() const {
+    return mCanvas->getSaveCount();
+}
+
+int SkiaCanvas::save(SkCanvas::SaveFlags flags) {
+    int count = mCanvas->save();
+    recordPartialSave(flags);
+    return count;
+}
+
+void SkiaCanvas::restore() {
+    const SaveRec* rec = (NULL == mSaveStack.get())
+            ? NULL
+            : static_cast<SaveRec*>(mSaveStack->back());
+    int currentSaveCount = mCanvas->getSaveCount() - 1;
+    SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+    if (NULL == rec || rec->saveCount != currentSaveCount) {
+        // Fast path - no record for this frame.
+        mCanvas->restore();
+        return;
+    }
+
+    bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+    bool preserveClip   = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+    SkMatrix savedMatrix;
+    if (preserveMatrix) {
+        savedMatrix = mCanvas->getTotalMatrix();
+    }
+
+    SkTArray<SkClipStack::Element> savedClips;
+    if (preserveClip) {
+        saveClipsForFrame(savedClips, currentSaveCount);
+    }
+
+    mCanvas->restore();
+
+    if (preserveMatrix) {
+        mCanvas->setMatrix(savedMatrix);
+    }
+
+    if (preserveClip && !savedClips.empty()) {
+        applyClips(savedClips);
+    }
+
+    mSaveStack->pop_back();
+}
+
+void SkiaCanvas::restoreToCount(int restoreCount) {
+    while (mCanvas->getSaveCount() > restoreCount) {
+        this->restore();
+    }
+}
+
+int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
+            const SkPaint* paint, SkCanvas::SaveFlags flags) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    int count = mCanvas->saveLayer(&bounds, paint, flags | SkCanvas::kMatrixClip_SaveFlag);
+    recordPartialSave(flags);
+    return count;
+}
+
+int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, SkCanvas::SaveFlags flags) {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    int count = mCanvas->saveLayerAlpha(&bounds, alpha, flags | SkCanvas::kMatrixClip_SaveFlag);
+    recordPartialSave(flags);
+    return count;
+}
+
+// ----------------------------------------------------------------------------
+// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
+    // A partial save is a save operation which doesn't capture the full canvas state.
+    // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+    // Mask-out non canvas state bits.
+    flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+    if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+        // not a partial save.
+        return;
+    }
+
+    if (NULL == mSaveStack.get()) {
+        mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+    }
+
+    SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+    // Store the save counter in the SkClipStack domain.
+    // (0-based, equal to the number of save ops on the stack).
+    rec->saveCount = mCanvas->getSaveCount() - 1;
+    rec->saveFlags = flags;
+}
+
+void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount) {
+    SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+                                   SkClipStack::Iter::kTop_IterStart);
+    while (const SkClipStack::Element* elem = clipIterator.next()) {
+        if (elem->getSaveCount() < frameSaveCount) {
+            // done with the current frame.
+            break;
+        }
+        SkASSERT(elem->getSaveCount() == frameSaveCount);
+        clips.push_back(*elem);
+    }
+}
+
+void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
+    ClipCopier clipCopier(mCanvas);
+
+    // The clip stack stores clips in device space.
+    SkMatrix origMatrix = mCanvas->getTotalMatrix();
+    mCanvas->resetMatrix();
+
+    // We pushed the clips in reverse order.
+    for (int i = clips.count() - 1; i >= 0; --i) {
+        clips[i].replay(&clipCopier);
+    }
+
+    mCanvas->setMatrix(origMatrix);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Matrix
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::getMatrix(SkMatrix* outMatrix) const {
+    *outMatrix = mCanvas->getTotalMatrix();
+}
+
+void SkiaCanvas::setMatrix(const SkMatrix& matrix) {
+    mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvas::concat(const SkMatrix& matrix) {
+    mCanvas->concat(matrix);
+}
+
+void SkiaCanvas::rotate(float degrees) {
+    mCanvas->rotate(degrees);
+}
+
+void SkiaCanvas::scale(float sx, float sy) {
+    mCanvas->scale(sx, sy);
+}
+
+void SkiaCanvas::skew(float sx, float sy) {
+    mCanvas->skew(sx, sy);
+}
+
+void SkiaCanvas::translate(float dx, float dy) {
+    mCanvas->translate(dx, dy);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Clips
+// ----------------------------------------------------------------------------
+
+// This function is a mirror of SkCanvas::getClipBounds except that it does
+// not outset the edge of the clip to account for anti-aliasing. There is
+// a skia bug to investigate pushing this logic into back into skia.
+// (see https://code.google.com/p/skia/issues/detail?id=1303)
+bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
+    SkIRect ibounds;
+    if (!mCanvas->getClipDeviceBounds(&ibounds)) {
+        return false;
+    }
+
+    SkMatrix inverse;
+    // if we can't invert the CTM, we can't return local clip bounds
+    if (!mCanvas->getTotalMatrix().invert(&inverse)) {
+        if (outRect) {
+            outRect->setEmpty();
+        }
+        return false;
+    }
+
+    if (NULL != outRect) {
+        SkRect r = SkRect::Make(ibounds);
+        inverse.mapRect(outRect, r);
+    }
+    return true;
+}
+
+bool SkiaCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+    SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
+    return mCanvas->quickReject(bounds);
+}
+
+bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
+    return mCanvas->quickReject(path);
+}
+
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->clipRect(rect, op);
+    return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+    mCanvas->clipPath(*path, op);
+    return mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+    SkPath rgnPath;
+    if (region->getBoundaryPath(&rgnPath)) {
+        // The region is specified in device space.
+        SkMatrix savedMatrix = mCanvas->getTotalMatrix();
+        mCanvas->resetMatrix();
+        mCanvas->clipPath(rgnPath, op);
+        mCanvas->setMatrix(savedMatrix);
+    } else {
+        mCanvas->clipRect(SkRect::MakeEmpty(), op);
+    }
+    return mCanvas->isClipEmpty();
+}
+
+// ----------------------------------------------------------------------------
+// Canvas state operations: Filters
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
+    mCanvas->setDrawFilter(drawFilter);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+    mCanvas->drawColor(color, mode);
+}
+
+void SkiaCanvas::drawPaint(const SkPaint& paint) {
+    mCanvas->drawPaint(paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Geometry
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
+                            SkCanvas::PointMode mode) {
+    // convert the floats into SkPoints
+    count >>= 1;    // now it is the number of points
+    SkAutoSTMalloc<32, SkPoint> storage(count);
+    SkPoint* pts = storage.get();
+    for (int i = 0; i < count; i++) {
+        pts[i].set(points[0], points[1]);
+        points += 2;
+    }
+    mCanvas->drawPoints(mode, count, pts, paint);
+}
+
+
+void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
+    mCanvas->drawPoint(x, y, paint);
+}
+
+void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+}
+
+void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
+                          const SkPaint& paint) {
+    mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+}
+
+void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+}
+
+void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
+        const SkPaint& paint) {
+    mCanvas->drawRectCoords(left, top, right, bottom, paint);
+
+}
+
+void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
+        float rx, float ry, const SkPaint& paint) {
+    SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawRoundRect(rect, rx, ry, paint);
+}
+
+void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+    mCanvas->drawCircle(x, y, radius, paint);
+}
+
+void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+    SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawOval(oval, paint);
+}
+
+void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
+        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+    SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
+    mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+}
+
+void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+                              const float* verts, const float* texs, const int* colors,
+                              const uint16_t* indices, int indexCount, const SkPaint& paint) {
+#ifndef SK_SCALAR_IS_FLOAT
+    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+    const int ptCount = vertexCount >> 1;
+    mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
+                          (SkColor*)colors, NULL, indices, indexCount, paint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+    mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+    mCanvas->drawBitmapMatrix(bitmap, matrix, paint);
+}
+
+void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+                            float srcRight, float srcBottom, float dstLeft, float dstTop,
+                            float dstRight, float dstBottom, const SkPaint* paint) {
+    SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+    SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+    mCanvas->drawBitmapRectToRect(bitmap, &srcRect, dstRect, paint);
+}
+
+void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+        const float* vertices, const int* colors, const SkPaint* paint) {
+
+    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+    const int indexCount = meshWidth * meshHeight * 6;
+
+    /*  Our temp storage holds 2 or 3 arrays.
+        texture points [ptCount * sizeof(SkPoint)]
+        optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
+            copy to convert from float to fixed
+        indices [ptCount * sizeof(uint16_t)]
+    */
+    ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
+    storageSize += indexCount * sizeof(uint16_t);  // indices[]
+
+
+#ifndef SK_SCALAR_IS_FLOAT
+    SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
+#endif
+    SkAutoMalloc storage(storageSize);
+    SkPoint* texs = (SkPoint*)storage.get();
+    uint16_t* indices = (uint16_t*)(texs + ptCount);
+
+    // cons up texture coordinates and indices
+    {
+        const SkScalar w = SkIntToScalar(bitmap.width());
+        const SkScalar h = SkIntToScalar(bitmap.height());
+        const SkScalar dx = w / meshWidth;
+        const SkScalar dy = h / meshHeight;
+
+        SkPoint* texsPtr = texs;
+        SkScalar y = 0;
+        for (int i = 0; i <= meshHeight; i++) {
+            if (i == meshHeight) {
+                y = h;  // to ensure numerically we hit h exactly
+            }
+            SkScalar x = 0;
+            for (int j = 0; j < meshWidth; j++) {
+                texsPtr->set(x, y);
+                texsPtr += 1;
+                x += dx;
+            }
+            texsPtr->set(w, y);
+            texsPtr += 1;
+            y += dy;
+        }
+        SkASSERT(texsPtr - texs == ptCount);
+    }
+
+    // cons up indices
+    {
+        uint16_t* indexPtr = indices;
+        int index = 0;
+        for (int i = 0; i < meshHeight; i++) {
+            for (int j = 0; j < meshWidth; j++) {
+                // lower-left triangle
+                *indexPtr++ = index;
+                *indexPtr++ = index + meshWidth + 1;
+                *indexPtr++ = index + meshWidth + 2;
+                // upper-right triangle
+                *indexPtr++ = index;
+                *indexPtr++ = index + meshWidth + 2;
+                *indexPtr++ = index + 1;
+                // bump to the next cell
+                index += 1;
+            }
+            // bump to the next row
+            index += 1;
+        }
+        SkASSERT(indexPtr - indices == indexCount);
+        SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
+    }
+
+    // double-check that we have legal indices
+#ifdef SK_DEBUG
+    {
+        for (int i = 0; i < indexCount; i++) {
+            SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
+        }
+    }
+#endif
+
+    // cons-up a shader for the bitmap
+    SkPaint tmpPaint;
+    if (paint) {
+        tmpPaint = *paint;
+    }
+    SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+                                                    SkShader::kClamp_TileMode,
+                                                    SkShader::kClamp_TileMode);
+    SkSafeUnref(tmpPaint.setShader(shader));
+
+    mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
+                         texs, (const SkColor*)colors, NULL, indices,
+                         indexCount, tmpPaint);
+}
+
+// ----------------------------------------------------------------------------
+// Canvas draw operations: Text
+// ----------------------------------------------------------------------------
+
+class DrawTextFunctor {
+public:
+    DrawTextFunctor(const Layout& layout, SkCanvas* canvas, float x, float y, SkPaint* paint,
+                uint16_t* glyphs, SkPoint* pos)
+            : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
+                pos(pos) { }
+
+    void operator()(size_t start, size_t end) {
+        for (size_t i = start; i < end; i++) {
+            glyphs[i] = layout.getGlyphId(i);
+            pos[i].fX = x + layout.getX(i);
+            pos[i].fY = y + layout.getY(i);
+        }
+        canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
+    }
+private:
+    const Layout& layout;
+    SkCanvas* canvas;
+    float x;
+    float y;
+    SkPaint* paint;
+    uint16_t* glyphs;
+    SkPoint* pos;
+};
+
+void SkiaCanvas::drawText(const char* text, int start, int count, int contextCount,
+        float x, float y, int bidiFlags, const SkPaint& paint, TypefaceImpl* typeface) {
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+    layout.doLayout((uint16_t*)text, start, count, contextCount, css);
+
+    size_t nGlyphs = layout.nGlyphs();
+    uint16_t* glyphs = new uint16_t[nGlyphs];
+    SkPoint* pos = new SkPoint[nGlyphs];
+
+    SkPaint paintCopy(paint);
+    x += MinikinUtils::xOffsetForTextAlign(&paintCopy, layout);
+    paintCopy.setTextAlign(SkPaint::kLeft_Align);
+    paintCopy.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    DrawTextFunctor f(layout, mCanvas, x, y, &paintCopy, glyphs, pos);
+    MinikinUtils::forFontRun(layout, &paintCopy, f);
+    drawTextDecorations(x, y, layout.getAdvance(), paintCopy);
+
+    delete[] glyphs;
+    delete[] pos;
+}
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
+#define kStdUnderline_Offset    (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void SkiaCanvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+    uint32_t flags;
+    SkDrawFilter* drawFilter = mCanvas->getDrawFilter();
+    if (drawFilter) {
+        SkPaint paintCopy(paint);
+        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+        flags = paintCopy.getFlags();
+    } else {
+        flags = paint.getFlags();
+    }
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        SkScalar left = x;
+        SkScalar right = x + length;
+        float textSize = paint.getTextSize();
+        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
+        if (flags & SkPaint::kUnderlineText_Flag) {
+            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
+            mCanvas->drawRectCoords(left, top, right, bottom, paint);
+        }
+        if (flags & SkPaint::kStrikeThruText_Flag) {
+            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
+            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
+            mCanvas->drawRectCoords(left, top, right, bottom, paint);
+        }
+    }
+}
+
+void SkiaCanvas::drawPosText(const char* text, const float* positions, int count, int posCount,
+        const SkPaint& paint) {
+    SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
+    int indx;
+    for (indx = 0; indx < posCount; indx++) {
+        posPtr[indx].fX = positions[indx << 1];
+        posPtr[indx].fY = positions[(indx << 1) + 1];
+    }
+
+    SkPaint paintCopy(paint);
+    paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    mCanvas->drawPosText(text, count, posPtr, paintCopy);
+
+    delete[] posPtr;
+}
+
+void SkiaCanvas::drawTextOnPath(const char* text, int count, const SkPath& path,
+        float hOffset, float vOffset, const SkPaint& paint) {
+    mCanvas->drawTextOnPathHV(text, count, path, hOffset, vOffset, paint);
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 3812c27..9436a47 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -19,9 +19,9 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <vector>
 
+#include "Canvas.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 
-#include "SkCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -132,8 +132,9 @@
         jint pageWidth, jint pageHeight,
         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
-    return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
-            contentLeft, contentTop, contentRight, contentBottom));
+    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
new file mode 100644
index 0000000..fd96a90
--- /dev/null
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "Canvas.h"
+#include "SkGraphics.h"
+#include "SkPorterDuff.h"
+#include "TypefaceImpl.h"
+
+#include <minikin/Layout.h>
+#include "MinikinSkia.h"
+#include "MinikinUtils.h"
+
+namespace android {
+
+namespace CanvasJNI {
+
+static Canvas* get_canvas(jlong canvasHandle) {
+    return reinterpret_cast<Canvas*>(canvasHandle);
+}
+
+static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
+    delete get_canvas(canvasHandle);
+}
+
+// Native wrapper constructor used by Canvas(Bitmap)
+static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
+}
+
+// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
+// optionally copying canvas matrix & clip state.
+static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                      jboolean copyState) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
+}
+
+static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+    return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->width());
+}
+
+static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->height());
+}
+
+static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
+}
+
+static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
+}
+
+static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                      jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
+    SkPaint* paint  = reinterpret_cast<SkPaint*>(paintHandle);
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
+}
+
+static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                           jfloat r, jfloat b, jint alpha, jint flagsHandle) {
+    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
+    return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
+}
+
+static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
+        doThrowISE(env, "Underflow in restore");
+        return;
+    }
+    canvas->restore();
+}
+
+static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
+        doThrowIAE(env, "Underflow in restoreToCount");
+        return;
+    }
+    canvas->restoreToCount(restoreCount);
+}
+
+static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->getMatrix(matrix);
+}
+
+static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
+}
+
+static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    get_canvas(canvasHandle)->concat(*matrix);
+}
+
+static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
+    get_canvas(canvasHandle)->rotate(degrees);
+}
+
+static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->scale(sx, sy);
+}
+
+static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+    get_canvas(canvasHandle)->skew(sx, sy);
+}
+
+static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
+    get_canvas(canvasHandle)->translate(dx, dy);
+}
+
+static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
+    SkRect   r;
+    SkIRect ir;
+    bool result = get_canvas(canvasHandle)->getClipBounds(&r);
+
+    if (!result) {
+        r.setEmpty();
+    }
+    r.round(&ir);
+
+    (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
+                                jfloat left, jfloat top, jfloat right, jfloat bottom) {
+    bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
+    return result ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
+                         jfloat r, jfloat b, jint opHandle) {
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                         jint opHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
+                           jint opHandle) {
+    SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
+    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
+    bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
+    return emptyClip ? JNI_FALSE : JNI_TRUE;
+}
+
+static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
+     SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+     get_canvas(canvasHandle)->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
+}
+
+static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPaint(*paint);
+}
+
+static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+                      jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
+}
+
+static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                       jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
+}
+
+static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
+                     jfloat stopX, jfloat stopY, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
+}
+
+static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
+                      jint offset, jint count, jlong paintHandle) {
+    NPE_CHECK_RETURN_VOID(env, jptsArray);
+    AutoJavaFloatArray autoPts(env, jptsArray);
+    float* floats = autoPts.ptr();
+    const int length = autoPts.length();
+
+    if ((offset | count) < 0 || offset + count > length) {
+        doThrowAIOOBE(env);
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
+}
+
+static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
+}
+
+static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
+}
+
+static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
+                       jfloat radius, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
+}
+
+static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                     jfloat right, jfloat bottom, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
+}
+
+static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
+                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
+                    jboolean useCenter, jlong paintHandle) {
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
+                                       useCenter, *paint);
+}
+
+static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+                     jlong paintHandle) {
+    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawPath(*path, *paint);
+}
+
+static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
+                         jint modeHandle, jint vertexCount,
+                         jfloatArray jverts, jint vertIndex,
+                         jfloatArray jtexs, jint texIndex,
+                         jintArray jcolors, jint colorIndex,
+                         jshortArray jindices, jint indexIndex,
+                         jint indexCount, jlong paintHandle) {
+    AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
+    AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
+    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
+    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
+
+    const float* verts = vertA.ptr() + vertIndex;
+    const float* texs = texA.ptr() + vertIndex;
+    const int* colors = NULL;
+    const uint16_t* indices = NULL;
+
+    if (jcolors != NULL) {
+        colors = colorA.ptr() + colorIndex;
+    }
+    if (jindices != NULL) {
+        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
+    }
+
+    SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
+                                           indices, indexCount, *paint);
+}
+
+static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
+                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
+                       jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+        if (screenDensity != 0 && screenDensity != bitmapDensity) {
+            SkPaint filteredPaint;
+            if (paint) {
+                filteredPaint = *paint;
+            }
+            filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
+        } else {
+            canvas->drawBitmap(*bitmap, left, top, paint);
+        }
+    } else {
+        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+        SkScalar scale = canvasDensity / (float)bitmapDensity;
+        canvas->translate(left, top);
+        canvas->scale(scale, scale);
+
+        SkPaint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+
+        canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
+        canvas->restore();
+    }
+}
+
+static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                             jlong matrixHandle, jlong paintHandle) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
+}
+
+static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           float srcLeft, float srcTop, float srcRight, float srcBottom,
+                           float dstLeft, float dstTop, float dstRight, float dstBottom,
+                           jlong paintHandle, jint screenDensity, jint bitmapDensity) {
+    Canvas* canvas = get_canvas(canvasHandle);
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+
+    if (screenDensity != 0 && screenDensity != bitmapDensity) {
+        SkPaint filteredPaint;
+        if (paint) {
+            filteredPaint = *paint;
+        }
+        filteredPaint.setFilterLevel(SkPaint::kLow_FilterLevel);
+        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
+    } else {
+        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
+                           dstLeft, dstTop, dstRight, dstBottom, paint);
+    }
+}
+
+static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
+                            jintArray jcolors, jint offset, jint stride,
+                            jfloat x, jfloat y, jint width, jint height,
+                            jboolean hasAlpha, jlong paintHandle) {
+    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
+    // correct the alphaType to kOpaque_SkAlphaType.
+    SkImageInfo info = SkImageInfo::Make(width, height,
+                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                           kPremul_SkAlphaType);
+    SkBitmap bitmap;
+    if (!bitmap.allocPixels(info)) {
+        return;
+    }
+
+    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
+        return;
+    }
+
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
+}
+
+static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
+                           jint meshWidth, jint meshHeight, jfloatArray jverts,
+                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
+    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
+    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
+    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
+
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
+                                             vertA.ptr(), colorA.ptr(), paint);
+}
+
+static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
+                          jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
+    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
+    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
+                           jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+    const int count = end - start;
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + start;
+    get_canvas(canvasHandle)->drawText(textArray, 0, count, count, x, y, bidiFlags, *paint, typeface);
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
+                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
+                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + contextIndex;
+    get_canvas(canvasHandle)->drawText(textArray, index - contextIndex, count, contextCount,
+                                       x, y, bidiFlags, *paint, typeface);
+    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
+}
+
+static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
+                              jint start, jint end, jint contextStart, jint contextEnd,
+                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
+                              jlong typefaceHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
+    jint count = end - start;
+    jint contextCount = contextEnd - contextStart;
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars) + contextStart;
+    get_canvas(canvasHandle)->drawText(textArray, start - contextStart, count, contextCount,
+                                       x, y, bidiFlags, *paint, typeface);
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void drawPosTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                             jint index, jint count, jfloatArray pos, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    jchar* jchars = text ? env->GetCharArrayElements(text, NULL) : NULL;
+    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+    const char* textArray = reinterpret_cast<const char*>(jchars) + index;
+    get_canvas(canvasHandle)->drawPosText(textArray, posArray, count << 1, posCount, *paint);
+
+    if (text) {
+        env->ReleaseCharArrayElements(text, jchars, 0);
+    }
+    if (pos) {
+        env->ReleaseFloatArrayElements(pos, posArray, 0);
+    }
+}
+
+
+static void drawPosTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                              jfloatArray pos, jlong paintHandle) {
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    const jchar* jchars = text ? env->GetStringChars(text, NULL) : NULL;
+    int byteLength = text ? env->GetStringLength(text) : 0;
+    float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
+    int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
+
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+    get_canvas(canvasHandle)->drawPosText(textArray , posArray, byteLength << 1, posCount, *paint);
+
+    if (text) {
+        env->ReleaseStringChars(text, jchars);
+    }
+    if (pos) {
+        env->ReleaseFloatArrayElements(pos, posArray, 0);
+    }
+}
+
+class DrawTextOnPathFunctor {
+public:
+    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
+                float vOffset, const SkPaint& paint, const SkPath& path)
+            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+                paint(paint), path(path) {
+    }
+    void operator()(size_t start, size_t end) {
+        uint16_t glyphs[1];
+        for (size_t i = start; i < end; i++) {
+            glyphs[0] = layout.getGlyphId(i);
+            float x = hOffset + layout.getX(i);
+            float y = vOffset + layout.getY(i);
+            canvas->drawTextOnPath((const char*) glyphs, 1, path, x, y, paint);
+        }
+    }
+private:
+    const Layout& layout;
+    Canvas* canvas;
+    float hOffset;
+    float vOffset;
+    const SkPaint& paint;
+    const SkPath& path;
+};
+
+static void drawTextOnPath(Canvas* canvas, const char* text, int count, int bidiFlags,
+                           const SkPath& path, float hOffset, float vOffset,
+                           const SkPaint& paint, TypefaceImpl* typeface) {
+    SkPaint paintCopy(paint);
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, &paintCopy, bidiFlags, typeface);
+    layout.doLayout((uint16_t*)text, 0, count, count, css);
+    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
+
+    // Set align to left for drawing, as we don't want individual
+    // glyphs centered or right-aligned; the offset above takes
+    // care of all alignment.
+    paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
+    MinikinUtils::forFontRun(layout, &paintCopy, f);
+}
+
+static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
+                                jint index, jint count, jlong pathHandle, jfloat hOffset,
+                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
+                                jlong typefaceHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    jchar* jchars = env->GetCharArrayElements(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+
+    drawTextOnPath(get_canvas(canvasHandle), textArray + index, count, bidiFlags, *path,
+                   hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseCharArrayElements(text, jchars, 0);
+}
+
+static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
+                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
+                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
+    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
+    SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
+    const jchar* jchars = env->GetStringChars(text, NULL);
+    const char* textArray = reinterpret_cast<const char*>(jchars);
+    int count = env->GetStringLength(text);
+
+    drawTextOnPath(get_canvas(canvasHandle), textArray, count, bidiFlags, *path,
+                   hOffset, vOffset, *paint, typeface);
+
+    env->ReleaseStringChars(text, jchars);
+}
+
+static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
+    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+}
+
+static void freeCaches(JNIEnv* env, jobject) {
+    SkGraphics::PurgeFontCache();
+}
+
+static void freeTextLayoutCaches(JNIEnv* env, jobject) {
+    Layout::purgeCaches();
+}
+
+}; // namespace CanvasJNI
+
+static JNINativeMethod gMethods[] = {
+    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
+    {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
+    {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
+    {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
+    {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
+    {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
+    {"native_save","(JI)I", (void*) CanvasJNI::save},
+    {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+    {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
+    {"native_restore","(J)V", (void*) CanvasJNI::restore},
+    {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+    {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
+    {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
+    {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
+    {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
+    {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
+    {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
+    {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
+    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+    {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+    {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+    {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+    {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
+    {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
+    {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
+    {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
+    {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
+    {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+    {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+    {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+    {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+    {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+    {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+    {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+    {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+    {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
+    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+    {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
+    {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+    {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+    {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
+    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
+    {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
+    {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
+    {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
+    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
+};
+
+int register_android_graphics_Canvas(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
index f827907..eb8f6dd 100644
--- a/core/jni/android_graphics_Picture.cpp
+++ b/core/jni/android_graphics_Picture.cpp
@@ -51,7 +51,7 @@
 
 static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
                                           jlong pictureHandle) {
-    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
     Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
     SkASSERT(canvas);
     SkASSERT(picture);
@@ -84,12 +84,7 @@
 static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
                                                      jint w, jint h) {
     Picture* pict = reinterpret_cast<Picture*>(pictHandle);
-    // beginRecording does not ref its return value, it just returns it.
-    SkCanvas* canvas = pict->beginRecording(w, h);
-    // the java side will wrap this guy in a Canvas.java, which will call
-    // unref in its finalizer, so we have to ref it here, so that both that
-    // Canvas.java and our picture can both be owners
-    canvas->ref();
+    Canvas* canvas = pict->beginRecording(w, h);
     return reinterpret_cast<jlong>(canvas);
 }
 
diff --git a/core/res/res/anim/launch_task_behind_background.xml b/core/res/res/anim/launch_task_behind_background.xml
new file mode 100644
index 0000000..358511f
--- /dev/null
+++ b/core/res/res/anim/launch_task_behind_background.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+
+    <translate android:fromYDelta="110%" android:toYDelta="66%"
+               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+               android:interpolator="@interpolator/decelerate_quint"
+               android:startOffset="50"
+               android:duration="300" />
+
+    <translate android:fromYDelta="0%" android:toYDelta="167%"
+               android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+               android:interpolator="@interpolator/accelerate_quint"
+               android:startOffset="433"
+               android:duration="300" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/launch_task_behind_source.xml b/core/res/res/anim/launch_task_behind_source.xml
new file mode 100644
index 0000000..426ee5d
--- /dev/null
+++ b/core/res/res/anim/launch_task_behind_source.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.6"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/accelerate_cubic"
+        android:duration="133"/>
+
+    <translate android:fromYDelta="0" android:toYDelta="10%"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/accelerate_cubic"
+        android:duration="350"/>
+
+    <scale android:fromXScale="1.0" android:toXScale="0.9"
+        android:fromYScale="1.0" android:toYScale="0.9"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:pivotX="50%p" android:pivotY="50%p"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="350" />
+
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.6"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="133"/>
+
+    <translate android:fromYDelta="0%" android:toYDelta="-10%"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="350"/>
+
+    <scale android:fromXScale="1.0" android:toXScale="1.1"
+        android:fromYScale="1.0" android:toYScale="1.1"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:pivotX="50%p" android:pivotY="50%p"
+        android:interpolator="@interpolator/decelerate_cubic"
+        android:startOffset="433"
+        android:duration="350" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ed228e4..1b18997 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1892,6 +1892,14 @@
         <!--  When opening an activity in a new task, this is the animation that is
               run on the activity of the old task (which is exiting the screen). -->
         <attr name="taskOpenExitAnimation" format="reference" />
+        <!--  When opening an activity in a new task using Intent/FLAG_ACTIVITY_LAUNCH_BEHIND,
+              this is the animation that is run on the activity of the new task (which is
+              entering the screen and then leaving). -->
+        <attr name="launchTaskBehindBackgroundAnimation" format="reference" />
+        <!--  When opening an activity in a new task using Intent.FLAG_ACTIVITY_LAUNCH_BEHIND,
+              this is the animation that is run on the activity of the old task (which is
+              already on the screen and then stays on). -->
+        <attr name="launchTaskBehindSourceAnimation" format="reference" />
         <!--  When closing the last activity of a task, this is the animation that is
               run on the activity of the next task (which is entering the screen). -->
         <attr name="taskCloseEnterAnimation" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c9c29ca..348a6a0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1513,6 +1513,7 @@
      See {@link com.android.server.notification.NotificationSignalExtractor} -->
     <string-array name="config_notificationSignalExtractors">
         <item>com.android.server.notification.ValidateNotificationPeople</item>
+        <item>com.android.server.notification.PackagePriorityExtractor</item>
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
     </string-array>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fee5c43..cb16b5f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2236,6 +2236,8 @@
   <public type="attr" name="multiArch" />
   <public type="attr" name="touchscreenBlocksFocus" />
   <public type="attr" name="windowElevation" />
+  <public type="attr" name="launchTaskBehindBackgroundAnimation" />
+  <public type="attr" name="launchTaskBehindSourceAnimation" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 32065a18..a8b634c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -82,6 +82,8 @@
         <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
         <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
         <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
+        <item name="launchTaskBehindBackgroundAnimation">@anim/launch_task_behind_background</item>
+        <item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
         <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
         <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
         <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 99596ef..5211762 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -158,7 +158,7 @@
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
-        mNativeCanvasWrapper = initCanvas(nativeCanvas);
+        mNativeCanvasWrapper = nativeCanvas;
         mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
@@ -921,7 +921,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawRGB(int r, int g, int b) {
-        native_drawRGB(mNativeCanvasWrapper, r, g, b);
+        drawColor(Color.rgb(r, g, b));
     }
 
     /**
@@ -934,7 +934,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawARGB(int a, int r, int g, int b) {
-        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
+        drawColor(Color.argb(a, r, g, b));
     }
 
     /**
@@ -944,7 +944,7 @@
      * @param color the color to draw onto the canvas
      */
     public void drawColor(int color) {
-        native_drawColor(mNativeCanvasWrapper, color);
+        native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
     /**
@@ -1298,13 +1298,28 @@
      */
     public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
             @Nullable Paint paint) {
-        if (dst == null) {
-            throw new NullPointerException();
-        }
-        throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                          paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
-    }
+      if (dst == null) {
+          throw new NullPointerException();
+      }
+      throwIfCannotDraw(bitmap);
+      final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+      float left, top, right, bottom;
+      if (src == null) {
+          left = top = 0;
+          right = bitmap.getWidth();
+          bottom = bitmap.getHeight();
+      } else {
+          left = src.left;
+          right = src.right;
+          top = src.top;
+          bottom = src.bottom;
+      }
+
+      native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+              dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+              bitmap.mDensity);
+  }
 
     /**
      * Draw the specified bitmap, scaling/translating automatically to fill
@@ -1334,8 +1349,23 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
-                paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+        final long nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+        int left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
+        }
+
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, right, bottom,
+            dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
+            bitmap.mDensity);
     }
 
     /**
@@ -1863,7 +1893,6 @@
     public static native void freeTextLayoutCaches();
 
     private static native long initRaster(long nativeBitmapOrZero);
-    private static native long initCanvas(long canvasHandle);
     private static native void native_setBitmap(long canvasHandle,
                                                 long bitmapHandle,
                                                 boolean copyState);
@@ -1916,11 +1945,6 @@
     private static native boolean native_quickReject(long nativeCanvas,
                                                      float left, float top,
                                                      float right, float bottom);
-    private static native void native_drawRGB(long nativeCanvas, int r, int g,
-                                              int b);
-    private static native void native_drawARGB(long nativeCanvas, int a, int r,
-                                               int g, int b);
-    private static native void native_drawColor(long nativeCanvas, int color);
     private static native void native_drawColor(long nativeCanvas, int color,
                                                 int mode);
     private static native void native_drawPaint(long nativeCanvas,
@@ -1962,16 +1986,9 @@
                                                  int screenDensity,
                                                  int bitmapDensity);
     private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
-                                                 Rect src, RectF dst,
-                                                 long nativePaintOrZero,
-                                                 int screenDensity,
-                                                 int bitmapDensity);
-    private static native void native_drawBitmap(long nativeCanvas,
-                                                 long nativeBitmap,
-                                                 Rect src, Rect dst,
-                                                 long nativePaintOrZero,
-                                                 int screenDensity,
-                                                 int bitmapDensity);
+            float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float dstLeft, float dstTop, float dstRight, float dstBottom,
+            long nativePaintOrZero, int screenDensity, int bitmapDensity);
     private static native void native_drawBitmap(long nativeCanvas, int[] colors,
                                                 int offset, int stride, float x,
                                                  float y, int width, int height,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index c9f541b..7fa1975 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2351,7 +2351,8 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    const Rect& bounds = vertexBuffer.getBounds();
+    Rect bounds(vertexBuffer.getBounds());
+    bounds.translate(translateX, translateY);
     dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
 
     int color = paint->getColor();
diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDevice.java
index 1fd27fe..96d6196 100644
--- a/media/java/android/media/AudioDevice.java
+++ b/media/java/android/media/AudioDevice.java
@@ -66,8 +66,20 @@
         return mConfig.port().address();
     }
 
+    /** @hide */
+    public static int convertDeviceTypeToInternalDevice(int deviceType) {
+        return EXT_TO_INT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+    }
+
+    /** @hide */
+    public static int convertInternalDeviceToDeviceType(int intDevice) {
+        return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, DEVICE_TYPE_UNKNOWN);
+    }
+
     private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
 
+    private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
+
     static {
         INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, DEVICE_TYPE_BUILTIN_EARPIECE);
@@ -110,6 +122,27 @@
         // not covered here, legacy
         //AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
         //AudioSystem.DEVICE_IN_REMOTE_SUBMIX
+
+        // privileges mapping to output device
+        EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_EARPIECE, AudioSystem.DEVICE_OUT_EARPIECE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_SPEAKER, AudioSystem.DEVICE_OUT_SPEAKER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADSET, AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADPHONES, AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_ANALOG, AudioSystem.DEVICE_OUT_LINE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_DIGITAL, AudioSystem.DEVICE_OUT_SPDIF);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_OUT_BLUETOOTH_SCO);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI, AudioSystem.DEVICE_OUT_HDMI);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI_ARC, AudioSystem.DEVICE_OUT_HDMI_ARC);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_DEVICE, AudioSystem.DEVICE_OUT_USB_DEVICE);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_ACCESSORY, AudioSystem.DEVICE_OUT_USB_ACCESSORY);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_DOCK, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM, AudioSystem.DEVICE_OUT_FM);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+        EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
     }
 }
 
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 025d354..79be108 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -78,9 +78,9 @@
     public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
     public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
     public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
-    /** @hide */
+    /** @hide  CANDIDATE FOR PUBLIC API */
     public static final int CHANNEL_OUT_SIDE_LEFT =         0x800;
-    /** @hide */
+    /** @hide  CANDIDATE FOR PUBLIC API */
     public static final int CHANNEL_OUT_SIDE_RIGHT =       0x1000;
     /** @hide */
     public static final int CHANNEL_OUT_TOP_CENTER =       0x2000;
@@ -128,6 +128,35 @@
             CHANNEL_OUT_LOW_FREQUENCY);
     // CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
 
+    /**
+     * @hide
+     * Return the number of channels from an output channel mask
+     * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+     * @return number of channels for the mask
+     */
+    public static int channelCountFromOutChannelMask(int mask) {
+        return Integer.bitCount(mask);
+    }
+    /**
+     * @hide
+     * Return a channel mask ready to be used by native code
+     * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+     * @return a native channel mask
+     */
+    public static int convertChannelOutMaskToNativeMask(int javaMask) {
+        return (javaMask >> 2);
+    }
+
+    /**
+     * @hide
+     * Return a java output channel mask
+     * @param mask a native channel mask
+     * @return a combination of the CHANNEL_OUT_* definitions
+     */
+    public static int convertNativeChannelMaskToOutMask(int nativeMask) {
+        return (nativeMask << 2);
+    }
+
     public static final int CHANNEL_IN_DEFAULT = 1;
     // These directly match native
     public static final int CHANNEL_IN_LEFT = 0x4;
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 9b381cc..9fa3f50 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -1282,7 +1282,7 @@
     /**
      * @hide
      */
-    public int byteArrayToInt(byte[] valueBuf) {
+    public static int byteArrayToInt(byte[] valueBuf) {
         return byteArrayToInt(valueBuf, 0);
 
     }
@@ -1290,7 +1290,7 @@
     /**
      * @hide
      */
-    public int byteArrayToInt(byte[] valueBuf, int offset) {
+    public static int byteArrayToInt(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
         return converter.getInt(offset);
@@ -1300,7 +1300,7 @@
     /**
      * @hide
      */
-    public byte[] intToByteArray(int value) {
+    public static byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
         converter.putInt(value);
@@ -1310,14 +1310,14 @@
     /**
      * @hide
      */
-    public short byteArrayToShort(byte[] valueBuf) {
+    public static short byteArrayToShort(byte[] valueBuf) {
         return byteArrayToShort(valueBuf, 0);
     }
 
     /**
      * @hide
      */
-    public short byteArrayToShort(byte[] valueBuf, int offset) {
+    public static short byteArrayToShort(byte[] valueBuf, int offset) {
         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
         converter.order(ByteOrder.nativeOrder());
         return converter.getShort(offset);
@@ -1327,7 +1327,7 @@
     /**
      * @hide
      */
-    public byte[] shortToByteArray(short value) {
+    public static byte[] shortToByteArray(short value) {
         ByteBuffer converter = ByteBuffer.allocate(2);
         converter.order(ByteOrder.nativeOrder());
         short sValue = (short) value;
@@ -1338,7 +1338,7 @@
     /**
      * @hide
      */
-    public byte[] concatArrays(byte[]... arrays) {
+    public static byte[] concatArrays(byte[]... arrays) {
         int len = 0;
         for (byte[] a : arrays) {
             len += a.length;
diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
index 6b20006..136761b 100644
--- a/media/java/android/media/audiofx/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -16,9 +16,13 @@
 
 package android.media.audiofx;
 
+import android.media.AudioDevice;
+import android.media.AudioFormat;
 import android.media.audiofx.AudioEffect;
 import android.util.Log;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.StringTokenizer;
 
 
@@ -44,8 +48,10 @@
 public class Virtualizer extends AudioEffect {
 
     private final static String TAG = "Virtualizer";
+    private final static boolean DEBUG = false;
 
-    // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h
+    // These constants must be synchronized with those in
+    //        system/media/audio_effects/include/audio_effects/effect_virtualizer.h
     /**
      * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
      */
@@ -55,6 +61,21 @@
      * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
      */
     public static final int PARAM_STRENGTH = 1;
+    /**
+     * @hide
+     * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
+     */
+    public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
+    /**
+     * @hide
+     * Parameter ID to force the virtualization mode to be that of a specific device
+     */
+    public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
+    /**
+     * @hide
+     * Parameter ID to query the current virtualization mode.
+     */
+    public static final int PARAM_VIRTUALIZATION_MODE = 4;
 
     /**
      * Indicates if strength parameter is supported by the virtualizer engine
@@ -145,6 +166,223 @@
     }
 
     /**
+     * Checks if a configuration is supported, and query the virtual speaker angles.
+     * @param inputChannelMask
+     * @param deviceType
+     * @param angles if non-null: array in which the angles will be written. If null, no angles
+     *    are returned
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        // parameter check
+        if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
+            throw (new IllegalArgumentException(
+                    "Virtualizer: illegal CHANNEL_INVALID channel mask"));
+        }
+        int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
+                AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
+        int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
+        if ((angles != null) && (angles.length < (nbChannels * 3))) {
+            Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
+                    + nbChannels + ")");
+            throw (new IllegalArgumentException(
+                    "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
+                    + ", should be " + (nbChannels * 3)));
+        }
+
+        ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
+        paramsConverter.order(ByteOrder.nativeOrder());
+        paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
+        // convert channel mask to internal native representation
+        paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
+        // convert Java device type to internal representation
+        paramsConverter.putInt(AudioDevice.convertDeviceTypeToInternalDevice(deviceType));
+        // allocate an array to store the results
+        byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
+
+        // call into the effect framework
+        int status = getParameter(paramsConverter.array(), result);
+        if (DEBUG) {
+            Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
+                    + Integer.toHexString(deviceType) + ") returns " + status);
+        }
+
+        if (status >= 0) {
+            if (angles != null) {
+                // convert and copy the results
+                ByteBuffer resultConverter = ByteBuffer.wrap(result);
+                resultConverter.order(ByteOrder.nativeOrder());
+                for (int i = 0 ; i < nbChannels ; i++) {
+                    // write the channel mask
+                    angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
+                            resultConverter.getInt((i * 4 * 3)));
+                    // write the azimuth
+                    angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
+                    // write the elevation
+                    angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
+                    if (DEBUG) {
+                        Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
+                                + " at az=" + angles[3*i+1] + "deg"
+                                + " elev="  + angles[3*i+2] + "deg");
+                    }
+                }
+            }
+            return true;
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            // a BAD_VALUE return from getParameter indicates the configuration is not supported
+            // don't throw an exception, just return false
+            return false;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
+        return false;
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Checks if the combination of a channel mask and device type is supported by this virtualizer.
+     * Some virtualizer implementations may only support binaural processing (i.e. only support
+     * headphone output), some may support transaural processing (i.e. for speaker output) for the
+     * built-in speakers. Use this method to query the virtualizer implementation capabilities.
+     * @param inputChannelMask the channel mask of the content to virtualize.
+     * @param deviceType the device type for which virtualization processing is to be performed.
+     *    Valid values are the device types defined in {@link AudioDevice}.
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise.
+     *    <br>An indication that a certain channel mask is not supported doesn't necessarily mean
+     *    you cannot play content with that channel mask, it more likely implies the content will
+     *    be downmixed before being virtualized. For instance a virtualizer that only supports a
+     *    mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
+     *    will still be able to process content with a mask of
+     *    {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
+     *    then will virtualize, as opposed to virtualizing each channel individually.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean canVirtualize(int inputChannelMask, int deviceType)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        return getAnglesInt(inputChannelMask, deviceType, null);
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
+     * mask and device type.
+     * If the virtualization configuration (mask and device) is supported (see
+     * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
+     * definition of each virtual speaker and its azimuth and elevation angles relative to the
+     * listener.
+     * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
+     * @param inputChannelMask the channel mask of the content to virtualize.
+     * @param deviceType the device type for which virtualization processing is to be performed.
+     *    Valid values are the device types defined in {@link AudioDevice}.
+     * @param angles a non-null array whose length is 3 times the number of channels in the channel
+     *    mask.
+     *    If the method indicates the configuration is supported, the array will contain upon return
+     *    triplets of values: for each channel <code>i</code> among the channels of the mask:
+     *    <ul>
+     *      <li>the element at index <code>3*i</code> in the array contains the speaker
+     *          identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
+     *      <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
+     *          expressed in degrees, where 0 is the direction the listener faces, 180 is behind
+     *          the listener, and -90 is to her/his left,</li>
+     *      <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
+     *          where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
+     *          directly below the listener.</li>
+     * @return true if the combination of channel mask and output device type is supported, false
+     *    otherwise.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean getSpeakerAngles(int inputChannelMask, int deviceType, int[] angles)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        if (angles == null) {
+            throw (new IllegalArgumentException(
+                    "Virtualizer: illegal null channel / angle array"));
+        }
+
+        return getAnglesInt(inputChannelMask, deviceType, angles);
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Forces the virtualizer effect to use the processing mode used for the given device type.
+     * The effect must be enabled for the forced mode to be applied.
+     * @param deviceType one of the device types defined in {@link AudioDevice}.
+     *     Use {@link AudioDevice#DEVICE_TYPE_UNKNOWN} to return to the non-forced mode.
+     * @return true if the processing mode for the device type is supported, and it is successfully
+     *     set, or forcing was successfully disabled with {@link AudioDevice#DEVICE_TYPE_UNKNOWN},
+     *     false otherwise.
+     * @throws IllegalStateException
+     * @throws IllegalArgumentException
+     * @throws UnsupportedOperationException
+     */
+    public boolean forceVirtualizationMode(int deviceType)
+            throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+        // convert Java device type to internal representation
+        int internalDevice = AudioDevice.convertDeviceTypeToInternalDevice(deviceType);
+
+        int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
+
+        if (status >= 0) {
+            return true;
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            // a BAD_VALUE return from setParameter indicates the mode can't be forced to that
+            // of this device, don't throw an exception, just return false
+            return false;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
+        return false;
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Return the device type which reflects the virtualization mode being used, if any.
+     * @return a device type (as defined in {@link AudioDevice}) which reflects the virtualization
+     *     mode being used.
+     *     If virtualization is not active, the device type will be
+     *     {@link AudioDevice#DEVICE_TYPE_UNKNOWN}. Virtualization may not be active either because
+     *     the effect is not enabled or because the current output device is not compatible with
+     *     this virtualization implementation.
+     */
+    public int getVirtualizationMode() {
+        int[] value = new int[1];
+        int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
+        if (status >= 0) {
+            return AudioDevice.convertInternalDeviceToDeviceType(value[0]);
+        } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+            return AudioDevice.DEVICE_TYPE_UNKNOWN;
+        } else {
+            // something wrong may have happened
+            checkStatus(status);
+        }
+        // unexpected virtualizer behavior
+        Log.e(TAG, "unexpected status code " + status
+                + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
+        return AudioDevice.DEVICE_TYPE_UNKNOWN;
+    }
+
+    /**
      * The OnParameterChangeListener interface defines a method called by the Virtualizer when a
      * parameter value has changed.
      */
diff --git a/packages/PrintSpooler/res/layout/preview_page.xml b/packages/PrintSpooler/res/layout/preview_page.xml
index 0e314d1..bd56371 100644
--- a/packages/PrintSpooler/res/layout/preview_page.xml
+++ b/packages/PrintSpooler/res/layout/preview_page.xml
@@ -31,7 +31,7 @@
     <RelativeLayout
         android:id="@+id/page_footer"
         android:layout_width="fill_parent"
-        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:layout_height="32dip"
         android:background="@*android:color/material_grey_500"
         android:orientation="horizontal">
 
@@ -40,7 +40,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textAppearance="?android:attr/textAppearanceSmall"
             android:textColor="?android:attr/textColorPrimary">
         </TextView>
 
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 8896a7b..3905646 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -29,7 +29,7 @@
         android:layout_height="wrap_content"
         android:padding="16dip"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorForegroundInverse">
+        android:background="?android:attr/colorPrimary">
 
         <Spinner
             android:id="@+id/destination_spinner"
@@ -51,7 +51,7 @@
         android:paddingEnd="16dip"
         android:orientation="horizontal"
         android:elevation="@dimen/preview_controls_elevation"
-        android:background="?android:attr/colorForegroundInverse">
+        android:background="?android:attr/colorPrimary">
 
         <TextView
             android:layout_width="wrap_content"
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 2da0714..05a04ce 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -22,7 +22,7 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:elevation="@dimen/preview_controls_elevation"
-    android:background="?android:attr/colorForegroundInverse">
+    android:background="?android:attr/colorPrimary">
 
         <LinearLayout
          android:id="@+id/draggable_content"
@@ -61,6 +61,8 @@
                      android:layout_width="fill_parent"
                      android:layout_height="wrap_content"
                      style="?android:attr/editTextStyle"
+                     android:singleLine="true"
+                     android:ellipsize="end"
                      android:inputType="numberDecimal">
                  </view>
 
@@ -88,8 +90,7 @@
                  <Spinner
                      android:id="@+id/paper_size_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -116,8 +117,7 @@
                  <Spinner
                      android:id="@+id/color_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -144,8 +144,7 @@
                  <Spinner
                      android:id="@+id/orientation_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -173,8 +172,7 @@
                  <Spinner
                      android:id="@+id/range_options_spinner"
                      android:layout_width="fill_parent"
-                     android:layout_height="wrap_content"
-                     style="@style/PrintOptionSpinnerStyle">
+                     android:layout_height="wrap_content">
                  </Spinner>
 
              </LinearLayout>
@@ -207,7 +205,8 @@
                      android:layout_width="fill_parent"
                      android:layout_height="wrap_content"
                      android:layout_gravity="bottom|fill_horizontal"
-                     style="@style/PrintOptionEditTextStyle"
+                     android:singleLine="true"
+                     android:ellipsize="end"
                      android:visibility="visible"
                      android:inputType="textNoSuggestions">
                  </view>
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
index 1fb221a..14403a1 100644
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -17,8 +17,6 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
-    android:paddingStart="8dip"
-    android:paddingEnd="8dip"
     android:minHeight="?android:attr/listPreferredItemHeightSmall"
     android:orientation="vertical"
     android:gravity="start|center_vertical">
@@ -27,12 +25,13 @@
         android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        style="?android:attr/spinnerDropDownItemStyle"
         android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorPrimary"
         android:singleLine="true"
         android:ellipsize="end"
         android:textIsSelectable="false"
         android:gravity="top|left"
-        android:textColor="?android:attr/textColorPrimary"
         android:duplicateParentState="true">
     </TextView>
 
@@ -40,12 +39,13 @@
         android:id="@+id/subtitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        style="?android:attr/spinnerDropDownItemStyle"
         android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary"
         android:singleLine="true"
         android:ellipsize="end"
         android:textIsSelectable="false"
         android:visibility="gone"
-        android:textColor="?android:attr/textColorPrimary"
         android:duplicateParentState="true">
     </TextView>
 
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
deleted file mode 100644
index 9637847..0000000
--- a/packages/PrintSpooler/res/values/styles.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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>
-
-    <style name="PrintOptionSpinnerStyle">
-        <item name="android:paddingTop">0dip</item>
-        <item name="android:paddingBottom">0dip</item>
-        <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
-    </style>
-
-    <style name="PrintOptionEditTextStyle">
-
-         <item name="android:singleLine">true</item>
-         <item name="android:ellipsize">end</item>
-    </style>
-
-</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index e1e6c44..227451f 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,11 +16,18 @@
 
 <resources>
 
-    <style name="PrintActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+    <style name="PrintActivity" parent="@android:style/Theme.Material">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+
         <item name="android:backgroundDimEnabled">false</item>
+
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+        <item name="android:colorAccent">@*android:color/material_dark_teal_A400</item>
     </style>
 
     <style name="SelectPrinterActivityTheme" parent="@android:style/Theme.DeviceDefault.Light">
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index 555aa97..efb030e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -22,6 +22,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
 import com.android.printspooler.R;
 
 /**
@@ -301,6 +302,7 @@
             mSummaryContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mDraggableContent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mMoreOptionsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            ensureImeClosedAndInputFocusCleared();
         }
         if ((mDragProgress > 0 && progress == 0)
                 || (mDragProgress < 1.0f && progress == 1.0f)) {
@@ -351,6 +353,18 @@
         }
     }
 
+    private void ensureImeClosedAndInputFocusCleared() {
+        View focus = findFocus();
+        if (focus != null) {
+            InputMethodManager imm = (InputMethodManager) mContext.getSystemService(
+                    Context.INPUT_METHOD_SERVICE);
+            if (imm.isActive(focus)) {
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+            focus.clearFocus();
+        }
+    }
+
     private final class DragCallbacks extends ViewDragHelper.Callback {
         @Override
         public boolean tryCaptureView(View child, int pointerId) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6554ed3..86f8777 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3199,13 +3199,17 @@
                     break;
                 }
                 case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
-                        break;
+                    if (msg.arg1 == 0) {
+                        setProvNotificationVisibleIntent(false, msg.arg2, 0, null, null);
+                    } else {
+                        NetworkAgentInfo nai = mNetworkForNetId.get(msg.arg2);
+                        if (nai == null) {
+                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+                            break;
+                        }
+                        setProvNotificationVisibleIntent(true, msg.arg2, nai.networkInfo.getType(),
+                                nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     }
-                    setProvNotificationVisibleIntent(msg.arg1 != 0, nai.networkInfo.getType(),
-                            nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     break;
                 }
                 case NetworkStateTracker.EVENT_STATE_CHANGED: {
@@ -4958,10 +4962,19 @@
                     break;
             }
         }
-        setProvNotificationVisibleIntent(visible, networkType, extraInfo, pendingIntent);
+        // Concatenate the range of types onto the range of NetIDs.
+        int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+        setProvNotificationVisibleIntent(visible, id, networkType, extraInfo, pendingIntent);
     }
 
-    private void setProvNotificationVisibleIntent(boolean visible, int networkType,
+    /**
+     * Show or hide network provisioning notificaitons.
+     *
+     * @param id an identifier that uniquely identifies this notification.  This must match
+     *         between show and hide calls.  We use the NetID value but for legacy callers
+     *         we concatenate the range of types with the range of NetIDs.
+     */
+    private void setProvNotificationVisibleIntent(boolean visible, int id, int networkType,
             String extraInfo, PendingIntent intent) {
         if (DBG) {
             log("setProvNotificationVisibleIntent: E visible=" + visible + " networkType=" +
@@ -5008,14 +5021,14 @@
             notification.contentIntent = intent;
 
             try {
-                notificationManager.notify(NOTIFICATION_ID, networkType, notification);
+                notificationManager.notify(NOTIFICATION_ID, id, notification);
             } catch (NullPointerException npe) {
                 loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
                 npe.printStackTrace();
             }
         } else {
             try {
-                notificationManager.cancel(NOTIFICATION_ID, networkType);
+                notificationManager.cancel(NOTIFICATION_ID, id);
             } catch (NullPointerException npe) {
                 loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
                 npe.printStackTrace();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 543384f..8ef6dd6 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5655,6 +5655,11 @@
     }
 
     @Override
+    public final void notifyLaunchTaskBehindComplete(IBinder token) {
+        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
+    }
+
+    @Override
     public String getCallingPackage(IBinder token) {
         synchronized (this) {
             ActivityRecord r = getCallingRecordLocked(token);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 46521c5..6c47922 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -167,6 +167,8 @@
     ActivityContainer mInitialActivityContainer;
 
     TaskDescription taskDescription; // the recents information for this activity
+    boolean mLaunchTaskBehind; // this activity is actively being launched with
+        // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
 
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
@@ -400,6 +402,7 @@
         mInitialActivityContainer = container;
         if (options != null) {
             pendingOptions = new ActivityOptions(options);
+            mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind();
         }
 
         // This starts out true, since the initial state of an activity
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7e5cac7..32f2624 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1135,19 +1135,15 @@
         return true;
     }
 
-    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
-        ActivityRecord r = topRunningActivityLocked(null);
-        if (r != null) {
-            ensureActivitiesVisibleLocked(r, starting, null, configChanges);
-        }
-    }
-
     /**
      * Make sure that all activities that need to be visible (that is, they
      * currently can be seen by the user) actually are.
      */
-    final void ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting,
-            String onlyThisProcess, int configChanges) {
+    final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
+        ActivityRecord top = topRunningActivityLocked(null);
+        if (top == null) {
+            return;
+        }
         if (DEBUG_VISBILITY) Slog.v(
                 TAG, "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
@@ -1179,37 +1175,34 @@
                     continue;
                 }
                 aboveTop = false;
-                if (!behindFullscreen) {
+                // mLaunchingBehind: Activities launching behind are at the back of the task stack
+                // but must be drawn initially for the animation as though they were visible.
+                if (!behindFullscreen || r.mLaunchTaskBehind) {
                     if (DEBUG_VISBILITY) Slog.v(
                             TAG, "Make visible? " + r + " finishing=" + r.finishing
                             + " state=" + r.state);
 
-                    final boolean doThisProcess = onlyThisProcess == null
-                            || onlyThisProcess.equals(r.processName);
-
                     // First: if this is not the current activity being started, make
                     // sure it matches the current configuration.
-                    if (r != starting && doThisProcess) {
+                    if (r != starting) {
                         ensureActivityConfigurationLocked(r, 0);
                     }
 
                     if (r.app == null || r.app.thread == null) {
-                        if (onlyThisProcess == null || onlyThisProcess.equals(r.processName)) {
-                            // This activity needs to be visible, but isn't even
-                            // running...  get it started, but don't resume it
-                            // at this point.
-                            if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
-                            if (r != starting) {
-                                r.startFreezingScreenLocked(r.app, configChanges);
-                            }
-                            if (!r.visible) {
-                                if (DEBUG_VISBILITY) Slog.v(
-                                        TAG, "Starting and making visible: " + r);
-                                setVisibile(r, true);
-                            }
-                            if (r != starting) {
-                                mStackSupervisor.startSpecificActivityLocked(r, false, false);
-                            }
+                        // This activity needs to be visible, but isn't even
+                        // running...  get it started, but don't resume it
+                        // at this point.
+                        if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
+                        if (r != starting) {
+                            r.startFreezingScreenLocked(r.app, configChanges);
+                        }
+                        if (!r.visible || r.mLaunchTaskBehind) {
+                            if (DEBUG_VISBILITY) Slog.v(
+                                    TAG, "Starting and making visible: " + r);
+                            setVisibile(r, true);
+                        }
+                        if (r != starting) {
+                            mStackSupervisor.startSpecificActivityLocked(r, false, false);
                         }
 
                     } else if (r.visible) {
@@ -1225,7 +1218,7 @@
                             }
                         } catch(RemoteException e) {
                         }
-                    } else if (onlyThisProcess == null) {
+                    } else {
                         // This activity is not currently visible, but is running.
                         // Tell it to become visible.
                         r.visible = true;
@@ -1648,7 +1641,9 @@
                 } else {
                     mWindowManager.prepareAppTransition(prev.task == next.task
                             ? AppTransition.TRANSIT_ACTIVITY_OPEN
-                            : AppTransition.TRANSIT_TASK_OPEN, false);
+                            : next.mLaunchTaskBehind
+                                    ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
+                                    : AppTransition.TRANSIT_TASK_OPEN, false);
                 }
             }
             if (false) {
@@ -1852,17 +1847,17 @@
 
         mTaskHistory.remove(task);
         // Now put task at top.
-        int stackNdx = mTaskHistory.size();
+        int taskNdx = mTaskHistory.size();
         if (!isCurrentProfileLocked(task.userId)) {
             // Put non-current user tasks below current user tasks.
-            while (--stackNdx >= 0) {
-                if (!isCurrentProfileLocked(mTaskHistory.get(stackNdx).userId)) {
+            while (--taskNdx >= 0) {
+                if (!isCurrentProfileLocked(mTaskHistory.get(taskNdx).userId)) {
                     break;
                 }
             }
-            ++stackNdx;
+            ++taskNdx;
         }
-        mTaskHistory.add(stackNdx, task);
+        mTaskHistory.add(taskNdx, task);
         updateTaskMovement(task, true);
     }
 
@@ -1870,7 +1865,8 @@
             boolean doResume, boolean keepCurTransition, Bundle options) {
         TaskRecord rTask = r.task;
         final int taskId = rTask.taskId;
-        if (taskForIdLocked(taskId) == null || newTask) {
+        // mLaunchTaskBehind tasks get placed at the back of the task stack.
+        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
             // Last activity in task had been removed or ActivityManagerService is reusing task.
             // Insert or replace.
             // Might not even be in.
@@ -1895,7 +1891,8 @@
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                                r.userId, r.info.configChanges, task.voiceSession != null);
+                                r.userId, r.info.configChanges, task.voiceSession != null,
+                                r.mLaunchTaskBehind);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
@@ -1949,14 +1946,16 @@
                 mNoAnimActivities.add(r);
             } else {
                 mWindowManager.prepareAppTransition(newTask
-                        ? AppTransition.TRANSIT_TASK_OPEN
+                        ? r.mLaunchTaskBehind
+                                ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
+                                : AppTransition.TRANSIT_TASK_OPEN
                         : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                 mNoAnimActivities.remove(r);
             }
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges, task.voiceSession != null);
+                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1972,7 +1971,12 @@
                     == ActivityOptions.ANIM_SCENE_TRANSITION) {
                 doShow = false;
             }
-            if (SHOW_APP_STARTING_PREVIEW && doShow) {
+            if (r.mLaunchTaskBehind) {
+                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
+                // tell WindowManager that r is visible even though it is at the back of the stack.
+                mWindowManager.setAppVisibility(r.appToken, true);
+                ensureActivitiesVisibleLocked(null, 0);
+            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
                 // "has the same starting icon" as the next one.  This allows the
                 // window manager to keep the previous window it had previously
@@ -2003,7 +2007,7 @@
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
-                    r.info.configChanges, task.voiceSession != null);
+                    r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
             ActivityOptions.abort(options);
             options = null;
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bc184c6..7c8dd81 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -144,6 +144,7 @@
     static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
     static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
     static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12;
+    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 13;
 
     private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
@@ -1557,9 +1558,9 @@
                     break;
             }
         }
-        final int launchBehindFlags = Intent.FLAG_ACTIVITY_LAUNCH_BEHIND |
-                Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-        final boolean affiliateTask = (launchFlags & launchBehindFlags) == launchBehindFlags;
+
+        final boolean launchTaskBehind = r.mLaunchTaskBehind &&
+                (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
 
         if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new
@@ -1709,7 +1710,7 @@
                                 sourceStack.topActivity().task == sourceRecord.task)) {
                             // We really do want to push this one into the
                             // user's face, right now.
-                            if (affiliateTask && sourceRecord != null) {
+                            if (launchTaskBehind && sourceRecord != null) {
                                 intentActivity.setTaskToAffiliateWith(sourceRecord.task);
                             }
                             movedHome = true;
@@ -1886,7 +1887,7 @@
         boolean newTask = false;
         boolean keepCurTransition = false;
 
-        TaskRecord taskToAffiliate = affiliateTask && sourceRecord != null ?
+        TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
                 sourceRecord.task : null;
 
         // Should this be considered a new task?
@@ -1898,12 +1899,15 @@
             }
             newTask = true;
             targetStack = adjustStackFocus(r, newTask);
-            targetStack.moveToFront();
+            if (!launchTaskBehind) {
+                targetStack.moveToFront();
+            }
             if (reuseTask == null) {
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
-                        voiceSession, voiceInteractor, true), taskToAffiliate);
+                        voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
+                        taskToAffiliate);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
@@ -1997,7 +2001,10 @@
         ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
         targetStack.mLastPausedActivity = null;
         targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
-        mService.setFocusedActivityLocked(r);
+        if (!launchTaskBehind) {
+            // Don't set focus on an activity that's going to the back.
+            mService.setFocusedActivityLocked(r);
+        }
         return ActivityManager.START_SUCCESS;
     }
 
@@ -2394,7 +2401,8 @@
                 mWindowManager.addAppToken(0, r.appToken, taskId, stackId,
                         r.info.screenOrientation, r.fullscreen,
                         (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
-                        r.userId, r.info.configChanges, task.voiceSession != null);
+                        r.userId, r.info.configChanges, task.voiceSession != null,
+                        r.mLaunchTaskBehind);
             }
             mWindowManager.addTask(taskId, stackId, false);
         }
@@ -2642,6 +2650,19 @@
         return true;
     }
 
+    // Called when WindowManager has finished animating the launchingBehind activity to the back.
+    void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
+        r.mLaunchTaskBehind = false;
+        final TaskRecord task = r.task;
+        task.setLastThumbnail(task.stack.screenshotActivities(r));
+        mService.addRecentTaskLocked(task);
+        mWindowManager.setAppVisibility(r.appToken, false);
+    }
+
+    void scheduleLaunchTaskBehindComplete(IBinder token) {
+        mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
+    }
+
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
         // First the front stacks. In case any are not fullscreen and are in front of home.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3293,6 +3314,14 @@
                         ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
                     }
                 } break;
+                case LAUNCH_TASK_BEHIND_COMPLETE: {
+                    synchronized (mService) {
+                        ActivityRecord r = ActivityRecord.forToken((IBinder) msg.obj);
+                        if (r != null) {
+                            handleLaunchTaskBehindCompleteLocked(r);
+                        }
+                    }
+                } break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8289f5a..545723a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -148,8 +148,8 @@
     /**
      * Request ConnectivityService display provisioning notification.
      * arg1    = Whether to make the notification visible.
-     * obj     = Intent to be launched when notification selected by user.
-     * replyTo = NetworkAgentInfo.messenger so ConnectivityService can identify sender.
+     * arg2    = NetID.
+     * obj     = Intent to be launched when notification selected by user, null if !arg1.
      */
     public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 12;
 
@@ -447,9 +447,9 @@
             // Initiate notification to sign-in.
             Intent intent = new Intent(ACTION_SIGN_IN_REQUESTED);
             intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetworkAgentInfo.network.netId));
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, 0,
+            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
+                    mNetworkAgentInfo.network.netId,
                     PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
-            message.replyTo = mNetworkAgentInfo.messenger;
             mConnectivityServiceHandler.sendMessage(message);
         }
 
@@ -470,8 +470,8 @@
 
         @Override
         public void exit() {
-            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, 0, null);
-            message.replyTo = mNetworkAgentInfo.messenger;
+            Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
+                    mNetworkAgentInfo.network.netId, null);
             mConnectivityServiceHandler.sendMessage(message);
             mContext.unregisterReceiver(mUserRespondedBroadcastReceiver);
             mUserRespondedBroadcastReceiver = null;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 6cd4019..0546a55 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -28,6 +28,12 @@
         if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
             return lhs.isRecentlyIntrusive() ? -1 : 1;
         }
+        final int leftPackagePriority = lhs.getPackagePriority();
+        final int rightPackagePriority = rhs.getPackagePriority();
+        if (leftPackagePriority != rightPackagePriority) {
+            // by priority, high to low
+            return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
+        }
         final int leftScore = lhs.sbn.getScore();
         final int rightScore = rhs.sbn.getScore();
         if (leftScore != rightScore) {
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index d8ab9d7..1335706 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -63,4 +63,9 @@
             }
         };
     }
-}
\ No newline at end of file
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        // ignore: config has no relevant information yet.
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 892f61f..bbe0885 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -103,11 +103,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import java.util.concurrent.TimeUnit;
 
 /** {@hide} */
 public class NotificationManagerService extends SystemService {
@@ -120,7 +118,8 @@
     static final int MESSAGE_TIMEOUT = 2;
     static final int MESSAGE_SAVE_POLICY_FILE = 3;
     static final int MESSAGE_RECONSIDER_RANKING = 4;
-    static final int MESSAGE_SEND_RANKING_UPDATE = 5;
+    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
+    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
 
     static final int LONG_DELAY = 3500; // 3.5 seconds
     static final int SHORT_DELAY = 2000; // 2 seconds
@@ -152,7 +151,6 @@
     private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
-    private Handler mRankingHandler = null;
 
     private Light mNotificationLight;
     Light mAttentionLight;
@@ -178,7 +176,6 @@
     // used as a mutex for access to all active notifications & listeners
     final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
-    final NotificationComparator mRankingComparator = new NotificationComparator();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
@@ -203,7 +200,7 @@
     private static final String TAG_PACKAGE = "package";
     private static final String ATTR_NAME = "name";
 
-    final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
+    private RankingHelper mRankingHelper;
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
@@ -360,6 +357,7 @@
                         }
                     }
                     mZenModeHelper.readXml(parser);
+                    mRankingHelper.readXml(parser);
                 }
             } catch (FileNotFoundException e) {
                 // No data yet
@@ -398,6 +396,7 @@
                 out.startTag(null, TAG_BODY);
                 out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
                 mZenModeHelper.writeXml(out);
+                mRankingHelper.writeXml(out);
                 out.endTag(null, TAG_BODY);
                 out.endDocument();
                 mPolicyFile.finishWrite(stream);
@@ -752,13 +751,23 @@
 
     @Override
     public void onStart() {
+        Resources resources = getContext().getResources();
+
         mAm = ActivityManagerNative.getDefault();
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
 
         mHandler = new WorkerHandler();
         mRankingThread.start();
-        mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
+        String[] extractorNames;
+        try {
+            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
+        } catch (Resources.NotFoundException e) {
+            extractorNames = new String[0];
+        }
+        mRankingHelper = new RankingHelper(getContext(),
+                new RankingWorkerHandler(mRankingThread.getLooper()),
+                extractorNames);
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
@@ -782,7 +791,6 @@
         mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
         mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION);
 
-        Resources resources = getContext().getResources();
         mDefaultNotificationColor = resources.getColor(
                 R.color.config_defaultNotificationColor);
         mDefaultNotificationLedOn = resources.getInteger(
@@ -837,25 +845,6 @@
 
         mSettingsObserver = new SettingsObserver(mHandler);
 
-        // spin up NotificationSignalExtractors
-        String[] extractorNames = resources.getStringArray(
-                R.array.config_notificationSignalExtractors);
-        for (String extractorName : extractorNames) {
-            try {
-                Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
-                NotificationSignalExtractor extractor =
-                        (NotificationSignalExtractor) extractorClass.newInstance();
-                extractor.initialize(getContext());
-                mSignalExtractors.add(extractor);
-            } catch (ClassNotFoundException e) {
-                Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
-            } catch (InstantiationException e) {
-                Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
-            } catch (IllegalAccessException e) {
-                Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
-            }
-        }
-
         mArchive = new Archive(resources.getInteger(
                 R.integer.config_notificationServiceArchiveSize));
 
@@ -1062,6 +1051,19 @@
                     == AppOpsManager.MODE_ALLOWED);
         }
 
+        @Override
+        public void setPackagePriority(String pkg, int uid, int priority) {
+            checkCallerIsSystem();
+            mRankingHelper.setPackagePriority(pkg, uid, priority);
+            savePolicyFile();
+        }
+
+        @Override
+        public int getPackagePriority(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mRankingHelper.getPackagePriority(pkg, uid);
+        }
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
@@ -1402,6 +1404,9 @@
                 mZenModeHelper.dump(pw, "    ");
             }
 
+            pw.println("\n  Ranking Config:");
+            mRankingHelper.dump(pw, "    ", filter);
+
             pw.println("\n  Notification listeners:");
             mListeners.dump(pw, filter);
 
@@ -1509,16 +1514,7 @@
                     // Retain ranking information from previous record
                     r.copyRankingInformation(old);
                 }
-                if (!mSignalExtractors.isEmpty()) {
-                    for (NotificationSignalExtractor extractor : mSignalExtractors) {
-                        try {
-                            RankingReconsideration recon = extractor.process(r);
-                            scheduleRankingReconsideration(recon);
-                        } catch (Throwable t) {
-                            Slog.w(TAG, "NotificationSignalExtractor failed.", t);
-                        }
-                    }
-                }
+                mRankingHelper.extractSignals(r);
 
                 // 3. Apply local rules
 
@@ -1563,7 +1559,7 @@
 
                     applyZenModeLocked(r);
 
-                    Collections.sort(mNotificationList, mRankingComparator);
+                    mRankingHelper.sort(mNotificationList);
 
                     if (notification.icon != 0) {
                         mListeners.notifyPostedLocked(n);
@@ -1838,14 +1834,6 @@
         }
     }
 
-    private void scheduleRankingReconsideration(RankingReconsideration recon) {
-        if (recon != null) {
-            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, recon);
-            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
-            mRankingHandler.sendMessageDelayed(m, delay);
-        }
-    }
-
     private void handleRankingReconsideration(Message message) {
         if (!(message.obj instanceof RankingReconsideration)) return;
         RankingReconsideration recon = (RankingReconsideration) message.obj;
@@ -1860,7 +1848,7 @@
             boolean interceptBefore = record.isIntercepted();
             recon.applyChangesLocked(record);
             applyZenModeLocked(record);
-            Collections.sort(mNotificationList, mRankingComparator);
+            mRankingHelper.sort(mNotificationList);
             int indexAfter = findNotificationRecordIndexLocked(record);
             boolean interceptAfter = record.isIntercepted();
             changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
@@ -1873,6 +1861,25 @@
         }
     }
 
+    private void handleRankingConfigChange() {
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            ArrayList<String> orderBefore = new ArrayList<String>(N);
+            for (int i = 0; i < N; i++) {
+                final NotificationRecord r = mNotificationList.get(i);
+                orderBefore.add(r.getKey());
+                mRankingHelper.extractSignals(r);
+            }
+            mRankingHelper.sort(mNotificationList);
+            for (int i = 0; i < N; i++) {
+                if (!orderBefore.get(i).equals(mNotificationList.get(i).getKey())) {
+                    scheduleSendRankingUpdate();
+                    return;
+                }
+            }
+        }
+    }
+
     // let zen mode evaluate this record
     private void applyZenModeLocked(NotificationRecord record) {
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
@@ -1880,7 +1887,7 @@
 
     // lock on mNotificationList
     private int findNotificationRecordIndexLocked(NotificationRecord target) {
-        return Collections.binarySearch(mNotificationList, target, mRankingComparator);
+        return mRankingHelper.indexOf(mNotificationList, target);
     }
 
     private void scheduleSendRankingUpdate() {
@@ -1928,6 +1935,9 @@
                 case MESSAGE_RECONSIDER_RANKING:
                     handleRankingReconsideration(msg);
                     break;
+                case MESSAGE_RANKING_CONFIG_CHANGE:
+                    handleRankingConfigChange();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0e6265c..088b813 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -58,6 +58,7 @@
 
     // Is this record an update of an old record?
     public boolean isUpdate;
+    private int mPackagePriority;
 
     NotificationRecord(StatusBarNotification sbn, int score)
     {
@@ -70,6 +71,7 @@
     public void copyRankingInformation(NotificationRecord previous) {
         mContactAffinity = previous.mContactAffinity;
         mRecentlyIntrusive = previous.mRecentlyIntrusive;
+        mPackagePriority = previous.mPackagePriority;
         mIntercept = previous.mIntercept;
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
     }
@@ -141,6 +143,7 @@
         pw.println(prefix + "  stats=" + stats.toString());
         pw.println(prefix + "  mContactAffinity=" + mContactAffinity);
         pw.println(prefix + "  mRecentlyIntrusive=" + mRecentlyIntrusive);
+        pw.println(prefix + "  mPackagePriority=" + mPackagePriority);
         pw.println(prefix + "  mIntercept=" + mIntercept);
         pw.println(prefix + "  mRankingTimeMs=" + mRankingTimeMs);
     }
@@ -193,6 +196,14 @@
         return mRecentlyIntrusive;
     }
 
+    public void setPackagePriority(int packagePriority) {
+      mPackagePriority = packagePriority;
+    }
+
+    public int getPackagePriority() {
+        return mPackagePriority;
+    }
+
     public boolean setIntercepted(boolean intercept) {
         mIntercept = intercept;
         return mIntercept;
@@ -230,5 +241,4 @@
         }
         return sbn.getPostTime();
     }
-
 }
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 1537ea9..43d05d0 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -38,4 +38,10 @@
      */
     public RankingReconsideration process(NotificationRecord notification);
 
+    /**
+     * Called whenever the {@link RankingConfig} changes.
+     *
+     * @param config information about which signals are important.
+     */
+    void setConfig(RankingConfig config);
 }
diff --git a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
new file mode 100644
index 0000000..9cdb3e1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.Slog;
+
+public class PackagePriorityExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "ImportantPackageExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+
+        final int packagePriority = mConfig.getPackagePriority(
+                record.sbn.getPackageName(), record.sbn.getUid());
+        record.setPackagePriority(packagePriority);
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
new file mode 100644
index 0000000..7d0bd59
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.server.notification;
+
+public interface RankingConfig {
+    int getPackagePriority(String packageName, int uid);
+
+    void setPackagePriority(String packageName, int uid, int priority);
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
new file mode 100644
index 0000000..fc03c17
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -0,0 +1,250 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+public class RankingHelper implements RankingConfig {
+    private static final String TAG = "RankingHelper";
+    private static final boolean DEBUG = false;
+
+    private static final int XML_VERSION = 1;
+
+    private static final String TAG_RANKING = "ranking";
+    private static final String TAG_PACKAGE = "package";
+    private static final String ATT_VERSION = "version";
+
+    private static final String ATT_NAME = "name";
+    private static final String ATT_UID = "uid";
+    private static final String ATT_PRIORITY = "priority";
+
+    private static final String VALUE_HIGH = "high";
+
+    private final NotificationSignalExtractor[] mSignalExtractors;
+    private final NotificationComparator mRankingComparator = new NotificationComparator();
+
+    // Package name to uid, to priority. Would be better as Table<String, Int, Int>
+    private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+
+    private final Context mContext;
+    private final Handler mRankingHandler;
+
+    public RankingHelper(Context context, Handler rankingHandler, String[] extractorNames) {
+        mContext = context;
+        mRankingHandler = rankingHandler;
+        mPackagePriorities = new ArrayMap<String, SparseIntArray>();
+
+        final int N = extractorNames.length;
+        mSignalExtractors = new NotificationSignalExtractor[N];
+        for (int i = 0; i < N; i++) {
+            try {
+                Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
+                NotificationSignalExtractor extractor =
+                        (NotificationSignalExtractor) extractorClass.newInstance();
+                extractor.initialize(mContext);
+                extractor.setConfig(this);
+                mSignalExtractors[i] = extractor;
+            } catch (ClassNotFoundException e) {
+                Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
+            } catch (InstantiationException e) {
+                Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
+            } catch (IllegalAccessException e) {
+                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
+            }
+        }
+    }
+
+    public void extractSignals(NotificationRecord r) {
+        final int N = mSignalExtractors.length;
+        for (int i = 0; i < N; i++) {
+            NotificationSignalExtractor extractor = mSignalExtractors[i];
+            try {
+                RankingReconsideration recon = extractor.process(r);
+                if (recon != null) {
+                    Message m = Message.obtain(mRankingHandler,
+                            NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
+                    long delay = recon.getDelay(TimeUnit.MILLISECONDS);
+                    mRankingHandler.sendMessageDelayed(m, delay);
+                }
+            } catch (Throwable t) {
+                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
+            }
+        }
+    }
+
+    public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int type = parser.getEventType();
+        if (type != XmlPullParser.START_TAG) return;
+        String tag = parser.getName();
+        if (!TAG_RANKING.equals(tag)) return;
+        mPackagePriorities.clear();
+        final int version = safeInt(parser, ATT_VERSION, XML_VERSION);
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            tag = parser.getName();
+            if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
+                return;
+            }
+            if (type == XmlPullParser.START_TAG) {
+                if (TAG_PACKAGE.equals(tag)) {
+                    int uid = safeInt(parser, ATT_UID, UserHandle.USER_ALL);
+                    int priority = safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT);
+                    String name = parser.getAttributeValue(null, ATT_NAME);
+
+                    if (!TextUtils.isEmpty(name) && priority != Notification.PRIORITY_DEFAULT) {
+                        SparseIntArray priorityByUid = mPackagePriorities.get(name);
+                        if (priorityByUid == null) {
+                            priorityByUid = new SparseIntArray();
+                            mPackagePriorities.put(name, priorityByUid);
+                        }
+                        priorityByUid.put(uid, priority);
+                    }
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to reach END_DOCUMENT");
+    }
+
+    public void writeXml(XmlSerializer out) throws IOException {
+        out.startTag(null, TAG_RANKING);
+        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+
+        final int N = mPackagePriorities.size();
+        for (int i = 0; i < N; i ++) {
+            String name = mPackagePriorities.keyAt(i);
+            SparseIntArray priorityByUid = mPackagePriorities.get(name);
+            final int M = priorityByUid.size();
+            for (int j = 0; j < M; j++) {
+                int uid = priorityByUid.keyAt(j);
+                int priority = priorityByUid.get(uid);
+                out.startTag(null, TAG_PACKAGE);
+                out.attribute(null, ATT_NAME, name);
+                out.attribute(null, ATT_UID, Integer.toString(uid));
+                out.attribute(null, ATT_PRIORITY, Integer.toString(priority));
+                out.endTag(null, TAG_PACKAGE);
+            }
+        }
+        out.endTag(null, TAG_RANKING);
+    }
+
+    private void updateConfig() {
+        final int N = mSignalExtractors.length;
+        for (int i = 0; i < N; i++) {
+            mSignalExtractors[i].setConfig(this);
+        }
+        mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
+    }
+
+    public void sort(ArrayList<NotificationRecord> notificationList) {
+        Collections.sort(notificationList, mRankingComparator);
+    }
+
+    public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
+        return Collections.binarySearch(notificationList, target, mRankingComparator);
+    }
+
+    private static int safeInt(XmlPullParser parser, String att, int defValue) {
+        final String val = parser.getAttributeValue(null, att);
+        return tryParseInt(val, defValue);
+    }
+
+    private static int tryParseInt(String value, int defValue) {
+        if (TextUtils.isEmpty(value)) return defValue;
+        try {
+            return Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+    @Override
+    public int getPackagePriority(String packageName, int uid) {
+        int priority = Notification.PRIORITY_DEFAULT;
+        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
+        if (priorityByUid != null) {
+            priority = priorityByUid.get(uid, Notification.PRIORITY_DEFAULT);
+        }
+        return priority;
+    }
+
+    @Override
+    public void setPackagePriority(String packageName, int uid, int priority) {
+        if (priority == getPackagePriority(packageName, uid)) {
+            return;
+        }
+        SparseIntArray priorityByUid = mPackagePriorities.get(packageName);
+        if (priorityByUid == null) {
+            priorityByUid = new SparseIntArray();
+            mPackagePriorities.put(packageName, priorityByUid);
+        }
+        priorityByUid.put(uid, priority);
+        updateConfig();
+    }
+
+    public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
+        if (filter == null) {
+            final int N = mSignalExtractors.length;
+            pw.print(prefix);
+            pw.print("mSignalExtractors.length = ");
+            pw.println(N);
+            for (int i = 0; i < N; i++) {
+                pw.print(prefix);
+                pw.print("  ");
+                pw.println(mSignalExtractors[i]);
+            }
+        }
+        final int N = mPackagePriorities.size();
+        if (filter == null) {
+            pw.print(prefix);
+            pw.println("package priorities:");
+        }
+        for (int i = 0; i < N; i++) {
+            String name = mPackagePriorities.keyAt(i);
+            if (filter == null || filter.matches(name)) {
+                SparseIntArray priorityByUid = mPackagePriorities.get(name);
+                final int M = priorityByUid.size();
+                for (int j = 0; j < M; j++) {
+                    int uid = priorityByUid.keyAt(j);
+                    int priority = priorityByUid.get(uid);
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print(name);
+                    pw.print(" (");
+                    pw.print(uid);
+                    pw.print(") has priority: ");
+                    pw.println(priority);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 4ac2dcc..bdc364c 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -264,6 +264,11 @@
         return validatePeople(record);
     }
 
+    @Override
+    public void setConfig(RankingConfig config) {
+        // ignore: config has no relevant information yet.
+    }
+
     private static class LookupResult {
         private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
         public static final int INVALID_ID = -1;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e007600..0e1340c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -46,6 +46,8 @@
 import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindBackgroundAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
 import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
@@ -72,44 +74,42 @@
             WindowManagerService.DEBUG_APP_TRANSITIONS;
     private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
 
-    /** Bit mask that is set for all enter transition. */
-    public static final int TRANSIT_ENTER_MASK = 0x1000;
-
-    /** Bit mask that is set for all exit transitions. */
-    public static final int TRANSIT_EXIT_MASK = 0x2000;
 
     /** Not set up for a transition. */
     public static final int TRANSIT_UNSET = -1;
     /** No animation for transition. */
     public static final int TRANSIT_NONE = 0;
     /** A window in a new activity is being opened on top of an existing one in the same task. */
-    public static final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_ACTIVITY_OPEN = 6;
     /** The window in the top-most activity is being closed to reveal the
      * previous activity in the same task. */
-    public static final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
     /** A window in a new task is being opened on top of an existing one
      * in another activity's task. */
-    public static final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_TASK_OPEN = 8;
     /** A window in the top-most activity is being closed to reveal the
      * previous activity in a different task. */
-    public static final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_TASK_CLOSE = 9;
     /** A window in an existing task is being displayed on top of an existing one
      * in another activity's task. */
-    public static final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_TASK_TO_FRONT = 10;
     /** A window in an existing task is being put below all other tasks. */
-    public static final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_TASK_TO_BACK = 11;
     /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
      * does, effectively closing the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
     /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
      * effectively opening the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_WALLPAPER_OPEN = 13;
     /** A window in a new activity is being opened on top of an existing one, and both are on top
      * of the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK;
+    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
     /** The window in the top-most activity is being closed to reveal the previous activity, and
      * both are on top of the wallpaper. */
-    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK;
+    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+    /** A window in a new task is being opened behind an existing one in another activity's task.
+     * The new window will show briefly and then be gone. */
+    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
 
     /** Fraction of animation at which the recents thumbnail becomes completely transparent */
     private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
@@ -811,6 +811,10 @@
                             ? WindowAnimation_wallpaperIntraCloseEnterAnimation
                             : WindowAnimation_wallpaperIntraCloseExitAnimation;
                     break;
+                case TRANSIT_TASK_OPEN_BEHIND:
+                    animAttr = enter
+                            ? WindowAnimation_launchTaskBehindSourceAnimation
+                            : WindowAnimation_launchTaskBehindBackgroundAnimation;
             }
             a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
@@ -896,9 +900,6 @@
             case TRANSIT_NONE: {
                 return "TRANSIT_NONE";
             }
-            case TRANSIT_EXIT_MASK: {
-                return "TRANSIT_EXIT_MASK";
-            }
             case TRANSIT_ACTIVITY_OPEN: {
                 return "TRANSIT_ACTIVITY_OPEN";
             }
@@ -929,6 +930,9 @@
             case TRANSIT_WALLPAPER_INTRA_CLOSE: {
                 return "TRANSIT_WALLPAPER_INTRA_CLOSE";
             }
+            case TRANSIT_TASK_OPEN_BEHIND: {
+                return "TRANSIT_TASK_OPEN_BEHIND";
+            }
             default: {
                 return "<UNKNOWN>";
             }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 63ae98e..874e105 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,7 +16,9 @@
 
 package com.android.server.wm;
 
+import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.view.Display;
@@ -281,9 +283,21 @@
 
         final int N = mAllAppWinAnimators.size();
         for (int i=0; i<N; i++) {
-            mAllAppWinAnimators.get(i).finishExit();
+            final WindowStateAnimator winAnim = mAllAppWinAnimators.get(i);
+            if (mAppToken.mLaunchTaskBehind) {
+                winAnim.mWin.mExiting = true;
+            }
+            winAnim.finishExit();
         }
-        mAppToken.updateReportedVisibilityLocked();
+        if (mAppToken.mLaunchTaskBehind) {
+            try {
+                mService.mActivityManager.notifyLaunchTaskBehindComplete(mAppToken.token);
+            } catch (RemoteException e) {
+            }
+            mAppToken.mLaunchTaskBehind = false;
+        } else {
+            mAppToken.updateReportedVisibilityLocked();
+        }
 
         return false;
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 12c15e2..312689b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -109,6 +109,8 @@
 
     boolean mDeferRemoval;
 
+    boolean mLaunchTaskBehind;
+
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 396ec8f..a5959d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -497,8 +497,8 @@
     boolean mStartingIconInTransition = false;
     boolean mSkipAppTransitionAnimation = false;
 
-    final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
-    final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<AppWindowToken>();
+    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<AppWindowToken>();
 
     boolean mIsTouchDevice;
 
@@ -3234,6 +3234,12 @@
                                 SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN);
             }
 
+            if (atoken.mLaunchTaskBehind) {
+                // Differentiate the two animations. This one which is briefly on the screen
+                // gets the !enter animation, and the other activity which remains on the
+                // screen gets the enter animation. Both appear in the mOpeningApps set.
+                enter = false;
+            }
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
                     mCurConfiguration.orientation, containingFrame, contentInsets, isFullScreen,
                     isVoiceInteraction);
@@ -3449,14 +3455,14 @@
         EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
         Task task = new Task(atoken, stack, userId);
         mTaskIdToTask.put(taskId, task);
-        stack.addTask(task, true);
+        stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
         return task;
     }
 
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
-            int configChanges, boolean voiceInteraction) {
+            int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3490,6 +3496,7 @@
             atoken.requestedOrientation = requestedOrientation;
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
+            atoken.mLaunchTaskBehind = launchTaskBehind;
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                     + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
@@ -3954,16 +3961,16 @@
         }
 
         synchronized(mWindowMap) {
-            if (DEBUG_APP_TRANSITIONS) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Slog.w(TAG, "Execute app transition: " + mAppTransition, e);
-            }
+            if (DEBUG_APP_TRANSITIONS) Slog.w(TAG, "Execute app transition: " + mAppTransition,
+                    new RuntimeException("here").fillInStackTrace());
             if (mAppTransition.isTransitionSet()) {
                 mAppTransition.setReady();
                 final long origId = Binder.clearCallingIdentity();
-                performLayoutAndPlaceSurfacesLocked();
-                Binder.restoreCallingIdentity(origId);
+                try {
+                    performLayoutAndPlaceSurfacesLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
             }
         }
     }
@@ -4370,17 +4377,11 @@
                 return;
             }
 
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
-                RuntimeException e = null;
-                if (!HIDE_STACK_CRAWLS) {
-                    e = new RuntimeException();
-                    e.fillInStackTrace();
-                }
-                Slog.v(TAG, "setAppVisibility(" + token + ", visible=" + visible
-                        + "): " + mAppTransition
-                        + " hidden=" + wtoken.hidden
-                        + " hiddenRequested=" + wtoken.hiddenRequested, e);
-            }
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG, "setAppVisibility(" +
+                    token + ", visible=" + visible + "): " + mAppTransition +
+                    " hidden=" + wtoken.hidden + " hiddenRequested=" +
+                    wtoken.hiddenRequested, HIDE_STACK_CRAWLS ?
+                            null : new RuntimeException("here").fillInStackTrace());
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
@@ -4428,6 +4429,21 @@
                         wtoken.waitingToHide = true;
                     }
                 }
+                if (mAppTransition.getAppTransition() == AppTransition.TRANSIT_TASK_OPEN_BEHIND) {
+                    // We're launchingBehind, add the launching activity to mOpeningApps.
+                    final WindowState win =
+                            findFocusedWindowLocked(getDefaultDisplayContentLocked());
+                    if (win != null) {
+                        final AppWindowToken focusedToken = win.mAppToken;
+                        if (focusedToken != null) {
+                            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "TRANSIT_TASK_OPEN_BEHIND, " +
+                                    " adding " + focusedToken + " to mOpeningApps");
+                            // Force animation to be loaded.
+                            focusedToken.hidden = true;
+                            mOpeningApps.add(focusedToken);
+                        }
+                    }
+                }
                 return;
             }
 
@@ -8558,7 +8574,7 @@
             // all of the apps are ready.  Otherwise just go because
             // we'll unfreeze the display when everyone is ready.
             for (i=0; i<NN && goodToGo; i++) {
-                AppWindowToken wtoken = mOpeningApps.get(i);
+                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                         "Check opening app=" + wtoken + ": allDrawn="
                         + wtoken.allDrawn + " startingDisplayed="
@@ -8631,12 +8647,12 @@
             for (i=0; i<NN; i++) {
                 final AppWindowToken wtoken;
                 if (i < NC) {
-                    wtoken = mClosingApps.get(i);
+                    wtoken = mClosingApps.valueAt(i);
                     if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
                         closingAppHasWallpaper = true;
                     }
                 } else {
-                    wtoken = mOpeningApps.get(i - NC);
+                    wtoken = mOpeningApps.valueAt(i - NC);
                     if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
                         openingAppHasWallpaper = true;
                     }
@@ -8710,7 +8726,7 @@
 
             NN = mOpeningApps.size();
             for (i=0; i<NN; i++) {
-                AppWindowToken wtoken = mOpeningApps.get(i);
+                AppWindowToken wtoken = mOpeningApps.valueAt(i);
                 final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
                 appAnimator.clearThumbnail();
@@ -8743,7 +8759,7 @@
             }
             NN = mClosingApps.size();
             for (i=0; i<NN; i++) {
-                AppWindowToken wtoken = mClosingApps.get(i);
+                AppWindowToken wtoken = mClosingApps.valueAt(i);
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
                 wtoken.mAppAnimator.clearThumbnail();
                 wtoken.inPendingTransaction = false;
diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
index 1db3c5e..4fe30e6 100644
--- a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
+++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java
@@ -23,17 +23,13 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.admin.IDevicePolicyManager;
-import android.content.AbstractRestrictionsProvider;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.IPermissionResponseCallback;
-import android.content.IRestrictionsProvider;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
-import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -127,58 +123,12 @@
                     enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
                             " match caller ");
                     // Prepare and broadcast the intent to the provider
-                    Intent intent = new Intent();
+                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
                     intent.setComponent(restrictionsProvider);
-                    new ProviderServiceConnection(intent, null, userHandle) {
-                        @Override
-                        public void run() throws RemoteException {
-                            if (DEBUG) {
-                                Log.i(LOG_TAG, "calling requestPermission for " + packageName
-                                        + ", type=" + requestType + ", data=" + requestData);
-                            }
-                            mRestrictionsProvider.requestPermission(packageName,
-                                    requestType, requestData);
-                        }
-                    }.bind();
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        @Override
-        public void getPermissionResponse(final String packageName, final String requestId,
-                final IPermissionResponseCallback callback) throws RemoteException {
-            int callingUid = Binder.getCallingUid();
-            int userHandle = UserHandle.getUserId(callingUid);
-            if (mDpm != null) {
-                long ident = Binder.clearCallingIdentity();
-                try {
-                    ComponentName restrictionsProvider =
-                            mDpm.getRestrictionsProvider(userHandle);
-                    // Check if there is a restrictions provider
-                    if (restrictionsProvider == null) {
-                        throw new IllegalStateException(
-                            "Cannot fetch permission without a restrictions provider registered");
-                    }
-                    // Check that the packageName matches the caller.
-                    enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
-                            " match caller ");
-                    // Prepare and broadcast the intent to the provider
-                    Intent intent = new Intent();
-                    intent.setComponent(restrictionsProvider);
-                    new ProviderServiceConnection(intent, callback.asBinder(), userHandle) {
-                        @Override
-                        public void run() throws RemoteException {
-                            if (DEBUG) {
-                                Log.i(LOG_TAG, "calling getPermissionResponse for " + packageName
-                                        + ", id=" + requestId);
-                            }
-                            Bundle response = mRestrictionsProvider.getPermissionResponse(
-                                    packageName, requestId);
-                            callback.onResponse(response);
-                        }
-                    }.bind();
+                    intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
+                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
+                    mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -226,81 +176,5 @@
                 // Shouldn't happen
             }
         }
-
-        abstract class ProviderServiceConnection
-                implements IBinder.DeathRecipient, ServiceConnection {
-
-            protected IRestrictionsProvider mRestrictionsProvider;
-            private Intent mIntent;
-            protected int mUserHandle;
-            protected IBinder mResponse;
-            private boolean mAbort;
-
-            public ProviderServiceConnection(Intent intent, IBinder response, int userHandle) {
-                mIntent = intent;
-                mResponse = response;
-                mUserHandle = userHandle;
-                if (mResponse != null) {
-                    try {
-                        mResponse.linkToDeath(this, 0 /* flags */);
-                    } catch (RemoteException re) {
-                        close();
-                    }
-                }
-            }
-
-            /** Bind to the RestrictionsProvider process */
-            public void bind() {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "binding to service: " + mIntent);
-                }
-                mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
-                        new UserHandle(mUserHandle));
-            }
-
-            private void close() {
-                mAbort = true;
-                unbind();
-            }
-
-            private void unbind() {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "unbinding from service");
-                }
-                mContext.unbindService(this);
-            }
-
-            /** Implement this to call the appropriate method on the service */
-            public abstract void run() throws RemoteException;
-
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "connected to " + name);
-                }
-                mRestrictionsProvider = IRestrictionsProvider.Stub.asInterface(service);
-                if (!mAbort) {
-                    try {
-                        run();
-                    } catch (RemoteException re) {
-                        Log.w("RestrictionsProvider", "Remote exception: " + re);
-                    }
-                }
-                close();
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "disconnected from " + name);
-                }
-                mRestrictionsProvider = null;
-            }
-
-            @Override
-            public void binderDied() {
-                mAbort = true;
-            }
-        }
     }
 }
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index ba7e253..b17f929 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -73,6 +73,7 @@
         private final String mDisconnectCauseMsg;
         private final long mConnectTimeMillis;
         private final GatewayInfo mGatewayInfo;
+        private final int mVideoState;
 
         /**
          * @return The handle (e.g., phone number) to which the {@code Call} is currently
@@ -167,7 +168,8 @@
                         Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
                         Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
                         Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
-                        Objects.equals(mGatewayInfo, d.mGatewayInfo);
+                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
+                        Objects.equals(mVideoState, d.mVideoState);
             }
             return false;
         }
@@ -184,7 +186,8 @@
                     Objects.hashCode(mDisconnectCauseCode) +
                     Objects.hashCode(mDisconnectCauseMsg) +
                     Objects.hashCode(mConnectTimeMillis) +
-                    Objects.hashCode(mGatewayInfo);
+                    Objects.hashCode(mGatewayInfo) +
+                    Objects.hashCode(mVideoState);
         }
 
         /** {@hide} */
@@ -198,7 +201,8 @@
                 int disconnectCauseCode,
                 String disconnectCauseMsg,
                 long connectTimeMillis,
-                GatewayInfo gatewayInfo) {
+                GatewayInfo gatewayInfo,
+                int videoState) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -209,6 +213,7 @@
             mDisconnectCauseMsg = disconnectCauseMsg;
             mConnectTimeMillis = connectTimeMillis;
             mGatewayInfo = gatewayInfo;
+            mVideoState = videoState;
         }
     }
 
@@ -545,7 +550,8 @@
                 inCallCall.getDisconnectCauseCode(),
                 inCallCall.getDisconnectCauseMsg(),
                 inCallCall.getConnectTimeMillis(),
-                inCallCall.getGatewayInfo());
+                inCallCall.getGatewayInfo(),
+                inCallCall.getVideoState());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index 355c260..db8395c 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -50,6 +50,7 @@
     private final String mParentCallId;
     private final List<String> mChildCallIds;
     private final StatusHints mStatusHints;
+    private final int mVideoState;
 
     /** @hide */
     public InCallCall(
@@ -69,7 +70,8 @@
             ICallVideoProvider callVideoProvider,
             String parentCallId,
             List<String> childCallIds,
-            StatusHints statusHints) {
+            StatusHints statusHints,
+            int videoState) {
         mId = id;
         mState = state;
         mDisconnectCauseCode = disconnectCauseCode;
@@ -87,6 +89,7 @@
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
         mStatusHints = statusHints;
+        mVideoState = videoState;
     }
 
     /** The unique ID of the call. */
@@ -204,6 +207,14 @@
         return mStatusHints;
     }
 
+    /**
+     * The video state.
+     * @return The video state of the call.
+     */
+    public int getVideoState() {
+        return mVideoState;
+    }
+
     /** Responsible for creating InCallCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<InCallCall> CREATOR =
             new Parcelable.Creator<InCallCall> () {
@@ -230,10 +241,12 @@
             List<String> childCallIds = new ArrayList<>();
             source.readList(childCallIds, classLoader);
             StatusHints statusHints = source.readParcelable(classLoader);
+            int videoState = source.readInt();
             return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
                     cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
                     callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
-                    account, callVideoProvider, parentCallId, childCallIds, statusHints);
+                    account, callVideoProvider, parentCallId, childCallIds, statusHints,
+                    videoState);
         }
 
         @Override
@@ -269,6 +282,7 @@
         destination.writeString(mParentCallId);
         destination.writeList(mChildCallIds);
         destination.writeParcelable(mStatusHints, 0);
+        destination.writeInt(mVideoState);
     }
 
     @Override
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index a6c09f3..62c92a1 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
         }
         
         try {
-            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false);
+            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2f40003..6927b26 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -72,7 +72,7 @@
 
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
-            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9)
+            boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10)
             throws RemoteException {
         // TODO Auto-generated method stub