Merge "Support cluster-style installs for bundled apps." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index d35610b..f799671 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5327,6 +5327,7 @@
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getBlockUninstall(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5366,6 +5367,7 @@
     method public int setApplicationsBlocked(android.content.ComponentName, android.content.Intent, boolean);
     method public void setBlockUninstall(android.content.ComponentName, java.lang.String, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
@@ -5452,6 +5454,7 @@
     method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
     method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
+    method public void onRestoreFinished();
     field public static final int TYPE_DIRECTORY = 2; // 0x2
     field public static final int TYPE_FILE = 1; // 0x1
   }
@@ -16550,6 +16553,7 @@
     method public java.lang.String getId();
     method public android.content.Intent getIntentForSettingsActivity();
     method public android.content.Intent getIntentForSetupActivity();
+    method public java.lang.String getParentId();
     method public android.content.pm.ServiceInfo getServiceInfo();
     method public int getType();
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
@@ -28030,6 +28034,7 @@
     method public int getState();
     method public void hold();
     method public void phoneAccountClicked();
+    method public void phoneAccountSelected(android.telecomm.PhoneAccount);
     method public void playDtmfTone(char);
     method public void postDialContinue(boolean);
     method public void reject(boolean, java.lang.String);
@@ -28043,6 +28048,7 @@
     field public static final int STATE_DISCONNECTED = 7; // 0x7
     field public static final int STATE_HOLDING = 3; // 0x3
     field public static final int STATE_NEW = 0; // 0x0
+    field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
     field public static final int STATE_RINGING = 2; // 0x2
   }
 
@@ -28130,6 +28136,7 @@
     enum_constant public static final android.telecomm.CallState DISCONNECTED;
     enum_constant public static final android.telecomm.CallState NEW;
     enum_constant public static final android.telecomm.CallState ON_HOLD;
+    enum_constant public static final android.telecomm.CallState PRE_DIAL_WAIT;
     enum_constant public static final android.telecomm.CallState RINGING;
   }
 
@@ -28274,6 +28281,7 @@
     method public void holdCall(java.lang.String);
     method public void mute(boolean);
     method public void phoneAccountClicked(java.lang.String);
+    method public void phoneAccountSelected(java.lang.String, android.telecomm.PhoneAccount);
     method public void playDtmfTone(java.lang.String, char);
     method public void postDialContinue(java.lang.String, boolean);
     method public void rejectCall(java.lang.String, boolean, java.lang.String);
@@ -31163,6 +31171,36 @@
     field public static final int WEEKDAY_WEDNESDAY = 4; // 0x4
   }
 
+  public static abstract class TtsSpan.Builder {
+    ctor public TtsSpan.Builder(java.lang.String);
+    method public android.text.style.TtsSpan build();
+    method public C setIntArgument(java.lang.String, int);
+    method public C setLongArgument(java.lang.String, long);
+    method public C setStringArgument(java.lang.String, java.lang.String);
+  }
+
+  public static class TtsSpan.CardinalBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.CardinalBuilder();
+    ctor public TtsSpan.CardinalBuilder(long);
+    ctor public TtsSpan.CardinalBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.CardinalBuilder setNumber(long);
+    method public android.text.style.TtsSpan.CardinalBuilder setNumber(java.lang.String);
+  }
+
+  public static class TtsSpan.SemioticClassBuilder extends android.text.style.TtsSpan.Builder {
+    ctor public TtsSpan.SemioticClassBuilder(java.lang.String);
+    method public C setAnimacy(java.lang.String);
+    method public C setCase(java.lang.String);
+    method public C setGender(java.lang.String);
+    method public C setMultiplicity(java.lang.String);
+  }
+
+  public static class TtsSpan.TextBuilder extends android.text.style.TtsSpan.SemioticClassBuilder {
+    ctor public TtsSpan.TextBuilder();
+    ctor public TtsSpan.TextBuilder(java.lang.String);
+    method public android.text.style.TtsSpan.TextBuilder setText(java.lang.String);
+  }
+
   public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public TypefaceSpan(java.lang.String);
     ctor public TypefaceSpan(android.os.Parcel);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 806a55b..aab6e80 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -155,6 +155,8 @@
 
     /** @hide */
     public static final int ERROR_CODE_USER_RESTRICTED = 100;
+    /** @hide */
+    public static final int ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101;
 
     /**
      * Bundle key used for the {@link String} account name in results
@@ -678,8 +680,7 @@
      * @param handler {@link Handler} identifying the callback thread,
      *     null for the main thread
      * @return An {@link AccountManagerFuture} which resolves to a Boolean,
-     *     true if the account has been successfully removed,
-     *     false if the authenticator forbids deleting this account.
+     *     true if the account has been successfully removed
      */
     public AccountManagerFuture<Boolean> removeAccount(final Account account,
             AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -698,6 +699,28 @@
     }
 
     /**
+     * @see #removeAccount(Account, AccountManagerCallback, Handler)
+     * @hide
+     */
+    public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account,
+            AccountManagerCallback<Boolean> callback, Handler handler,
+            final UserHandle userHandle) {
+        if (account == null) throw new IllegalArgumentException("account is null");
+        if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
+        return new Future2Task<Boolean>(handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.removeAccountAsUser(mResponse, account, userHandle.getIdentifier());
+            }
+            public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+                if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
+                    throw new AuthenticatorException("no result in response");
+                }
+                return bundle.getBoolean(KEY_BOOLEAN_RESULT);
+            }
+        }.start();
+    }
+
+    /**
      * Removes an auth token from the AccountManager's cache.  Does nothing if
      * the auth token is not currently in the cache.  Applications must call this
      * method when the auth token is found to have expired or otherwise become
@@ -1183,7 +1206,8 @@
      * <li> {@link AuthenticatorException} if no authenticator was registered for
      *      this account type or the authenticator failed to respond
      * <li> {@link OperationCanceledException} if the operation was canceled for
-     *      any reason, including the user canceling the creation process
+     *      any reason, including the user canceling the creation process or adding accounts
+     *      (of this type) has been disabled by policy
      * <li> {@link IOException} if the authenticator experienced an I/O problem
      *      creating a new account, usually because of network trouble
      * </ul>
@@ -1208,6 +1232,30 @@
     }
 
     /**
+     * @see #addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback, Handler)
+     * @hide
+     */
+    public AccountManagerFuture<Bundle> addAccountAsUser(final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final Bundle addAccountOptions, final Activity activity,
+            AccountManagerCallback<Bundle> callback, Handler handler, final UserHandle userHandle) {
+        if (accountType == null) throw new IllegalArgumentException("accountType is null");
+        if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
+        final Bundle optionsIn = new Bundle();
+        if (addAccountOptions != null) {
+            optionsIn.putAll(addAccountOptions);
+        }
+        optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.addAccountAsUser(mResponse, accountType, authTokenType,
+                        requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier());
+            }
+        }.start();
+    }
+
+    /**
      * Adds a shared account from the primary user to a secondary user. Adding the shared account
      * doesn't take effect immediately. When the target user starts up, any pending shared accounts
      * are attempted to be copied to the target user from the primary via calls to the
@@ -1608,8 +1656,10 @@
             }
 
             public void onError(int code, String message) {
-                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED) {
-                    // the authenticator indicated that this request was canceled, do so now
+                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
+                        || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
+                    // the authenticator indicated that this request was canceled or we were
+                    // forbidden to fulfill; cancel now
                     cancel(true /* mayInterruptIfRunning */);
                     return;
                 }
@@ -1668,7 +1718,10 @@
             }
 
             public void onError(int code, String message) {
-                if (code == ERROR_CODE_CANCELED) {
+                if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
+                        || code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
+                    // the authenticator indicated that this request was canceled or we were
+                    // forbidden to fulfill; cancel now
                     cancel(true /* mayInterruptIfRunning */);
                     return;
                 }
diff --git a/core/java/android/accounts/AccountManagerFuture.java b/core/java/android/accounts/AccountManagerFuture.java
index af00a08..77670d9 100644
--- a/core/java/android/accounts/AccountManagerFuture.java
+++ b/core/java/android/accounts/AccountManagerFuture.java
@@ -84,7 +84,8 @@
      * will be thrown rather than the call returning normally.
      * @return the actual result
      * @throws android.accounts.OperationCanceledException if the request was canceled for any
-     * reason
+     * reason (including if it is forbidden
+     * by policy to modify an account (of that type))
      * @throws android.accounts.AuthenticatorException if there was an error communicating with
      * the authenticator or if the authenticator returned an invalid response
      * @throws java.io.IOException if the authenticator returned an error response that indicates
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
index e1717a6..4ac2beb 100644
--- a/core/java/android/accounts/CantAddAccountActivity.java
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.TextView;
 
 import com.android.internal.R;
 
@@ -27,11 +28,26 @@
  * Just shows an error message about the account restrictions for the limited user.
  */
 public class CantAddAccountActivity extends Activity {
+    public static final String EXTRA_ERROR_CODE = "android.accounts.extra.ERROR_CODE";
+    public static final int MISSING = -1;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.app_not_authorized);
+
+        int errorCode = getIntent().getIntExtra(EXTRA_ERROR_CODE, MISSING);
+        if (errorCode != MISSING) {
+            TextView errorText = (TextView) findViewById(R.id.description);
+            switch (errorCode) {
+                case AccountManager.ERROR_CODE_USER_RESTRICTED:
+                    errorText.setText(R.string.app_no_restricted_accounts);
+                    break;
+                default:
+                    // TODO: Get better message. See: http://b/14642886
+                    errorText.setText(R.string.error_message_title);
+            }
+        }
     }
 
     public void onCancelButtonClicked(View view) {
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 1373dc8..a04875d 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -38,6 +38,7 @@
     void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
     boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
     void removeAccount(in IAccountManagerResponse response, in Account account);
+    void removeAccountAsUser(in IAccountManagerResponse response, in Account account, int userId);
     void invalidateAuthToken(String accountType, String authToken);
     String peekAuthToken(in Account account, String authTokenType);
     void setAuthToken(in Account account, String authTokenType, String authToken);
@@ -52,6 +53,9 @@
     void addAccount(in IAccountManagerResponse response, String accountType,
         String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
         in Bundle options);
+    void addAccountAsUser(in IAccountManagerResponse response, String accountType,
+        String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+        in Bundle options, int userId);
     void updateCredentials(in IAccountManagerResponse response, in Account account,
         String authTokenType, boolean expectActivityLaunch, in Bundle options);
     void editProperties(in IAccountManagerResponse response, String accountType,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 15f3a75..48954f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2610,15 +2610,7 @@
             return;
         }
 
-        if (mBackupAgents.get(packageName) != null) {
-            Slog.d(TAG, "BackupAgent " + "  for " + packageName
-                    + " already exists");
-            return;
-        }
-
-        BackupAgent agent = null;
         String classname = data.appInfo.backupAgentName;
-
         // full backup operation but no app-supplied agent?  use the default implementation
         if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
                 || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
@@ -2627,29 +2619,38 @@
 
         try {
             IBinder binder = null;
-            try {
-                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
-
-                java.lang.ClassLoader cl = packageInfo.getClassLoader();
-                agent = (BackupAgent) cl.loadClass(classname).newInstance();
-
-                // set up the agent's context
-                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
-                context.setOuterContext(agent);
-                agent.attach(context);
-
-                agent.onCreate();
-                binder = agent.onBind();
-                mBackupAgents.put(packageName, agent);
-            } catch (Exception e) {
-                // If this is during restore, fail silently; otherwise go
-                // ahead and let the user see the crash.
-                Slog.e(TAG, "Agent threw during creation: " + e);
-                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
-                        && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
-                    throw e;
+            BackupAgent agent = mBackupAgents.get(packageName);
+            if (agent != null) {
+                // reusing the existing instance
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "Reusing existing agent instance");
                 }
-                // falling through with 'binder' still null
+                binder = agent.onBind();
+            } else {
+                try {
+                    if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
+                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                    agent = (BackupAgent) cl.loadClass(classname).newInstance();
+
+                    // set up the agent's context
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+                    context.setOuterContext(agent);
+                    agent.attach(context);
+
+                    agent.onCreate();
+                    binder = agent.onBind();
+                    mBackupAgents.put(packageName, agent);
+                } catch (Exception e) {
+                    // If this is during restore, fail silently; otherwise go
+                    // ahead and let the user see the crash.
+                    Slog.e(TAG, "Agent threw during creation: " + e);
+                    if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
+                            && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+                        throw e;
+                    }
+                    // falling through with 'binder' still null
+                }
             }
 
             // tell the OS that we're live now
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 7036aea..451af99 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -125,6 +125,21 @@
             int token, IBackupManager callbackBinder);
 
     /**
+     * Provide the app with a canonical "all data has been delivered" end-of-restore
+     * callback so that it can do any postprocessing of the restored data that might
+     * be appropriate.  This is issued after both key/value and full data restore
+     * operations have completed.
+     *
+     * @param token Opaque token identifying this transaction.  This must
+     *        be echoed back to the backup service binder once the agent is
+     *        finished restoring the application based on the restore data
+     *        contents.
+     * @param callbackBinder Binder on which to indicate operation completion,
+     *        passed here as a convenience to the agent.
+     */
+    void doRestoreFinished(int token, IBackupManager callbackBinder);
+
+    /**
      * Out of band: instruct the agent to crash within the client process.  This is used
      * when the backup infrastructure detects a semantic error post-hoc and needs to
      * pass the problem back to the app.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4e7dac0..71ad0c9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2270,6 +2270,45 @@
     }
 
     /**
+     * Called by a profile owner to set whether caller-Id information from the managed
+     * profile will be shown for incoming calls.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param disabled If true caller-Id information in the managed profile is not displayed.
+     */
+    public void setCrossProfileCallerIdDisabled(ComponentName who, boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setCrossProfileCallerIdDisabled(who, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Determine whether or not caller-Id information has been disabled.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param who Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public boolean getCrossProfileCallerIdDisabled(ComponentName who) {
+        if (mService != null) {
+            try {
+                return mService.getCrossProfileCallerIdDisabled(who);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by the profile owner so that some intents sent in the managed profile can also be
      * resolved in the parent, or vice versa.
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -2598,9 +2637,17 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
+        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
+    }
+
+    /**
+     * @see #getAccountTypesWithManagementDisabled()
+     * @hide
+     */
+    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
         if (mService != null) {
             try {
-                return mService.getAccountTypesWithManagementDisabled();
+                return mService.getAccountTypesWithManagementDisabledAsUser(userId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d36497e..9b1979f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -146,6 +146,7 @@
 
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
+    String[] getAccountTypesWithManagementDisabledAsUser(int userId);
 
     void setLockTaskPackages(in String[] packages);
     String[] getLockTaskPackages();
@@ -161,4 +162,8 @@
 
     void setBlockUninstall(in ComponentName admin, in String packageName, boolean blockUninstall);
     boolean getBlockUninstall(in ComponentName admin, in String packageName);
+
+    void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
+    boolean getCrossProfileCallerIdDisabled(in ComponentName who);
+    boolean getCrossProfileCallerIdDisabledForUser(int userId);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index e2a86e8..87d785a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -209,7 +209,7 @@
      *            output stream.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is being restored from backup and should replace any
@@ -243,8 +243,7 @@
      *            When a full-backup dataset is being restored, this will be <code>null</code>.
      */
     public abstract void onRestore(BackupDataInput data, int appVersionCode,
-            ParcelFileDescriptor newState)
-            throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is having its entire file system contents backed up.  {@code data}
@@ -575,6 +574,20 @@
         FullBackup.restoreFile(data, size, type, mode, mtime, null);
     }
 
+    /**
+     * The application's restore operation has completed.  This method is called after
+     * all available data has been delivered to the application for restore (via either
+     * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
+     * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
+     * callbacks).  This provides the app with a stable end-of-restore opportunity to
+     * perform any appropriate post-processing on the data that was just delivered.
+     *
+     * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
+     * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
+     */
+    public void onRestoreFinished() {
+    }
+
     // ----- Core implementation -----
 
     /** @hide */
@@ -723,6 +736,24 @@
         }
 
         @Override
+        public void doRestoreFinished(int token, IBackupManager callbackBinder) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                BackupAgent.this.onRestoreFinished();
+            } finally {
+                // Ensure that any side-effect SharedPreferences writes have landed
+                waitForSharedPrefs();
+
+                Binder.restoreCallingIdentity(ident);
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                    // we'll time out anyway, so we're safe
+                }
+            }
+        }
+
+        @Override
         public void fail(String message) {
             getHandler().post(new FailRunnable(message));
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab33d75..833dc21 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -836,10 +836,6 @@
             pkg.baseCodePath = apkPath;
             pkg.mSignatures = null;
 
-            // TODO: Remove this when the WebView can load resources dynamically. b/11505352
-            pkg.usesOptionalLibraries = ArrayUtils.add(pkg.usesOptionalLibraries,
-                    "com.android.webview");
-
             return pkg;
 
         } catch (PackageParserException e) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 3a30123..2684e6c 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -100,7 +100,7 @@
         synchronized (sSync) {
             if (sSystem == null) {
                 AssetManager system = new AssetManager(true);
-                system.makeStringBlocks(false);
+                system.makeStringBlocks(null);
                 sSystem = system;
             }
         }
@@ -246,21 +246,21 @@
         if (mStringBlocks == null) {
             synchronized (this) {
                 if (mStringBlocks == null) {
-                    makeStringBlocks(true);
+                    makeStringBlocks(sSystem.mStringBlocks);
                 }
             }
         }
     }
 
-    /*package*/ final void makeStringBlocks(boolean copyFromSystem) {
-        final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;
+    /*package*/ final void makeStringBlocks(StringBlock[] seed) {
+        final int seedNum = (seed != null) ? seed.length : 0;
         final int num = getStringBlockCount();
         mStringBlocks = new StringBlock[num];
         if (localLOGV) Log.v(TAG, "Making string blocks for " + this
                 + ": " + num);
         for (int i=0; i<num; i++) {
-            if (i < sysNum) {
-                mStringBlocks[i] = sSystem.mStringBlocks[i];
+            if (i < seedNum) {
+                mStringBlocks[i] = seed[i];
             } else {
                 mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
             }
@@ -610,8 +610,11 @@
      * {@hide}
      */
     public final int addAssetPath(String path) {
-        int res = addAssetPathNative(path);
-        return res;
+        synchronized (this) {
+            int res = addAssetPathNative(path);
+            makeStringBlocks(mStringBlocks);
+            return res;
+        }
     }
 
     private native final int addAssetPathNative(String path);
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 04b2da5..3ab3b31 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -22,10 +22,13 @@
 import android.text.TextUtils;
 
 /**
- * A span that supplies additional meta-data intended for text-to-speech rendering
- * of the associated text.  If the text is being processed by a text-to-speech
- * engine, the engine may use the data in this span in addition to or instead of
- * its associated text.
+ * A span that supplies additional meta-data for the associated text intended
+ * for text-to-speech engines.  If the text is being processed by a
+ * text-to-speech engine, the engine may use the data in this span in addition
+ * to or instead of its associated text.
+ *
+ * The inner classes are there for convenience and provide builders for each
+ * TtsSpan type.
  */
 public class TtsSpan implements ParcelableSpan {
     private final String mType;
@@ -432,10 +435,18 @@
         mArgs = src.readPersistableBundle();
     }
 
+    /**
+     * Returns the type.
+     * @return The type of this instance.
+     */
     public String getType() {
         return mType;
     }
 
+    /**
+     * Returns a bundle of the arguments set.
+     * @return The bundle of the arguments set.
+     */
     public PersistableBundle getArgs() {
         return mArgs;
     }
@@ -455,4 +466,217 @@
     public int getSpanTypeId() {
         return TextUtils.TTS_SPAN;
     }
+
+    /**
+     * A simple builder for TtsSpans.
+     * This builder can be used directly, but the more specific subclasses of
+     * this builder like {@link TtsSpan.TextBuilder} and
+     * {@link TtsSpan.CardinalBuilder} are likely more useful.
+     *
+     * This class uses generics so methods from this class can return instances of
+     * its child classes, resulting in a fluent API (CRTP pattern).
+     */
+    public static abstract class Builder<C extends Builder<C>> {
+        // Holds the type of this class.
+        private final String mType;
+
+        // Holds the arguments of this class. It only stores objects of type
+        // String, Integer and Long.
+        private PersistableBundle mArgs = new PersistableBundle();
+
+        public Builder(String type) {
+            mType = type;
+        }
+
+        /**
+         * Returns a TtsSpan built from the parameters set by the setter
+         * methods.
+         * @return A TtsSpan built with parameters of this builder.
+         */
+        public TtsSpan build() {
+            return new TtsSpan(mType, mArgs);
+        }
+
+        /**
+         * Sets an argument to a string value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         * @return This instance.
+         */
+        @SuppressWarnings("unchecked")
+        public C setStringArgument(String arg, String value) {
+            mArgs.putString(arg, value);
+            return (C) this;
+        }
+
+        /**
+         * Sets an argument to an int value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         */
+        @SuppressWarnings("unchecked")
+        public C setIntArgument(String arg, int value) {
+            mArgs.putInt(arg, value);
+            return (C) this;
+        }
+
+        /**
+         * Sets an argument to a long value.
+         * @param arg The argument name.
+         * @param value The value the argument should be set to.
+         */
+        @SuppressWarnings("unchecked")
+        public C setLongArgument(String arg, long value) {
+            mArgs.putLong(arg, value);
+            return (C) this;
+        }
+    }
+
+    /**
+     * A builder for TtsSpans, has setters for morphosyntactic features.
+     * This builder can be used directly, but the more specific subclasses of
+     * this builder like {@link TtsSpan.TextBuilder} and
+     * {@link TtsSpan.CardinalBuilder} are likely more useful.
+     */
+    public static class SemioticClassBuilder<C extends SemioticClassBuilder<C>>
+            extends Builder<C> {
+
+        public SemioticClassBuilder(String type) {
+            super(type);
+        }
+
+        /**
+         * Sets the gender information for this instance.
+         * @param gender Can any of {@link TtsSpan#GENDER_NEUTRAL},
+         *     {@link TtsSpan#GENDER_MALE} and {@link TtsSpan#GENDER_FEMALE}.
+         * @return This instance.
+         */
+        public C setGender(String gender) {
+            return setStringArgument(TtsSpan.ARG_GENDER, gender);
+        }
+
+        /**
+         * Sets the animacy information for this instance.
+         * @param animacy Can be any of {@link TtsSpan#ANIMACY_ANIMATE} and
+         *     {@link TtsSpan#ANIMACY_INANIMATE}.
+         * @return This instance.
+         */
+        public C setAnimacy(String animacy) {
+            return setStringArgument(TtsSpan.ARG_ANIMACY, animacy);
+        }
+
+        /**
+         * Sets the multiplicity information for this instance.
+         * @param multiplicity Can be any of
+         *     {@link TtsSpan#MULTIPLICITY_SINGLE},
+         *     {@link TtsSpan#MULTIPLICITY_DUAL} and
+         *     {@link TtsSpan#MULTIPLICITY_PLURAL}.
+         * @return This instance.
+         */
+        public C setMultiplicity(String multiplicity) {
+            return setStringArgument(TtsSpan.ARG_MULTIPLICITY, multiplicity);
+        }
+
+        /**
+         * Sets the grammatical case information for this instance.
+         * @param grammaticalCase Can be any of {@link TtsSpan#CASE_NOMINATIVE},
+         *     {@link TtsSpan#CASE_ACCUSATIVE}, {@link TtsSpan#CASE_DATIVE},
+         *     {@link TtsSpan#CASE_ABLATIVE}, {@link TtsSpan#CASE_GENITIVE},
+         *     {@link TtsSpan#CASE_VOCATIVE}, {@link TtsSpan#CASE_LOCATIVE} and
+         *     {@link TtsSpan#CASE_INSTRUMENTAL}.
+         * @return This instance.
+         */
+        public C setCase(String grammaticalCase) {
+            return setStringArgument(TtsSpan.ARG_CASE, grammaticalCase);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_TEXT}.
+     */
+    public static class TextBuilder extends SemioticClassBuilder<TextBuilder> {
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_TEXT}.
+         */
+        public TextBuilder() {
+            super(TtsSpan.TYPE_TEXT);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_TEXT} and sets the
+         * {@link TtsSpan#ARG_TEXT} argument.
+         * @param text The text to be synthesized.
+         * @see #setText(String)
+         */
+        public TextBuilder(String text) {
+            this();
+            setText(text);
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_TEXT} argument, the text to be
+         * synthesized.
+         * @param text The string that will be synthesized.
+         * @return This instance.
+         */
+        public TextBuilder setText(String text) {
+            return setStringArgument(TtsSpan.ARG_TEXT, text);
+        }
+    }
+
+    /**
+     * A builder for TtsSpans of type {@link TtsSpan #TYPE_CARDINAL}.
+     */
+    public static class CardinalBuilder extends SemioticClassBuilder<CardinalBuilder> {
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL}.
+         */
+        public CardinalBuilder() {
+            super(TtsSpan.TYPE_CARDINAL);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The number to synthesize.
+         * @see #setNumber(long)
+         */
+        public CardinalBuilder(long number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Creates a TtsSpan of type {@link TtsSpan#TYPE_CARDINAL} and sets the
+         * {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number The number to synthesize.
+         * @see #setNumber(String)
+         */
+        public CardinalBuilder(String number) {
+            this();
+            setNumber(number);
+        }
+
+        /**
+         * Convenience method that converts the number to a String and set it to
+         * the value for {@link TtsSpan#ARG_NUMBER}.
+         * @param number The number that will be synthesized.
+         * @return This instance.
+         */
+        public CardinalBuilder setNumber(long number) {
+            return setNumber(String.valueOf(number));
+        }
+
+        /**
+         * Sets the {@link TtsSpan#ARG_NUMBER} argument.
+         * @param number A non-empty string of digits with an optional
+         *     leading + or -.
+         * @return This instance.
+         */
+        public CardinalBuilder setNumber(String number) {
+            return setStringArgument(TtsSpan.ARG_NUMBER, number);
+        }
+    }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f65aab5..0e22174 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,7 +122,6 @@
 	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 9f832b0..d17f46c 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -3,7 +3,6 @@
 
 #include "SkCamera.h"
 
-#include "Canvas.h"
 #include "GraphicsJNI.h"
 
 static jfieldID gNativeInstanceFieldID;
@@ -96,10 +95,10 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas(canvas);
+    v->applyToCanvas((SkCanvas*)native_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
new file mode 100644
index 0000000..6254f3d
--- /dev/null
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -0,0 +1,1330 @@
+/*
+ * 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
deleted file mode 100644
index 710845d..0000000
--- a/core/jni/android/graphics/Canvas.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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 74be577..5cc2b95 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -4,7 +4,6 @@
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
 
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkMath.h"
@@ -365,7 +364,7 @@
     SkASSERT(canvas);
     SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
     jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID);
-    SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+    SkCanvas* c = getNativeCanvas(canvasHandle);
     SkASSERT(c);
     return c;
 }
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 28a6edb..8150edf 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -47,6 +47,7 @@
     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 e82e8a6..ab5bdb0 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,7 +23,6 @@
 
 #include <Caches.h>
 
-#include "Canvas.h"
 #include "SkCanvas.h"
 #include "SkRegion.h"
 #include "GraphicsJNI.h"
@@ -120,7 +119,7 @@
     static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
         Res_png_9patch* chunk  = reinterpret_cast<Res_png_9patch*>(chunkHandle);
         const SkPaint* paint   = reinterpret_cast<SkPaint*>(paintHandle);
@@ -139,7 +138,7 @@
     static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
             jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
             jint destDensity, jint srcDensity) {
-        SkCanvas* canvas       = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+        SkCanvas* canvas       = GraphicsJNI::getNativeCanvas(canvasHandle);
         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 d214575..bc0c25f 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,13 +36,12 @@
     }
 }
 
-Canvas* Picture::beginRecording(int width, int height) {
+SkCanvas* Picture::beginRecording(int width, int height) {
     mPicture.reset(NULL);
     mRecorder.reset(new SkPictureRecorder);
     mWidth = width;
     mHeight = height;
-    SkCanvas* canvas = mRecorder->beginRecording(width, height, NULL, 0);
-    return Canvas::create_canvas(canvas);
+    return mRecorder->beginRecording(width, height, NULL, 0);
 }
 
 void Picture::endRecording() {
@@ -94,14 +93,14 @@
     }
 }
 
-void Picture::draw(Canvas* canvas) {
+void Picture::draw(SkCanvas* 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->getSkCanvas());
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
     }
 }
 
diff --git a/core/jni/android/graphics/Picture.h b/core/jni/android/graphics/Picture.h
index a2e5d4a..abb0403 100644
--- a/core/jni/android/graphics/Picture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -22,13 +22,14 @@
 #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
@@ -37,7 +38,7 @@
 public:
     explicit Picture(const Picture* src = NULL);
 
-    Canvas* beginRecording(int width, int height);
+    SkCanvas* beginRecording(int width, int height);
 
     void endRecording();
 
@@ -49,7 +50,7 @@
 
     void serialize(SkWStream* stream) const;
 
-    void draw(Canvas* canvas);
+    void draw(SkCanvas* canvas);
 
 private:
     int mWidth;
diff --git a/core/jni/android/graphics/SkiaCanvas.cpp b/core/jni/android/graphics/SkiaCanvas.cpp
deleted file mode 100644
index 5e93313..0000000
--- a/core/jni/android/graphics/SkiaCanvas.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * 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 9436a47..3812c27 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,9 +132,8 @@
         jint pageWidth, jint pageHeight,
         jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
-    SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
-            contentLeft, contentTop, contentRight, contentBottom);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
+    return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
+            contentLeft, contentTop, contentRight, contentBottom));
 }
 
 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
deleted file mode 100644
index fd96a90..0000000
--- a/core/jni/android_graphics_Canvas.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * 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 eb8f6dd..f827907 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) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasHandle);
+    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
     Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
     SkASSERT(canvas);
     SkASSERT(picture);
@@ -84,7 +84,12 @@
 static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
                                                      jint w, jint h) {
     Picture* pict = reinterpret_cast<Picture*>(pictHandle);
-    Canvas* canvas = pict->beginRecording(w, h);
+    // 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();
     return reinterpret_cast<jlong>(canvas);
 }
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ab19ad4..f57d61d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5085,6 +5085,9 @@
         <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
              default value is src_in, which treats the drawable as an alpha mask. -->
         <attr name="tintMode" />
+        <!-- Indicates if the drawable needs to be mirrored when its layout direction is
+             RTL (right-to-left). -->
+        <attr name="autoMirrored" />
     </declare-styleable>
 
     <!-- Define the virtual size of the drawing surface paths will draw to. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 08d354c..aaadc16 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -876,6 +876,7 @@
   <java-symbol type="string" name="config_customResolverActivity" />
   <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
   <java-symbol type="string" name="error_message_title" />
+  <java-symbol type="string" name="app_no_restricted_accounts" />
   <java-symbol type="string" name="action_bar_home_description_format" />
   <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
   <java-symbol type="string" name="wireless_display_route_description" />
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index ef4b260..72e2056 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 = nativeCanvas;
+        mNativeCanvasWrapper = initCanvas(nativeCanvas);
         mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
@@ -924,7 +924,7 @@
      * @param b blue component (0..255) of the color to draw onto the canvas
      */
     public void drawRGB(int r, int g, int b) {
-        drawColor(Color.rgb(r, g, b));
+        native_drawRGB(mNativeCanvasWrapper, r, g, b);
     }
 
     /**
@@ -937,7 +937,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) {
-        drawColor(Color.argb(a, r, g, b));
+        native_drawARGB(mNativeCanvasWrapper, a, r, g, b);
     }
 
     /**
@@ -947,7 +947,7 @@
      * @param color the color to draw onto the canvas
      */
     public void drawColor(int color) {
-        native_drawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+        native_drawColor(mNativeCanvasWrapper, color);
     }
 
     /**
@@ -1301,28 +1301,13 @@
      */
     public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
             @Nullable Paint paint) {
-      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);
-  }
+        if (dst == null) {
+            throw new NullPointerException();
+        }
+        throwIfCannotDraw(bitmap);
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
+                          paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
+    }
 
     /**
      * Draw the specified bitmap, scaling/translating automatically to fill
@@ -1352,23 +1337,8 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        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);
+        native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst,
+                paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
     }
 
     /**
@@ -1896,6 +1866,7 @@
     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);
@@ -1948,6 +1919,11 @@
     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,
@@ -1989,9 +1965,16 @@
                                                  int screenDensity,
                                                  int bitmapDensity);
     private native void native_drawBitmap(long nativeCanvas, long nativeBitmap,
-            float srcLeft, float srcTop, float srcRight, float srcBottom,
-            float dstLeft, float dstTop, float dstRight, float dstBottom,
-            long nativePaintOrZero, int screenDensity, int bitmapDensity);
+                                                 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);
     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/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 8c907b2..43e6509 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -33,6 +33,7 @@
 import android.graphics.PorterDuff.Mode;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.LayoutDirection;
 import android.util.Log;
 import android.util.PathParser;
 import android.util.Xml;
@@ -187,7 +188,13 @@
     public void draw(Canvas canvas) {
         final int saveCount = canvas.save();
         final Rect bounds = getBounds();
+        final boolean needMirroring = needMirroring();
+
         canvas.translate(bounds.left, bounds.top);
+        if (needMirroring) {
+            canvas.translate(bounds.width(), 0);
+            canvas.scale(-1.0f, 1.0f);
+        }
 
         if (!mAllowCaching) {
             mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
@@ -205,6 +212,7 @@
             }
             canvas.drawBitmap(bitmap, null, bounds, null);
         }
+
         canvas.restoreToCount(saveCount);
     }
 
@@ -352,6 +360,9 @@
         if (tint != null) {
             state.mTint = tint;
         }
+
+        state.mAutoMirrored = a.getBoolean(
+                R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
     }
 
     private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
@@ -469,12 +480,30 @@
         mAllowCaching = allowCaching;
     }
 
+    private boolean needMirroring() {
+        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        if (mVectorState.mAutoMirrored != mirrored) {
+            mVectorState.mAutoMirrored = mirrored;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mVectorState.mAutoMirrored;
+    }
+
     private static class VectorDrawableState extends ConstantState {
         int[] mThemeAttrs;
         int mChangingConfigurations;
         VPathRenderer mVPathRenderer;
         ColorStateList mTint;
         Mode mTintMode;
+        boolean mAutoMirrored;
 
         Bitmap mCachedBitmap;
         int[] mCachedThemeAttrs;
@@ -490,6 +519,7 @@
                 mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
                 mTint = copy.mTint;
                 mTintMode = copy.mTintMode;
+                mAutoMirrored = copy.mAutoMirrored;
             }
         }
 
@@ -497,6 +527,7 @@
             if (mCachedThemeAttrs == mThemeAttrs
                     && mCachedTint == mTint
                     && mCachedTintMode == mTintMode
+                    && mAutoMirrored == mAutoMirrored
                     && width == mCachedBitmap.getWidth()
                     && height == mCachedBitmap.getHeight()
                     && mCachedRootAlpha == mVPathRenderer.getRootAlpha())  {
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 610528c..99b3195 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -278,6 +278,7 @@
     const ResTable* getResTable(bool required = true) const;
     void setLocaleLocked(const char* locale);
     void updateResourceParamsLocked() const;
+    bool appendPathToResTable(const asset_path& ap) const;
 
     Asset* openIdmapLocked(const struct asset_path& ap) const;
 
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 4ba20d7..de6a33cf 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -231,6 +231,10 @@
     }
 #endif
 
+    if (mResources != NULL) {
+        appendPathToResTable(ap);
+    }
+
     return true;
 }
 
@@ -596,6 +600,96 @@
         return kFileTypeRegular;
 }
 
+bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+    Asset* ass = NULL;
+    ResTable* sharedRes = NULL;
+    bool shared = true;
+    bool onlyEmptyResources = true;
+    MY_TRACE_BEGIN(ap.path.string());
+    Asset* idmap = openIdmapLocked(ap);
+    size_t nextEntryIdx = mResources->getTableCount();
+    ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
+    if (ap.type != kFileTypeDirectory) {
+        if (nextEntryIdx == 0) {
+            // The first item is typically the framework resources,
+            // which we want to avoid parsing every time.
+            sharedRes = const_cast<AssetManager*>(this)->
+                mZipSet.getZipResourceTable(ap.path);
+            if (sharedRes != NULL) {
+                // skip ahead the number of system overlay packages preloaded
+                nextEntryIdx = sharedRes->getTableCount();
+            }
+        }
+        if (sharedRes == NULL) {
+            ass = const_cast<AssetManager*>(this)->
+                mZipSet.getZipResourceTableAsset(ap.path);
+            if (ass == NULL) {
+                ALOGV("loading resource table %s\n", ap.path.string());
+                ass = const_cast<AssetManager*>(this)->
+                    openNonAssetInPathLocked("resources.arsc",
+                                             Asset::ACCESS_BUFFER,
+                                             ap);
+                if (ass != NULL && ass != kExcludedAsset) {
+                    ass = const_cast<AssetManager*>(this)->
+                        mZipSet.setZipResourceTableAsset(ap.path, ass);
+                }
+            }
+            
+            if (nextEntryIdx == 0 && ass != NULL) {
+                // If this is the first resource table in the asset
+                // manager, then we are going to cache it so that we
+                // can quickly copy it out for others.
+                ALOGV("Creating shared resources for %s", ap.path.string());
+                sharedRes = new ResTable();
+                sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
+#ifdef HAVE_ANDROID_OS
+                const char* data = getenv("ANDROID_DATA");
+                LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+                String8 overlaysListPath(data);
+                overlaysListPath.appendPath(kResourceCache);
+                overlaysListPath.appendPath("overlays.list");
+                addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+#endif
+                sharedRes = const_cast<AssetManager*>(this)->
+                    mZipSet.setZipResourceTable(ap.path, sharedRes);
+            }
+        }
+    } else {
+        ALOGV("loading resource table %s\n", ap.path.string());
+        ass = const_cast<AssetManager*>(this)->
+            openNonAssetInPathLocked("resources.arsc",
+                                     Asset::ACCESS_BUFFER,
+                                     ap);
+        shared = false;
+    }
+
+    if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
+        ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
+        if (sharedRes != NULL) {
+            ALOGV("Copying existing resources for %s", ap.path.string());
+            mResources->add(sharedRes);
+        } else {
+            ALOGV("Parsing resources for %s", ap.path.string());
+            mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+        }
+        onlyEmptyResources = false;
+
+        if (!shared) {
+            delete ass;
+        }
+    } else {
+        ALOGV("Installing empty resources in to table %p\n", mResources);
+        mResources->addEmpty(nextEntryIdx + 1);
+    }
+
+    if (idmap != NULL) {
+        delete idmap;
+    }
+    MY_TRACE_END();
+
+    return onlyEmptyResources;
+}
+
 const ResTable* AssetManager::getResTable(bool required) const
 {
     ResTable* rt = mResources;
@@ -625,90 +719,8 @@
     bool onlyEmptyResources = true;
     const size_t N = mAssetPaths.size();
     for (size_t i=0; i<N; i++) {
-        Asset* ass = NULL;
-        ResTable* sharedRes = NULL;
-        bool shared = true;
-        const asset_path& ap = mAssetPaths.itemAt(i);
-        MY_TRACE_BEGIN(ap.path.string());
-        Asset* idmap = openIdmapLocked(ap);
-        ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
-        if (ap.type != kFileTypeDirectory) {
-            if (i == 0) {
-                // The first item is typically the framework resources,
-                // which we want to avoid parsing every time.
-                sharedRes = const_cast<AssetManager*>(this)->
-                    mZipSet.getZipResourceTable(ap.path);
-                if (sharedRes != NULL) {
-                    // skip ahead the number of system overlay packages preloaded
-                    i += sharedRes->getTableCount() - 1;
-                }
-            }
-            if (sharedRes == NULL) {
-                ass = const_cast<AssetManager*>(this)->
-                    mZipSet.getZipResourceTableAsset(ap.path);
-                if (ass == NULL) {
-                    ALOGV("loading resource table %s\n", ap.path.string());
-                    ass = const_cast<AssetManager*>(this)->
-                        openNonAssetInPathLocked("resources.arsc",
-                                                 Asset::ACCESS_BUFFER,
-                                                 ap);
-                    if (ass != NULL && ass != kExcludedAsset) {
-                        ass = const_cast<AssetManager*>(this)->
-                            mZipSet.setZipResourceTableAsset(ap.path, ass);
-                    }
-                }
-                
-                if (i == 0 && ass != NULL) {
-                    // If this is the first resource table in the asset
-                    // manager, then we are going to cache it so that we
-                    // can quickly copy it out for others.
-                    ALOGV("Creating shared resources for %s", ap.path.string());
-                    sharedRes = new ResTable();
-                    sharedRes->add(ass, idmap, i + 1, false);
-#ifdef HAVE_ANDROID_OS
-                    const char* data = getenv("ANDROID_DATA");
-                    LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
-                    String8 overlaysListPath(data);
-                    overlaysListPath.appendPath(kResourceCache);
-                    overlaysListPath.appendPath("overlays.list");
-                    addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, i);
-#endif
-                    sharedRes = const_cast<AssetManager*>(this)->
-                        mZipSet.setZipResourceTable(ap.path, sharedRes);
-                }
-            }
-        } else {
-            ALOGV("loading resource table %s\n", ap.path.string());
-            ass = const_cast<AssetManager*>(this)->
-                openNonAssetInPathLocked("resources.arsc",
-                                         Asset::ACCESS_BUFFER,
-                                         ap);
-            shared = false;
-        }
-
-        if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
-            ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
-            if (sharedRes != NULL) {
-                ALOGV("Copying existing resources for %s", ap.path.string());
-                mResources->add(sharedRes);
-            } else {
-                ALOGV("Parsing resources for %s", ap.path.string());
-                mResources->add(ass, idmap, i + 1, !shared);
-            }
-            onlyEmptyResources = false;
-
-            if (!shared) {
-                delete ass;
-            }
-        } else {
-            ALOGV("Installing empty resources in to table %p\n", mResources);
-            mResources->addEmpty(i + 1);
-        }
-
-        if (idmap != NULL) {
-            delete idmap;
-        }
-        MY_TRACE_END();
+        bool empty = appendPathToResTable(mAssetPaths.itemAt(i));
+        onlyEmptyResources = onlyEmptyResources && empty;
     }
 
     if (required && onlyEmptyResources) {
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 8e3c444..548ec91 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -198,6 +198,7 @@
         drawDeferDisabled = !strcasecmp(property, "true");
         INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
     } else {
+        drawDeferDisabled = false;
         INIT_LOGD("  Draw defer enabled");
     }
 
@@ -205,6 +206,7 @@
         drawReorderDisabled = !strcasecmp(property, "true");
         INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
     } else {
+        drawReorderDisabled = false;
         INIT_LOGD("  Draw reorder enabled");
     }
 
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 3dc9ffb..37f166b 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -76,6 +76,7 @@
 
     private final ResolveInfo mService;
     private final String mId;
+    private final String mParentId;
 
     // Attributes from XML meta data.
     private String mSetupActivity;
@@ -114,7 +115,7 @@
                         "Meta-data does not start with tv-input-service tag in " + si.name);
             }
 
-            TvInputInfo input = new TvInputInfo(context, service);
+            TvInputInfo input = new TvInputInfo(context, service, null);
             TypedArray sa = res.obtainAttributes(attrs,
                     com.android.internal.R.styleable.TvInputService);
             input.mSetupActivity = sa.getString(
@@ -154,10 +155,11 @@
      * @param service The ResolveInfo returned from the package manager about this TV input service.
      * @hide
      */
-    private TvInputInfo(Context context, ResolveInfo service) {
+    private TvInputInfo(Context context, ResolveInfo service, String parentId) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
         mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
+        mParentId = parentId;
     }
 
     /**
@@ -169,6 +171,24 @@
     }
 
     /**
+     * Returns the parent input ID.
+     * <p>
+     * When a part of the functionalities of a TV input is actually provided by another TV input,
+     * we can describe this relationship as the depending input having a "parent". It is primarily
+     * used for controlling underlying hardware when the current input itself does not have direct
+     * access to it. Examples include a TV input for a specific HDMI CEC logical device having a
+     * generic HDMI input as its parent and a HDMI-paired virtual input whose video stream comes
+     * from an external settop box. Applications may group inputs by parent ID to provide an easier
+     * access to similar inputs.
+     *
+     * @return the ID of the parent input, if exists. Returns {@code null} if the parent input is
+     *         not specified.
+     */
+    public String getParentId() {
+        return mParentId;
+    }
+
+    /**
      * Returns the information of the service that implements this TV input.
      */
     public ServiceInfo getServiceInfo() {
@@ -260,9 +280,7 @@
         }
 
         TvInputInfo obj = (TvInputInfo) o;
-        return mId.equals(obj.mId)
-                && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
-                && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
+        return mId.equals(obj.mId);
     }
 
     @Override
@@ -281,9 +299,11 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mId);
+        dest.writeString(mParentId);
         mService.writeToParcel(dest, flags);
         dest.writeString(mSetupActivity);
         dest.writeString(mSettingsActivity);
+        dest.writeInt(mType);
     }
 
     /**
@@ -317,8 +337,10 @@
 
     private TvInputInfo(Parcel in) {
         mId = in.readString();
+        mParentId = in.readString();
         mService = ResolveInfo.CREATOR.createFromParcel(in);
         mSetupActivity = in.readString();
         mSettingsActivity = in.readString();
+        mType = in.readInt();
     }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b7210e1..5752646 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8c8e9a..f8c9d4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -703,4 +703,5 @@
 
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">Contents hidden</string>
+    <string name="guest_exit_guest">Exit guest</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 3c647ed..67eef56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -81,13 +81,7 @@
             UserSwitcherController.UserRecord item = getItem(position);
             UserDetailItemView v = UserDetailItemView.convertOrInflate(
                     mContext, convertView, parent);
-            String name;
-            if (item.isGuest) {
-                name = mContext.getString(
-                        item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
-            } else {
-                name = item.info.name;
-            }
+            String name = getName(mContext, item);
             if (item.picture == null) {
                 v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle_qs));
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 0b35f59..8b86258 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -628,6 +628,7 @@
     public void computeScroll() {
         if (mScroller.computeScrollOffset()) {
             setStackScroll(mScroller.getCurrY());
+            invalidate();
 
             // If we just finished scrolling, then disable the hw layers
             if (mScroller.isFinished()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4640067..7cc8ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -30,8 +30,10 @@
 import android.graphics.Bitmap;
 import android.os.AsyncTask;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManagerGlobal;
@@ -64,15 +66,37 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mReceiver, filter);
-        refreshUsers();
+        filter.addAction(Intent.ACTION_USER_STOPPING);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
+                null /* permission */, null /* scheduler */);
+        refreshUsers(UserHandle.USER_NULL);
     }
 
-    private void refreshUsers() {
-        new AsyncTask<Void, Void, ArrayList<UserRecord>>() {
+    /**
+     * Refreshes users from UserManager.
+     *
+     * The pictures are only loaded if they have not been loaded yet.
+     *
+     * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+     */
+    private void refreshUsers(int forcePictureLoadForId) {
 
+        SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
+        final int N = mUsers.size();
+        for (int i = 0; i < N; i++) {
+            UserRecord r = mUsers.get(i);
+            if (r == null || r.info == null
+                    || r.info.id == forcePictureLoadForId || r.picture == null) {
+                continue;
+            }
+            bitmaps.put(r.info.id, r.picture);
+        }
+
+        new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
+            @SuppressWarnings("unchecked")
             @Override
-            protected ArrayList<UserRecord> doInBackground(Void... params) {
+            protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
+                final SparseArray<Bitmap> bitmaps = params[0];
                 List<UserInfo> infos = mUserManager.getUsers(true);
                 if (infos == null) {
                     return null;
@@ -87,8 +111,11 @@
                         guestRecord = new UserRecord(info, null /* picture */,
                                 true /* isGuest */, isCurrent);
                     } else if (!info.isManagedProfile()) {
-                        records.add(new UserRecord(info, mUserManager.getUserIcon(info.id),
-                                false /* isGuest */, isCurrent));
+                        Bitmap picture = bitmaps.get(info.id);
+                        if (picture == null) {
+                            picture = mUserManager.getUserIcon(info.id);
+                        }
+                        records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent));
                     }
                 }
 
@@ -109,7 +136,7 @@
                     notifyAdapters();
                 }
             }
-        }.execute((Void[])null);
+        }.execute((SparseArray)bitmaps);
     }
 
     private void notifyAdapters() {
@@ -134,9 +161,16 @@
         }
 
         if (ActivityManager.getCurrentUser() == id) {
+            if (record.isGuest) {
+                exitGuest(id);
+            }
             return;
         }
 
+        switchToUserId(id);
+    }
+
+    private void switchToUserId(int id) {
         try {
             WindowManagerGlobal.getWindowManagerService().lockNow(null);
             ActivityManagerNative.getDefault().switchUser(id);
@@ -145,6 +179,12 @@
         }
     }
 
+    private void exitGuest(int id) {
+        // TODO: show confirmation dialog
+        switchToUserId(UserHandle.USER_OWNER);
+        mUserManager.removeUser(id);
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -153,15 +193,20 @@
                 final int N = mUsers.size();
                 for (int i = 0; i < N; i++) {
                     UserRecord record = mUsers.get(i);
+                    if (record.info == null) continue;
                     boolean shouldBeCurrent = record.info.id == currentId;
                     if (record.isCurrent != shouldBeCurrent) {
                         mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
                     }
                 }
                 notifyAdapters();
-            } else {
-                refreshUsers();
             }
+            int forcePictureLoadForId = UserHandle.USER_NULL;
+            if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
+                forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL);
+            }
+            refreshUsers(forcePictureLoadForId);
         }
     };
 
@@ -195,12 +240,25 @@
 
         @Override
         public long getItemId(int position) {
-            return mController.mUsers.get(position).info.id;
+            return position;
         }
 
         public void switchTo(UserRecord record) {
             mController.switchTo(record);
         }
+
+        public String getName(Context context, UserRecord item) {
+            if (item.isGuest) {
+                if (item.isCurrent) {
+                    return context.getString(R.string.guest_exit_guest);
+                } else {
+                    return context.getString(
+                            item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
+                }
+            } else {
+                return item.info.name;
+            }
+        }
     }
 
     public static final class UserRecord {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e8e2813..f54f798 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -233,6 +233,7 @@
     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
     static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+    static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
 
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
@@ -6309,6 +6310,7 @@
         RUNNING_QUEUE,
         RESTORE_KEYVALUE,
         RESTORE_FULL,
+        RESTORE_FINISHED,
         FINAL
     }
 
@@ -6343,6 +6345,9 @@
         // Our bookkeeping about the ancestral dataset
         private PackageManagerBackupAgent mPmAgent;
 
+        // Currently-bound backup agent for restore + restoreFinished purposes
+        private IBackupAgent mAgent;
+
         // What sort of restore we're doing now
         private RestoreDescription mRestoreDescription;
 
@@ -6441,6 +6446,13 @@
                     // this one is always valid too
                 }
             }
+
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
+                for (PackageInfo info : mAcceptSet) {
+                    Slog.v(TAG, "   " + info.packageName);
+                }
+            }
         }
 
         private String[] packagesToNames(List<PackageInfo> apps) {
@@ -6473,6 +6485,10 @@
                     restoreFull();
                     break;
 
+                case RESTORE_FINISHED:
+                    restoreFinished();
+                    break;
+
                 case FINAL:
                     if (!mFinished) finalizeRestore();
                     else {
@@ -6529,7 +6545,7 @@
         private  void startRestore() {
             sendStartRestore(mAcceptSet.size());
 
-            UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
+            UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
             try {
                 // If we don't yet have PM metadata for this token, synthesize an
                 // entry for the PM pseudopackage and make it the first to be
@@ -6544,6 +6560,7 @@
                     mPmAgent = new PackageManagerBackupAgent(metadataFile);
                 } catch (IOException e) {
                     // Nope, we need to get it via restore
+                    if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");
                     PackageInfo pmPackage = new PackageInfo();
                     pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mAcceptSet.add(0, pmPackage);
@@ -6581,8 +6598,11 @@
                     mCurrentPackage = new PackageInfo();
                     mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
-                    initiateOneRestore(mCurrentPackage, 0,
-                            IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
+                    mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
+                    if (MORE_DEBUG) {
+                        Slog.v(TAG, "initiating restore for PMBA");
+                    }
+                    initiateOneRestore(mCurrentPackage, 0);
                     // The PM agent called operationComplete() already, because our invocation
                     // of it is process-local and therefore synchronous.  That means that a
                     // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
@@ -6621,6 +6641,11 @@
                         nextState = UnifiedRestoreState.FINAL;
                         return;
                     }
+                } else {
+                    // We have the PMBA already, so we can proceed directly to
+                    // the RUNNING_QUEUE state ourselves.
+                    if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
                 }
             } catch (RemoteException e) {
                 // If we lost the transport at any time, halt
@@ -6762,10 +6787,10 @@
             }
 
             // Good to go!  Set up and bind the agent...
-            IBackupAgent agent = bindToAgentSynchronous(
+            mAgent = bindToAgentSynchronous(
                     mCurrentPackage.applicationInfo,
                     IApplicationThread.BACKUP_MODE_INCREMENTAL);
-            if (agent == null) {
+            if (mAgent == null) {
                 Slog.w(TAG, "Can't find backup agent for " + packageName);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Restore agent missing");
@@ -6775,7 +6800,7 @@
 
             // And then finally start the restore on this agent
             try {
-                initiateOneRestore(mCurrentPackage, metaInfo.versionCode, agent);
+                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
                 ++mCount;
             } catch (Exception e) {
                 Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -6785,7 +6810,7 @@
         }
 
         // Guts of a key/value restore operation
-        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
+        void initiateOneRestore(PackageInfo app, int appVersionCode) {
             final String packageName = app.packageName;
 
             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
@@ -6881,7 +6906,7 @@
                 // the operationComplete() callback will schedule the next step,
                 // so we do not do that here.
                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
-                agent.doRestore(mBackupData, appVersionCode, mNewState,
+                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                         token, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
@@ -6926,6 +6951,20 @@
             }
         }
 
+        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
+        private void restoreFinished() {
+            try {
+                final int token = generateToken();
+                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
+                mAgent.doRestoreFinished(token, mBackupManagerBinder);
+                // If we get this far, the callback or timeout will schedule the
+                // next restore state, so we're done
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
+                executeNextState(UnifiedRestoreState.FINAL);
+            }
+        }
+
         class StreamFeederThread extends RestoreEngine implements Runnable {
             final String TAG = "StreamFeederThread";
             FullRestoreEngine mEngine;
@@ -7202,23 +7241,58 @@
 
         @Override
         public void operationComplete() {
-            int size = (int) mBackupDataName.length();
-            EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
-
-            // Just go back to running the restore queue
-            keyValueAgentCleanup();
-
-            // If there was widget state associated with this app, get the OS to
-            // incorporate it into current bookeeping and then pass that along to
-            // the app as part of the restore operation.
-            if (mWidgetData != null) {
-                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "operationComplete() during restore: target="
+                        + mCurrentPackage.packageName
+                        + " state=" + mState);
             }
 
-            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
+            final UnifiedRestoreState nextState;
+            switch (mState) {
+                case RESTORE_KEYVALUE:
+                case RESTORE_FULL: {
+                    // Okay, we've just heard back from the agent that it's done with
+                    // the restore itself.  We now have to send the same agent its
+                    // doRestoreFinished() callback, so roll into that state.
+                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
+                    break;
+                }
+
+                case RESTORE_FINISHED: {
+                    // Okay, we're done with this package.  Tidy up and go on to the next
+                    // app in the queue.
+                    int size = (int) mBackupDataName.length();
+                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
+                            mCurrentPackage.packageName, size);
+
+                    // Just go back to running the restore queue
+                    keyValueAgentCleanup();
+
+                    // If there was widget state associated with this app, get the OS to
+                    // incorporate it into current bookeeping and then pass that along to
+                    // the app as part of the restore-time work.
+                    if (mWidgetData != null) {
+                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+                    }
+
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
+                    break;
+                }
+
+                default: {
+                    // Some kind of horrible semantic error; we're in an unexpected state.
+                    // Back off hard and wind up.
+                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
+                    keyValueAgentErrorCleanup();
+                    nextState = UnifiedRestoreState.FINAL;
+                    break;
+                }
+            }
+
+            executeNextState(nextState);
         }
 
-        // A call to agent.doRestore() has timed out
+        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
         @Override
         public void handleTimeout() {
             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e152ebe..36d67ee 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -871,18 +871,85 @@
         checkManageAccountsPermission();
         UserHandle user = Binder.getCallingUserHandle();
         UserAccounts accounts = getUserAccountsForCaller();
-        if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId)) {
             try {
+                // TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768
                 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
                         "User cannot modify accounts");
             } catch (RemoteException re) {
             }
             return;
         }
+        if (!canUserModifyAccountsForType(userId, account.type)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
 
         long identityToken = clearCallingIdentity();
 
         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
+        synchronized (accounts.credentialsPermissionNotificationIds) {
+            for (Pair<Pair<Account, String>, Integer> pair:
+                accounts.credentialsPermissionNotificationIds.keySet()) {
+                if (account.equals(pair.first.first)) {
+                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
+                    cancelNotification(id, user);
+                }
+            }
+        }
+
+        try {
+            new RemoveAccountSession(accounts, response, account).bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    @Override
+    public void removeAccountAsUser(IAccountManagerResponse response, Account account,
+            int userId) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "removeAccount: " + account
+                    + ", response " + response
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid()
+                    + ", for user id " + userId);
+        }
+        if (response == null) throw new IllegalArgumentException("response is null");
+        if (account == null) throw new IllegalArgumentException("account is null");
+
+        // Only allow the system process to modify accounts of other users
+        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+                    + " trying to remove account for " + userId);
+        checkManageAccountsPermission();
+
+        UserAccounts accounts = getUserAccounts(userId);
+        if (!canUserModifyAccounts(userId)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+                        "User cannot modify accounts");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, account.type)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            return;
+        }
+
+        UserHandle user = new UserHandle(userId);
+        long identityToken = clearCallingIdentity();
+
+        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
         synchronized(accounts.credentialsPermissionNotificationIds) {
             for (Pair<Pair<Account, String>, Integer> pair:
                 accounts.credentialsPermissionNotificationIds.keySet()) {
@@ -1526,20 +1593,23 @@
         checkManageAccountsPermission();
 
         // Is user disallowed from modifying accounts?
-        if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
             } catch (RemoteException re) {
             }
-            Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
-            cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            long identityToken = clearCallingIdentity();
+            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, accountType)) {
             try {
-                mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT);
-            } finally {
-                restoreCallingIdentity(identityToken);
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
             }
+            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
             return;
         }
 
@@ -1576,6 +1646,92 @@
     }
 
     @Override
+    public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccount: accountType " + accountType
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid()
+                    + ", for user id " + userId);
+        }
+        if (response == null) throw new IllegalArgumentException("response is null");
+        if (accountType == null) throw new IllegalArgumentException("accountType is null");
+        checkManageAccountsPermission();
+
+        // Only allow the system process to add accounts of other users
+        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+                    + " trying to add account for " + userId);
+
+        // Is user disallowed from modifying accounts?
+        if (!canUserModifyAccounts(userId)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+                        "User is not allowed to add an account!");
+            } catch (RemoteException re) {
+            }
+            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
+            return;
+        }
+        if (!canUserModifyAccountsForType(userId, accountType)) {
+            try {
+                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+                        "User cannot modify accounts of this type (policy).");
+            } catch (RemoteException re) {
+            }
+            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
+            return;
+        }
+
+        UserAccounts accounts = getUserAccounts(userId);
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
+        options.putInt(AccountManager.KEY_CALLER_UID, uid);
+        options.putInt(AccountManager.KEY_CALLER_PID, pid);
+
+        long identityToken = clearCallingIdentity();
+        try {
+            new Session(accounts, response, accountType, expectActivityLaunch,
+                    true /* stripAuthTokenFromResult */) {
+                @Override
+                public void run() throws RemoteException {
+                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
+                            options);
+                }
+
+                @Override
+                protected String toDebugString(long now) {
+                    return super.toDebugString(now) + ", addAccount"
+                            + ", accountType " + accountType
+                            + ", requiredFeatures "
+                            + (requiredFeatures != null
+                              ? TextUtils.join(",", requiredFeatures)
+                              : null);
+                }
+            }.bind();
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void showCantAddAccount(int errorCode) {
+        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
+        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
+        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        long identityToken = clearCallingIdentity();
+        try {
+            mContext.startActivity(cantAddAccount);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
+    }
+
+    @Override
     public void confirmCredentialsAsUser(IAccountManagerResponse response,
             final Account account, final Bundle options, final boolean expectActivityLaunch,
             int userId) {
@@ -2766,18 +2922,18 @@
                 Manifest.permission.USE_CREDENTIALS);
     }
 
-    private boolean canUserModifyAccounts(int callingUid, String accountType) {
-        if (callingUid != Process.myUid()) {
-            if (getUserManager().getUserRestrictions(
-                    new UserHandle(UserHandle.getUserId(callingUid)))
-                    .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
-                return false;
-            }
+    private boolean canUserModifyAccounts(int userId) {
+        if (getUserManager().getUserRestrictions(new UserHandle(userId))
+                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
+            return false;
         }
+        return true;
+    }
 
+    private boolean canUserModifyAccountsForType(int userId, String accountType) {
         DevicePolicyManager dpm = (DevicePolicyManager) mContext
                 .getSystemService(Context.DEVICE_POLICY_SERVICE);
-        String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
+        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
         for (String forbiddenType : typesArray) {
             if (forbiddenType.equals(accountType)) {
                 return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8b7e0d6..31c1c6c 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -364,28 +364,6 @@
     // devices.
     private boolean mShowDialogs = true;
 
-    /**
-     * Description of a request to start a new activity, which has been held
-     * due to app switches being disabled.
-     */
-    static class PendingActivityLaunch {
-        final ActivityRecord r;
-        final ActivityRecord sourceRecord;
-        final int startFlags;
-        final ActivityStack stack;
-
-        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack) {
-            r = _r;
-            sourceRecord = _sourceRecord;
-            startFlags = _startFlags;
-            stack = _stack;
-        }
-    }
-
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
-            = new ArrayList<PendingActivityLaunch>();
-
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -1438,7 +1416,7 @@
             } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    doPendingActivityLaunchesLocked(true);
+                    mStackSupervisor.doPendingActivityLaunchesLocked(true);
                 }
             } break;
             case KILL_APPLICATION_MSG: {
@@ -3339,19 +3317,6 @@
         mProcessObservers.finishBroadcast();
     }
 
-    final void doPendingActivityLaunchesLocked(boolean doResume) {
-        final int N = mPendingActivityLaunches.size();
-        if (N <= 0) {
-            return;
-        }
-        for (int i=0; i<N; i++) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
-            mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                    doResume && i == (N-1), null);
-        }
-        mPendingActivityLaunches.clear();
-    }
-
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo,
@@ -9140,6 +9105,7 @@
         }
     }
 
+    @Override
     public void stopAppSwitches() {
         if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 32f2624..91bc7e3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2732,18 +2732,23 @@
         return r;
     }
 
-    void finishAllActivitiesLocked() {
+    void finishAllActivitiesLocked(boolean immediately) {
+        boolean noActivitiesInStack = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
+                noActivitiesInStack = false;
+                if (r.finishing && !immediately) {
                     continue;
                 }
-                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
                 finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
             }
         }
+        if (noActivitiesInStack) {
+            mActivityContainer.onTaskListEmptyLocked();
+        }
     }
 
     final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
@@ -2858,6 +2863,7 @@
         // down to the max limit while they are still waiting to finish.
         mStackSupervisor.mFinishingActivities.remove(r);
         mStackSupervisor.mWaitingVisibleActivities.remove(r);
+        mStackSupervisor.removePendingActivityLaunchesLocked(r);
 
         // Remove any pending results.
         if (r.finishing && r.pendingResults != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b0dfe4a..8c8d14b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,7 +98,6 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.wm.WindowManagerService;
 
@@ -271,6 +270,28 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+            = new ArrayList<PendingActivityLaunch>();
+
+    /**
+     * Description of a request to start a new activity, which has been held
+     * due to app switches being disabled.
+     */
+    static class PendingActivityLaunch {
+        final ActivityRecord r;
+        final ActivityRecord sourceRecord;
+        final int startFlags;
+        final ActivityStack stack;
+
+        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+                int _startFlags, ActivityStack _stack) {
+            r = _r;
+            sourceRecord = _sourceRecord;
+            startFlags = _startFlags;
+            stack = _stack;
+        }
+    }
+
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
         mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
@@ -1421,7 +1442,7 @@
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
                 PendingActivityLaunch pal =
                         new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
-                mService.mPendingActivityLaunches.add(pal);
+                mPendingActivityLaunches.add(pal);
                 setDismissKeyguard(false);
                 ActivityOptions.abort(options);
                 return ActivityManager.START_SWITCHES_CANCELED;
@@ -1439,7 +1460,7 @@
             mService.mDidAppSwitch = true;
         }
 
-        mService.doPendingActivityLaunchesLocked(false);
+        doPendingActivityLaunchesLocked(false);
 
         err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                 startFlags, true, options);
@@ -2008,6 +2029,23 @@
         return ActivityManager.START_SUCCESS;
     }
 
+    final void doPendingActivityLaunchesLocked(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                    doResume && mPendingActivityLaunches.isEmpty(), null);
+        }
+    }
+
+    void removePendingActivityLaunchesLocked(ActivityRecord r) {
+        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            if (pal.r == r) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+    }
+
     void acquireLaunchWakelock() {
         if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
             throw new IllegalStateException("Calling must be system uid");
@@ -3312,7 +3350,9 @@
                     synchronized (mService) {
                         Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
                                 msg.obj);
-                        ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
+                        final ActivityContainer container = (ActivityContainer) msg.obj;
+                        container.mStack.finishAllActivitiesLocked(true);
+                        container.onTaskListEmptyLocked();
                     }
                 } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
@@ -3414,11 +3454,11 @@
 
                 final Message msg =
                         mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
-                mHandler.sendMessageDelayed(msg, 1000);
+                mHandler.sendMessageDelayed(msg, 2000);
 
                 long origId = Binder.clearCallingIdentity();
                 try {
-                    mStack.finishAllActivitiesLocked();
+                    mStack.finishAllActivitiesLocked(false);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3882769..43469ba 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -52,7 +52,6 @@
 import android.util.Xml;
 
 import com.android.internal.app.IAppOpsService;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
@@ -241,7 +240,6 @@
     }
 
     void systemReady() {
-        mUserPackageMonitor.register(mContext, null, UserHandle.ALL, false);
         userForeground(UserHandle.USER_OWNER);
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -1039,11 +1037,10 @@
     }
 
     /**
-     * Removes all the restrictions files (res_<packagename>) for a given user, if all is true,
-     * else removes only those packages that have been uninstalled.
+     * Removes all the restrictions files (res_<packagename>) for a given user.
      * Does not do any permissions checking.
      */
-    private void cleanAppRestrictions(int userId, boolean all) {
+    private void cleanAppRestrictions(int userId) {
         synchronized (mPackagesLock) {
             File dir = Environment.getUserSystemDirectory(userId);
             String[] files = dir.list();
@@ -1052,14 +1049,7 @@
                 if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
                     File resFile = new File(dir, fileName);
                     if (resFile.exists()) {
-                        if (all) {
-                            resFile.delete();
-                        } else {
-                            String pkg = restrictionsFileNameToPackage(fileName);
-                            if (!isPackageInstalled(pkg, userId)) {
-                                resFile.delete();
-                            }
-                        }
+                        resFile.delete();
                     }
                 }
             }
@@ -1326,15 +1316,21 @@
             checkManageUsersPermission("Only system can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
-            // Write the restrictions to XML
-            writeApplicationRestrictionsLocked(packageName, restrictions, userId);
+            if (restrictions == null || restrictions.isEmpty()) {
+                cleanAppRestrictionsForPackage(packageName, userId);
+            } else {
+                // Write the restrictions to XML
+                writeApplicationRestrictionsLocked(packageName, restrictions, userId);
+            }
         }
 
-        // Notify package of changes via an intent - only sent to explicitly registered receivers.
-        Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-        changeIntent.setPackage(packageName);
-        changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
+        if (isPackageInstalled(packageName, userId)) {
+            // Notify package of changes via an intent - only sent to explicitly registered receivers.
+            Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+            changeIntent.setPackage(packageName);
+            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
+        }
     }
 
     @Override
@@ -1437,7 +1433,7 @@
             // Remove restrictions pin
             setRestrictionsChallenge(null);
             // Remove any app restrictions
-            cleanAppRestrictions(userHandle, true);
+            cleanAppRestrictions(userHandle);
         }
         if (unblockApps) {
             unblockAllAppsForUser(userHandle);
@@ -1691,12 +1687,6 @@
                 user.lastLoggedInTime = now;
                 writeUserLocked(user);
             }
-            // If this is not a restricted profile and there is no restrictions pin, clean up
-            // all restrictions files that might have been left behind, else clean up just the
-            // ones with uninstalled packages
-            RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
-            final long salt = pinState == null ? 0 : pinState.salt;
-            cleanAppRestrictions(userId, (!user.isRestricted() && salt == 0));
         }
     }
 
@@ -1772,17 +1762,4 @@
             }
         }
     }
-
-    private PackageMonitor mUserPackageMonitor = new PackageMonitor() {
-        @Override
-        public void onPackageRemoved(String pkg, int uid) {
-            final int userId = this.getChangingUserId();
-            // Package could be disappearing because it is being blocked, so also check if
-            // it has been uninstalled.
-            final boolean uninstalled = isPackageDisappearing(pkg) == PACKAGE_PERMANENT_CHANGE;
-            if (uninstalled && userId >= 0 && !isPackageInstalled(pkg, userId)) {
-                cleanAppRestrictionsForPackage(pkg, userId);
-            }
-        }
-    };
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5959d4..fa22ded 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -11173,17 +11173,17 @@
         public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
             synchronized (mWindowMap) {
                 mWaitingForDrawnCallback = callback;
-                final WindowList windows = getDefaultWindowListLocked();
-                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                    final WindowState win = windows.get(winNdx);
-                    if (win.mHasSurface && win.isWinVisibleLw()) {
-                        if (!win.mIsWallpaper) {
-                            // Don't force wallpaper to redraw.
+                for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+                    final WindowList windows =
+                            mDisplayContents.valueAt(displayNdx).getWindowList();
+                    for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                        final WindowState win = windows.get(winNdx);
+                        if (win.mHasSurface && win.isWinVisibleLw() && !win.mIsWallpaper) {
                             win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                            // Force add to mResizingWindows.
+                            win.mLastContentInsets.set(-1, -1, -1, -1);
+                            mWaitingForDrawn.add(win);
                         }
-                        // Force add to mResizingWindows.
-                        win.mLastContentInsets.set(-1, -1, -1, -1);
-                        mWaitingForDrawn.add(win);
                     }
                 }
                 requestTraversalLocked();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 92d3d95..12ed920 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,7 +55,9 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
 import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -136,6 +138,7 @@
     private static final boolean DBG = false;
 
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
+    private static final String ATTR_SETUP_COMPLETE = "setup-complete";
 
     final Context mContext;
     final UserManager mUserManager;
@@ -174,6 +177,7 @@
             }
         }
     }
+
     public static class DevicePolicyData {
         int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         int mActivePasswordLength = 0;
@@ -188,6 +192,7 @@
         int mUserHandle;
         int mPasswordOwner = -1;
         long mLastMaximumTimeToLock = -1;
+        boolean mUserSetupComplete = false;
 
         final HashMap<ComponentName, ActiveAdmin> mAdminMap
                 = new HashMap<ComponentName, ActiveAdmin>();
@@ -252,6 +257,7 @@
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
+        private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
         private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
         private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -319,6 +325,7 @@
 
         boolean encryptionRequested = false;
         boolean disableCamera = false;
+        boolean disableCallerId = false;
         Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
@@ -431,6 +438,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
                 out.endTag(null, TAG_DISABLE_CAMERA);
             }
+            if (disableCallerId) {
+                out.startTag(null, TAG_DISABLE_CALLER_ID);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
+                out.endTag(null, TAG_DISABLE_CALLER_ID);
+            }
             if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
                 out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -513,6 +525,9 @@
                 } else if (TAG_DISABLE_CAMERA.equals(tag)) {
                     disableCamera = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
+                    disableCallerId = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -589,6 +604,8 @@
                     pw.println(encryptionRequested);
             pw.print(prefix); pw.print("disableCamera=");
                     pw.println(disableCamera);
+            pw.print(prefix); pw.print("disableCallerId=");
+                    pw.println(disableCallerId);
             pw.print(prefix); pw.print("disabledKeyguardFeatures=");
                     pw.println(disabledKeyguardFeatures);
         }
@@ -973,6 +990,10 @@
                 out.attribute(null, ATTR_PERMISSION_PROVIDER,
                         policy.mRestrictionsProvider.flattenToString());
             }
+            if (policy.mUserSetupComplete) {
+                out.attribute(null, ATTR_SETUP_COMPLETE,
+                        Boolean.toString(true));
+            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1074,6 +1095,10 @@
             if (permissionProvider != null) {
                 policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider);
             }
+            String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
+            if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
+                policy.mUserSetupComplete = true;
+            }
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -1266,6 +1291,10 @@
                 }
             }
         }
+        // Register an observer for watching for user setup complete.
+        new SetupContentObserver(mHandler).register(mContext.getContentResolver());
+        // Initialize the user setup state, to handle the upgrade case.
+        updateUserSetupComplete();
     }
 
     private void cleanUpOldUsers() {
@@ -3274,6 +3303,25 @@
         return null;
     }
 
+    // Returns the active profile owner for this user or null if the current user has no
+    // profile owner.
+    private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
+        String profileOwnerPackage = getProfileOwner(userHandle);
+        if (profileOwnerPackage == null) {
+            return null;
+        }
+
+        DevicePolicyData policy = getUserData(userHandle);
+        final int n = policy.mAdminList.size();
+        for (int i = 0; i < n; i++) {
+            ActiveAdmin admin = policy.mAdminList.get(i);
+            if (profileOwnerPackage.equals(admin.info.getPackageName())) {
+                return admin;
+            }
+        }
+        return null;
+    }
+
     @Override
     public String getProfileOwnerName(int userHandle) {
         if (!mHasFeature) {
@@ -3312,6 +3360,12 @@
         }
     }
 
+    private void enforceSystemProcess(String message) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException(message);
+        }
+    }
+
     private void enforceNotManagedProfile(int userHandle, String message) {
         if(isManagedProfile(userHandle)) {
             throw new SecurityException("You can not " + message + " for a managed profile. ");
@@ -3871,11 +3925,17 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabled() {
+        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+        enforceCrossUserPermission(userId);
         if (!mHasFeature) {
             return null;
         }
         synchronized (this) {
-            DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+            DevicePolicyData policy = getUserData(userId);
             final int N = policy.mAdminList.size();
             HashSet<String> resultSet = new HashSet<String>();
             for (int i = 0; i < N; i++) {
@@ -3933,6 +3993,50 @@
         return false;
     }
 
+    @Override
+    public void setCrossProfileCallerIdDisabled(ComponentName who, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.disableCallerId != disabled) {
+                admin.disableCallerId = disabled;
+                saveSettingsLocked(UserHandle.getCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileCallerIdDisabled(ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.disableCallerId;
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
+        enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+        synchronized (this) {
+            ActiveAdmin admin = getProfileOwnerAdmin(userId);
+            return (admin != null) ? admin.disableCallerId : false;
+        }
+    }
+
     /**
      * Sets which packages may enter lock task mode.
      *
@@ -4102,4 +4206,50 @@
             return audioManager.isMasterMute();
         }
     }
+
+    /**
+     * We need to update the internal state of whether a user has completed setup once. After
+     * that, we ignore any changes that reset the Settings.Secure.USER_SETUP_COMPLETE changes
+     * as we don't trust any apps that might try to reset it.
+     * <p>
+     * Unfortunately, we don't know which user's setup state was changed, so we write all of
+     * them.
+     */
+    void updateUserSetupComplete() {
+        List<UserInfo> users = mUserManager.getUsers(true);
+        ContentResolver resolver = mContext.getContentResolver();
+        final int N = users.size();
+        for (int i = 0; i < N; i++) {
+            int userHandle = users.get(i).id;
+            if (Settings.Secure.getIntForUser(resolver, Settings.Secure.USER_SETUP_COMPLETE, 0,
+                    userHandle) != 0) {
+                DevicePolicyData policy = getUserData(userHandle);
+                policy.mUserSetupComplete = true;
+                synchronized (this) {
+                    saveSettingsLocked(userHandle);
+                }
+            }
+        }
+    }
+
+    private class SetupContentObserver extends ContentObserver {
+
+        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
+                Settings.Secure.USER_SETUP_COMPLETE);
+
+        public SetupContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        void register(ContentResolver resolver) {
+            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mUserSetupComplete.equals(uri)) {
+                updateUserSetupComplete();
+            }
+        }
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 4a8623d..bed85fc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -94,7 +94,7 @@
     }
 
     public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
-        SQLiteDatabase db = this.getWritableDatabase();
+        SQLiteDatabase db = getWritableDatabase();
         ContentValues values = new ContentValues();
         // Generate a random ID for the model.
         values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
@@ -105,7 +105,7 @@
         if (db.insertWithOnConflict(
                 SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             for (Keyphrase keyphrase : soundModel.keyphrases) {
-                status &= addKeyphrase(soundModel.uuid, keyphrase);
+                status &= addOrUpdateKeyphrase(soundModel.uuid, keyphrase);
             }
             return status;
         } else {
@@ -114,18 +114,16 @@
         }
     }
 
-    /**
-     * TODO(sansid): Change to addOrUpdate to handle changes here.
-     */
-    private boolean addKeyphrase(UUID modelId, Keyphrase keyphrase) {
-        SQLiteDatabase db = this.getWritableDatabase();
+    private boolean addOrUpdateKeyphrase(UUID modelId, Keyphrase keyphrase) {
+        SQLiteDatabase db = getWritableDatabase();
         ContentValues values = new ContentValues();
         values.put(KeyphraseContract.KEY_ID, keyphrase.id);
         values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
-        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
         values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
         values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
-        if (db.insert(KeyphraseContract.TABLE, null, values) != -1) {
+        if (db.insertWithOnConflict(
+                KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             return true;
         } else {
             Slog.w(TAG, "Failed to persist keyphrase to database");
@@ -134,6 +132,26 @@
     }
 
     /**
+     * Deletes the sound model and associated keyphrases.
+     */
+    public boolean deleteKeyphraseSoundModel(UUID uuid) {
+        SQLiteDatabase db = getWritableDatabase();
+        String modelId = uuid.toString();
+        String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+        boolean status = true;
+        if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+            Slog.w(TAG, "No sound models deleted from the database");
+            status = false;
+        }
+        String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+        if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+            Slog.w(TAG, "No keyphrases deleted from the database");
+            status = false;
+        }
+        return status;
+    }
+
+    /**
      * Lists all the keyphrase sound models currently registered with the system.
      */
     public List<KeyphraseSoundModel> getKephraseSoundModels() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7204695..63702ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -281,12 +281,25 @@
                     throw new SecurityException("Caller does not hold the permission "
                             + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
                 }
+                if (model == null) {
+                    throw new IllegalArgumentException("Model must not be null");
+                }
+
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
-                        return STATUS_OK;
+                    // If the keyphrases are not present in the model, delete the model.
+                    if (model.keyphrases == null) {
+                        if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
                     } else {
-                        return STATUS_ERROR;
+                        if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(caller);
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index 9f38d0d..5a41acb 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -62,6 +62,11 @@
      */
     public static final int STATE_DISCONNECTED = 7;
 
+    /**
+     * The state of an outgoing {@code Call}, but waiting for user input before proceeding.
+     */
+    public static final int STATE_PRE_DIAL_WAIT = 8;
+
     public static class Details {
         private final Uri mHandle;
         private final int mHandlePresentation;
@@ -430,6 +435,15 @@
     }
 
     /**
+     * Notifies this {@code Call} the a {@code PhoneAccount} has been selected and to proceed
+     * with placing an outgoing call.
+     */
+    public void phoneAccountSelected(PhoneAccount account) {
+        mInCallAdapter.phoneAccountSelected(mTelecommCallId, account);
+
+    }
+
+    /**
      * Instructs this {@code Call} to enter a conference.
      */
     public void conference() {
@@ -703,6 +717,8 @@
         switch (inCallCallState) {
             case NEW:
                 return STATE_NEW;
+            case PRE_DIAL_WAIT:
+                return STATE_PRE_DIAL_WAIT;
             case DIALING:
                 return STATE_DIALING;
             case RINGING:
diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java
index a464da5..7abf4ab 100644
--- a/telecomm/java/android/telecomm/CallState.java
+++ b/telecomm/java/android/telecomm/CallState.java
@@ -32,6 +32,14 @@
     NEW,
 
     /**
+     * Indicates that the call is about to go into the outgoing and dialing state but is waiting for
+     * user input before it proceeds. For example, where no default {@link PhoneAccount} is set,
+     * this is the state where the InCallUI is waiting for the user to select a {@link PhoneAccount}
+     * to call from.
+     */
+    PRE_DIAL_WAIT,
+
+    /**
      * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
      * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
      * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index 66cf1df..f228046 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -200,6 +200,19 @@
     }
 
     /**
+     * Instructs Telecomm to add a PhoneAccount to the specified call
+     *
+     * @param callId The identifier of the call
+     * @param account The PhoneAccount through which to place the call
+     */
+    public void phoneAccountSelected(String callId, PhoneAccount account) {
+        try {
+            mAdapter.phoneAccountSelected(callId, account);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Instructs Telecomm to conference the specified call.
      *
      * @param callId The unique ID of the call.
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index ce0309f..b5b239b 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecomm;
 
 import android.telecomm.CallAudioState;
+import android.telecomm.PhoneAccount;
 
 /**
  * Internal remote callback interface for in-call services.
@@ -48,6 +49,8 @@
 
     void phoneAccountClicked(String callId);
 
+    void phoneAccountSelected(String callId, in PhoneAccount account);
+
     void conference(String callId);
 
     void splitFromConference(String callId);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5e6cb14..6e1ee78 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -24,7 +24,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.telecomm.PhoneAccount;
 import android.util.Log;
 
 import com.android.internal.telecomm.ITelecommService;
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 56fa0a9..a16b749 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -22,7 +22,8 @@
 
     <application
         android:hardwareAccelerated="true"
-        android:label="vector" >
+        android:label="vector"
+        android:supportsRtl="true" >
         <activity
             android:name="VectorDrawablePerformance"
             android:label="Vector Performance" >
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 5b4c4ab..2fdb676 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true" >
 
     <size
         android:height="64dp"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 90694fb..296e0261 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -12,7 +12,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
 
     <size
             android:width="64dp"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index c6595fa..1633326 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
 
     <size
         android:height="64dp"