Merge "Revert more integrity check changes."
diff --git a/api/current.txt b/api/current.txt
index 8f7e305..6bec747 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5970,6 +5970,8 @@
     method public void onReceive(android.content.Context, android.content.Intent);
     method public void onSecurityLogsAvailable(android.content.Context, android.content.Intent);
     method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
+    method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -35094,6 +35096,8 @@
     method public final void requestUnbind();
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void snoozeNotification(java.lang.String, long);
+    method public final void snoozeNotification(java.lang.String);
+    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 6a57e48..1f3fd7d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6142,6 +6142,8 @@
     method public void onReceive(android.content.Context, android.content.Intent);
     method public void onSecurityLogsAvailable(android.content.Context, android.content.Intent);
     method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
+    method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -37893,7 +37895,9 @@
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public final void snoozeNotification(java.lang.String, long);
+    method public final void snoozeNotification(java.lang.String);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
+    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index 004350d..4a4bf70 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5395,6 +5395,7 @@
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
+    method public android.content.ComponentName getEffectsSuppressor();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
@@ -5986,6 +5987,8 @@
     method public void onReceive(android.content.Context, android.content.Intent);
     method public void onSecurityLogsAvailable(android.content.Context, android.content.Intent);
     method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
+    method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -35187,6 +35190,8 @@
     method public final void requestUnbind();
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void snoozeNotification(java.lang.String, long);
+    method public final void snoozeNotification(java.lang.String);
+    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 15b99c6..2a7341a 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -77,7 +77,10 @@
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
     void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
 
-    void snoozeNotificationFromListener(in INotificationListener token, String key, long until);
+
+    void snoozeNotificationUntilFromListener(in INotificationListener token, String key, long until);
+    void snoozeNotificationFromListener(in INotificationListener token, String key);
+    void unsnoozeNotificationFromListener(in INotificationListener token, String key);
 
     void requestBindListener(in ComponentName component);
     void requestUnbindListener(in INotificationListener token);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 2234a38..047f349 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.TestApi;
 import android.app.Notification.Builder;
 import android.content.ComponentName;
 import android.content.Context;
@@ -428,6 +429,7 @@
     /**
      * @hide
      */
+    @TestApi
     public ComponentName getEffectsSuppressor() {
         INotificationManager service = getService();
         try {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index d89d2bb..b1eca5c 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.security.KeyChain;
 
 import java.lang.annotation.Retention;
@@ -306,6 +307,24 @@
             "android.app.extra.EXTRA_NETWORK_LOGS_COUNT";
 
     /**
+     * Broadcast action: notify the device owner that a user or profile has been added.
+     * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
+     * the new user.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USER_ADDED  = "android.app.action.USER_ADDED";
+
+    /**
+     * Broadcast action: notify the device owner that a user or profile has been removed.
+     * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
+     * the new user.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USER_REMOVED = "android.app.action.USER_REMOVED";
+
+    /**
      * A string containing the SHA-256 hash of the bugreport file.
      *
      * @see #ACTION_BUGREPORT_SHARE
@@ -690,6 +709,30 @@
             int networkLogsCount) {
     }
 
+     /**
+      * Called when a user or profile is created.
+      *
+      * <p>This callback is only applicable to device owners.
+      *
+      * @param context The running context as per {@link #onReceive}.
+      * @param intent The received intent as per {@link #onReceive}.
+      * @param newUser The {@link UserHandle} of the user that has just been added.
+      */
+     public void onUserAdded(Context context, Intent intent, UserHandle newUser) {
+     }
+
+     /**
+      * Called when a user or profile is removed.
+      *
+      * <p>This callback is only applicable to device owners.
+      *
+      * @param context The running context as per {@link #onReceive}.
+      * @param intent The received intent as per {@link #onReceive}.
+      * @param removedUser The {@link UserHandle} of the user that has just been removed.
+      */
+     public void onUserRemoved(Context context, Intent intent, UserHandle removedUser) {
+     }
+
     /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
@@ -748,6 +791,10 @@
             long batchToken = intent.getLongExtra(EXTRA_NETWORK_LOGS_TOKEN, -1);
             int networkLogsCount = intent.getIntExtra(EXTRA_NETWORK_LOGS_COUNT, 0);
             onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount);
+        } else if (ACTION_USER_ADDED.equals(action)) {
+            onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_USER_REMOVED.equals(action)) {
+            onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
         }
     }
 }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 45011eb..02ab30a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -534,7 +534,46 @@
     public final void snoozeNotification(String key, long snoozeUntil) {
         if (!isBound()) return;
         try {
-            getNotificationInterface().snoozeNotificationFromListener(mWrapper, key, snoozeUntil);
+            getNotificationInterface().snoozeNotificationUntilFromListener(
+                    mWrapper, key, snoozeUntil);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
+     * Inform the notification manager about snoozing a specific notification.
+     * <p>
+     * Use this to snooze a notification for an indeterminate time.  Upon being informed, the
+     * notification manager will actually remove the notification and you will get an
+     * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
+     * snoozing period expires, you will get a
+     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
+     * notification. Use {@link #unsnoozeNotification(String)} to restore the notification.
+     * @param key The key of the notification to snooze
+     */
+    public final void snoozeNotification(String key) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().snoozeNotificationFromListener(mWrapper, key);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
+     * Inform the notification manager about un-snoozing a specific notification.
+     * <p>
+     * This should only be used for notifications snoozed by this listener using
+     * {@link #snoozeNotification(String)}. Once un-snoozed, you will get a
+     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
+     * notification.
+     * @param key The key of the notification to snooze
+     */
+    public final void unsnoozeNotification(String key) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().unsnoozeNotificationFromListener(mWrapper, key);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 19e511cf..20193f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1485,7 +1485,7 @@
      */
     @Override  // NotificationData.Environment
     public boolean shouldHideNotifications(int userId) {
-        return isLockscreenPublicMode(mCurrentUserId) && !userAllowsNotificationsInPublic(userId)
+        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
                 || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 604b3ed..84c298b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1837,7 +1837,7 @@
          * @param token The binder for the listener, to check that the caller is allowed
          */
         @Override
-        public void snoozeNotificationFromListener(INotificationListener token, String key,
+        public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
                 long snoozeUntil) {
             long identity = Binder.clearCallingIdentity();
             try {
@@ -1849,6 +1849,38 @@
         }
 
         /**
+         * Allow an INotificationListener to snooze a single notification.
+         *
+         * @param token The binder for the listener, to check that the caller is allowed
+         */
+        @Override
+        public void snoozeNotificationFromListener(INotificationListener token, String key) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                snoozeNotificationInt(key, info);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        /**
+         * Allow an INotificationListener to un-snooze a single notification.
+         *
+         * @param token The binder for the listener, to check that the caller is allowed
+         */
+        @Override
+        public void unsnoozeNotificationFromListener(INotificationListener token, String key) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                unsnoozeNotificationInt(key, info);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        /**
          * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
          *
          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
@@ -2206,7 +2238,6 @@
 
         @Override
         public ComponentName getEffectsSuppressor() {
-            enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
             return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
         }
 
@@ -3686,6 +3717,34 @@
         }
     }
 
+    void snoozeNotificationInt(String key, ManagedServiceInfo listener) {
+        String listenerName = listener == null ? null : listener.component.toShortString();
+        // TODO: write to event log
+        if (DBG) {
+            Slog.d(TAG, String.format("snooze event(%s, %s)", key, listenerName));
+        }
+        synchronized (mNotificationList) {
+            final NotificationRecord r = mNotificationsByKey.get(key);
+            if (r != null) {
+                mNotificationList.remove(r);
+                cancelNotificationLocked(r, false, REASON_SNOOZED);
+                updateLightsLocked();
+                mSnoozeHelper.snooze(r, r.getUser().getIdentifier());
+                savePolicyFile();
+            }
+        }
+    }
+
+    void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
+        String listenerName = listener == null ? null : listener.component.toShortString();
+        // TODO: write to event log
+        if (DBG) {
+            Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
+        }
+        mSnoozeHelper.repost(key, Binder.getCallingUid());
+                savePolicyFile();
+    }
+
     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
             ManagedServiceInfo listener, boolean includeCurrentProfiles) {
         String listenerName = listener == null ? null : listener.component.toShortString();
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 738403e..409eef4 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -62,6 +63,8 @@
     // User id : package name : notification key : record.
     private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
             mSnoozedNotifications = new ArrayMap<>();
+    // notification key : package.
+    private ArrayMap<String, String> mPackages = new ArrayMap<>();
     private Callback mCallback;
 
     public SnoozeHelper(Context context, Callback callback,
@@ -82,10 +85,20 @@
     }
 
     /**
-     * Records a notification that should be snoozed until the given time and schedules an alarm
-     * to repost at that time.
+     * Snoozes a notification and schedules an alarm to repost at that time.
      */
     protected void snooze(NotificationRecord record, int userId, long until) {
+        snooze(record, userId);
+        scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
+    }
+
+    /**
+     * Records a snoozed notification.
+     */
+    protected void snooze(NotificationRecord record, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "Snoozing " + record.getKey());
+        }
         ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
                 mSnoozedNotifications.get(userId);
         if (records == null) {
@@ -98,13 +111,9 @@
         pkgRecords.put(record.getKey(), record);
         records.put(record.sbn.getPackageName(), pkgRecords);
         mSnoozedNotifications.put(userId, records);
-        if (DEBUG) {
-            Slog.d(TAG, "Snoozing " + record.getKey() + " until " + new Date(until));
-        }
-        scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
+        mPackages.put(record.getKey(), record.sbn.getPackageName());
     }
 
-
     protected boolean cancel(int userId, String pkg, String tag, int id) {
         if (mSnoozedNotifications.containsKey(userId)) {
             ArrayMap<String, NotificationRecord> recordsForPkg =
@@ -121,6 +130,7 @@
                 if (key != null) {
                     recordsForPkg.remove(key);
                     cancelAlarm(userId, pkg, key);
+                    mPackages.remove(key);
                     return true;
                 }
             }
@@ -145,6 +155,7 @@
                         int P = records.size();
                         for (int k = 0; k < P; k++) {
                             cancelAlarm(userId, snoozedPkgs.keyAt(j), records.keyAt(k));
+                            mPackages.remove(records.keyAt(k));
                         }
                     }
                 }
@@ -162,6 +173,7 @@
                 int N = records.size();
                 for (int i = 0; i < N; i++) {
                     cancelAlarm(userId, pkg, records.keyAt(i));
+                    mPackages.remove(records.keyAt(i));
                 }
                 return true;
             }
@@ -190,8 +202,8 @@
         pkgRecords.put(record.getKey(), record);
     }
 
-    @VisibleForTesting
-    void repost(String pkg, String key, int userId) {
+    protected void repost(String key, int userId) {
+        final String pkg = mPackages.remove(key);
         ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
                 mSnoozedNotifications.get(userId);
         if (records == null) {
@@ -202,6 +214,7 @@
             return;
         }
         final NotificationRecord record = pkgRecords.remove(key);
+
         if (record != null) {
             mCallback.repost(userId, record);
         }
@@ -213,7 +226,6 @@
                 new Intent(REPOST_ACTION)
                         .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                        .putExtra(EXTRA_PKG, pkg)
                         .putExtra(EXTRA_KEY, key)
                         .putExtra(EXTRA_USER_ID, userId),
                 PendingIntent.FLAG_UPDATE_CURRENT);
@@ -273,8 +285,8 @@
                 Slog.d(TAG, "Reposting notification");
             }
             if (REPOST_ACTION.equals(intent.getAction())) {
-                repost(intent.getStringExtra(EXTRA_PKG), intent.getStringExtra(EXTRA_KEY),
-                        intent.getIntExtra(EXTRA_USER_ID, 0));
+                repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
+                        UserHandle.USER_SYSTEM));
             }
         }
     };
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 1ef4a9f..acdcc72 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -219,19 +219,20 @@
 
                 final String dexoptType;
                 String oatDir = null;
-                switch (dexoptNeeded) {
+                boolean isOdexLocation = (dexoptNeeded < 0);
+                switch (Math.abs(dexoptNeeded)) {
                     case DexFile.NO_DEXOPT_NEEDED:
                         continue;
-                    case DexFile.DEX2OAT_NEEDED:
+                    case DexFile.DEX2OAT_FROM_SCRATCH:
+                    case DexFile.DEX2OAT_FOR_BOOT_IMAGE:
+                    case DexFile.DEX2OAT_FOR_FILTER:
+                    case DexFile.DEX2OAT_FOR_RELOCATION:
                         dexoptType = "dex2oat";
                         oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                         break;
-                    case DexFile.PATCHOAT_NEEDED:
+                    case DexFile.PATCHOAT_FOR_RELOCATION:
                         dexoptType = "patchoat";
                         break;
-                    case DexFile.SELF_PATCHOAT_NEEDED:
-                        dexoptType = "self patchoat";
-                        break;
                     default:
                         throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
                 }
@@ -383,7 +384,7 @@
         protected int adjustDexoptNeeded(int dexoptNeeded) {
             // Ensure compilation, no matter the current state.
             // TODO: The return value is wrong when patchoat is needed.
-            return DexFile.DEX2OAT_NEEDED;
+            return DexFile.DEX2OAT_FROM_SCRATCH;
         }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c32ee3a..895497c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -544,8 +544,10 @@
                 new MonitoringCertNotificationTask().execute(userId);
             }
             if (Intent.ACTION_USER_ADDED.equals(action)) {
+                sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
                 disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle);
                 disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
                 removeUserData(userHandle);
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -568,6 +570,17 @@
                 clearWipeProfileNotification();
             }
         }
+
+        private void sendUserAddedOrRemovedCommand(String action, int userHandle) {
+            synchronized (DevicePolicyManagerService.this) {
+                ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+                if (deviceOwner != null) {
+                    Bundle extras = new Bundle();
+                    extras.putParcelable(Intent.EXTRA_USER, UserHandle.of(userHandle));
+                    sendAdminCommandLocked(deviceOwner, action, extras, null);
+                }
+            }
+        }
     };
 
     static class ActiveAdmin {
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index 7a3ee7f..4f6a35c 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -37,6 +37,7 @@
 import static junit.framework.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -65,7 +66,7 @@
     }
 
     @Test
-    public void testSnooze() throws Exception {
+    public void testSnoozeForTime() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
         verify(mAm, times(1)).setExactAndAllowWhileIdle(
@@ -75,6 +76,16 @@
     }
 
     @Test
+    public void testSnooze() throws Exception {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM);
+        verify(mAm, never()).setExactAndAllowWhileIdle(
+                anyInt(), anyLong(), any(PendingIntent.class));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+    }
+
+    @Test
     public void testCancelByApp() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -152,7 +163,7 @@
         mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
         mSnoozeHelper.snooze(r2 , UserHandle.USER_ALL, 1000);
-        mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
+        mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
         verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
     }
 
@@ -165,7 +176,7 @@
         mSnoozeHelper.update(UserHandle.USER_SYSTEM, r);
         verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class));
 
-        mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
+        mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
         verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java
new file mode 100644
index 0000000..df85806
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.GcSnapshot;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
+import com.android.ninepatch.NinePatchChunk;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.text.TextUtils;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+
+public class BaseCanvas_Delegate {
+    // ---- delegate manager ----
+    protected static DelegateManager<BaseCanvas_Delegate> sManager =
+            new DelegateManager<>(BaseCanvas_Delegate.class);
+
+    // ---- delegate helper data ----
+    private final static boolean[] sBoolOut = new boolean[1];
+
+
+    // ---- delegate data ----
+    protected Bitmap_Delegate mBitmap;
+    protected GcSnapshot mSnapshot;
+
+    // ---- Public Helper methods ----
+
+    protected BaseCanvas_Delegate(Bitmap_Delegate bitmap) {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+    }
+
+    protected BaseCanvas_Delegate() {
+        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
+    }
+
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    protected void dispose() {
+        mSnapshot.dispose();
+    }
+
+    /**
+     * Returns the current {@link Graphics2D} used to draw.
+     */
+    public GcSnapshot getSnapshot() {
+        return mSnapshot;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
+            long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+        float right = left + image.getWidth();
+        float bottom = top + image.getHeight();
+
+        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+                0, 0, image.getWidth(), image.getHeight(),
+                (int)left, (int)top, (int)right, (int)bottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+            float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, (int) srcLeft, (int) srcTop,
+                (int) srcRight, (int) srcBottom, (int) dstLeft, (int) dstTop, (int) dstRight,
+                (int) dstBottom);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
+            final float x, final float y, int width, int height, boolean hasAlpha,
+            long nativePaintOrZero) {
+        // create a temp BufferedImage containing the content.
+        final BufferedImage image = new BufferedImage(width, height,
+                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
+        image.setRGB(0, 0, width, height, colors, offset, stride);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paint) -> {
+                    if (paint != null && paint.isFilterBitmap()) {
+                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                    }
+
+                    graphics.drawImage(image, (int) x, (int) y, null);
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawColor(long nativeCanvas, final int color, final int mode) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        final int w = canvasDelegate.mBitmap.getImage().getWidth();
+        final int h = canvasDelegate.mBitmap.getImage().getHeight();
+        draw(nativeCanvas, (graphics, paint) -> {
+            // reset its transform just in case
+            graphics.setTransform(new AffineTransform());
+
+            // set the color
+            graphics.setColor(new java.awt.Color(color, true /*alpha*/));
+
+            Composite composite = PorterDuffUtility.getComposite(
+                    PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
+            if (composite != null) {
+                graphics.setComposite(composite);
+            }
+
+            graphics.fillRect(0, 0, w, h);
+        });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPaint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPoint(long nativeCanvas, float x, float y,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawPoint is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawLine(long nativeCanvas,
+            final float startX, final float startY, final float stopX, final float stopY,
+            long paint) {
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawLines(long nativeCanvas,
+            final float[] pts, final int offset, final int count,
+            long nativePaint) {
+        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+                false /*forceSrcMode*/, (graphics, paintDelegate) -> {
+                    for (int i = 0; i < count; i += 4) {
+                        graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
+                                (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRect(long nativeCanvas,
+            final float left, final float top, final float right, final float bottom, long paint) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    int style = paintDelegate.getStyle();
+
+                    // draw
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.fillRect((int)left, (int)top,
+                                (int)(right-left), (int)(bottom-top));
+                    }
+
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.drawRect((int)left, (int)top,
+                                (int)(right-left), (int)(bottom-top));
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawOval(long nativeCanvas, final float left,
+            final float top, final float right, final float bottom, long paint) {
+        if (right > left && bottom > top) {
+            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                    (graphics, paintDelegate) -> {
+                        int style = paintDelegate.getStyle();
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fillOval((int)left, (int)top,
+                                    (int)(right - left), (int)(bottom - top));
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.drawOval((int)left, (int)top,
+                                    (int)(right - left), (int)(bottom - top));
+                        }
+                    });
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawCircle(long nativeCanvas,
+            float cx, float cy, float radius, long paint) {
+        nDrawOval(nativeCanvas,
+                cx - radius, cy - radius, cx + radius, cy + radius,
+                paint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawArc(long nativeCanvas,
+            final float left, final float top, final float right, final float bottom,
+            final float startAngle, final float sweep,
+            final boolean useCenter, long paint) {
+        if (right > left && bottom > top) {
+            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                    (graphics, paintDelegate) -> {
+                        int style = paintDelegate.getStyle();
+
+                        Arc2D.Float arc = new Arc2D.Float(
+                                left, top, right - left, bottom - top,
+                                -startAngle, -sweep,
+                                useCenter ? Arc2D.PIE : Arc2D.OPEN);
+
+                        // draw
+                        if (style == Paint.Style.FILL.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.fill(arc);
+                        }
+
+                        if (style == Paint.Style.STROKE.nativeInt ||
+                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                            graphics.draw(arc);
+                        }
+                    });
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRoundRect(long nativeCanvas,
+            final float left, final float top, final float right, final float bottom,
+            final float rx, final float ry, long paint) {
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    int style = paintDelegate.getStyle();
+
+                    // draw
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.fillRoundRect(
+                                (int)left, (int)top,
+                                (int)(right - left), (int)(bottom - top),
+                                2 * (int)rx, 2 * (int)ry);
+                    }
+
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.drawRoundRect(
+                                (int)left, (int)top,
+                                (int)(right - left), (int)(bottom - top),
+                                2 * (int)rx, 2 * (int)ry);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    public static void nDrawPath(long nativeCanvas, long path, long paint) {
+        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
+        if (pathDelegate == null) {
+            return;
+        }
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    Shape shape = pathDelegate.getJavaShape();
+                    Rectangle2D bounds = shape.getBounds2D();
+                    if (bounds.isEmpty()) {
+                        // Apple JRE 1.6 doesn't like drawing empty shapes.
+                        // http://b.android.com/178278
+
+                        if (pathDelegate.isEmpty()) {
+                            // This means that the path doesn't have any lines or curves so
+                            // nothing to draw.
+                            return;
+                        }
+
+                        // The stroke width is not consider for the size of the bounds so,
+                        // for example, a horizontal line, would be considered as an empty
+                        // rectangle.
+                        // If the strokeWidth is not 0, we use it to consider the size of the
+                        // path as well.
+                        float strokeWidth = paintDelegate.getStrokeWidth();
+                        if (strokeWidth <= 0.0f) {
+                            return;
+                        }
+                        bounds.setRect(bounds.getX(), bounds.getY(),
+                                Math.max(strokeWidth, bounds.getWidth()),
+                                Math.max(strokeWidth, bounds.getHeight()));
+                    }
+
+                    int style = paintDelegate.getStyle();
+
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.fill(shape);
+                    }
+
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.draw(shape);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawRegion(long nativeCanvas, long nativeRegion,
+            long nativePaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Some canvas paths may not be drawn", null, null);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
+            final float dstLeft, final float dstTop, final float dstRight, final float dstBottom,
+            long nativePaintOrZero, final int screenDensity, final int bitmapDensity) {
+
+        // get the delegate from the native int.
+        final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        byte[] c = NinePatch_Delegate.getChunk(ninePatch);
+        if (c == null) {
+            // not a 9-patch?
+            BufferedImage image = bitmapDelegate.getImage();
+            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
+                    image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
+                    (int) dstBottom);
+            return;
+        }
+
+        final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
+        assert chunkObject != null;
+        if (chunkObject == null) {
+            return;
+        }
+
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // this one can be null
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
+            @Override
+            public void draw(Graphics2D graphics, Paint_Delegate paint) {
+                chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
+                        (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
+                        bitmapDensity);
+            }
+        }, paintDelegate, true, false);
+
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
+            long nMatrix, long nPaint) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the delegate from the native int, which can be null
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            return;
+        }
+
+        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
+
+        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
+        if (matrixDelegate == null) {
+            return;
+        }
+
+        final AffineTransform mtx = matrixDelegate.getAffineTransform();
+
+        canvasDelegate.getSnapshot().draw((graphics, paint) -> {
+            if (paint != null && paint.isFilterBitmap()) {
+                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            }
+
+            //FIXME add support for canvas, screen and bitmap densities.
+            graphics.drawImage(image, mtx, null);
+        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawBitmapMesh(long nCanvas, Bitmap bitmap,
+            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
+            int colorOffset, long nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawVertices(long nCanvas, int mode, int n,
+            float[] verts, int vertOffset,
+            float[] texs, int texOffset,
+            int[] colors, int colorOffset,
+            short[] indices, int indexOffset,
+            int indexCount, long nPaint) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawVertices is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawText(long nativeCanvas, char[] text, int index, int count,
+            float startX, float startY, int flags, long paint, long typeface) {
+        drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0,
+                paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawText(long nativeCanvas, String text,
+            int start, int end, float x, float y, final int flags, long paint,
+            long typeface) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextRun(long nativeCanvas, String text,
+            int start, int end, int contextStart, int contextEnd,
+            float x, float y, boolean isRtl, long paint, long typeface) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
+            int start, int count, int contextStart, int contextCount,
+            float x, float y, boolean isRtl, long paint, long typeface) {
+        drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextOnPath(long nativeCanvas,
+            char[] text, int index,
+            int count, long path,
+            float hOffset,
+            float vOffset, int bidiFlags,
+            long paint, long typeface) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawTextOnPath(long nativeCanvas,
+            String text, long path,
+            float hOffset,
+            float vOffset,
+            int bidiFlags, long paint,
+            long typeface) {
+        // FIXME
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
+     * <p>Note that the drawable may actually be executed several times if there are
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
+     */
+    private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
+            GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint which can be null if nPaint is 0;
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
+
+        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
+    }
+
+    /**
+     * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
+     * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
+     * <p>Note that the drawable may actually be executed several times if there are
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
+     */
+    private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        canvasDelegate.mSnapshot.draw(drawable);
+    }
+
+    private static void drawText(long nativeCanvas, final char[] text, final int index,
+            final int count, final float startX, final float startY, final boolean isRtl,
+            long paint, final long typeface) {
+
+        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+                    // Any change to this method should be reflected in Paint.measureText
+
+                    // assert that the typeface passed is actually the one stored in paint.
+                    assert (typeface == paintDelegate.mNativeTypeface);
+
+                    // Paint.TextAlign indicates how the text is positioned relative to X.
+                    // LEFT is the default and there's nothing to do.
+                    float x = startX;
+                    int limit = index + count;
+                    if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+                        RectF bounds =
+                                paintDelegate.measureText(text, index, count, null, 0, isRtl);
+                        float m = bounds.right - bounds.left;
+                        if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                            x -= m / 2;
+                        } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                            x -= m;
+                        }
+                    }
+
+                    new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
+                            startY).renderText(index, limit, isRtl, null, 0, true);
+                });
+    }
+
+    private static void drawBitmap(long nativeCanvas, Bitmap_Delegate bitmap,
+            long nativePaintOrZero, final int sleft, final int stop, final int sright,
+            final int sbottom, final int dleft, final int dtop, final int dright,
+            final int dbottom) {
+        // get the delegate from the native int.
+        BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            return;
+        }
+
+        // get the paint, which could be null if the int is 0
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+
+        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
+
+        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
+                (graphics, paint) -> {
+                    if (paint != null && paint.isFilterBitmap()) {
+                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+                    }
+
+                    //FIXME add support for canvas, screen and bitmap densities.
+                    graphics.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright,
+                            sbottom, null);
+                });
+    }
+
+    /**
+     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
+     * The image returns, through a 1-size boolean array, whether the drawing code should
+     * use a SRC composite no matter what the paint says.
+     *
+     * @param bitmap the bitmap
+     * @param paint the paint that will be used to draw
+     * @param forceSrcMode whether the composite will have to be SRC
+     * @return the image to draw
+     */
+    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
+            boolean[] forceSrcMode) {
+        BufferedImage image = bitmap.getImage();
+        forceSrcMode[0] = false;
+
+        // if the bitmap config is alpha_8, then we erase all color value from it
+        // before drawing it.
+        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
+            fixAlpha8Bitmap(image);
+        } else if (!bitmap.hasAlpha()) {
+            // hasAlpha is merely a rendering hint. There can in fact be alpha values
+            // in the bitmap but it should be ignored at drawing time.
+            // There is two ways to do this:
+            // - override the composite to be SRC. This can only be used if the composite
+            //   was going to be SRC or SRC_OVER in the first place
+            // - Create a different bitmap to draw in which all the alpha channel values is set
+            //   to 0xFF.
+            if (paint != null) {
+                PorterDuff.Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
+
+                forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || mode == PorterDuff.Mode.SRC;
+            }
+
+            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
+            if (!forceSrcMode[0]) {
+                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
+            }
+        }
+
+        return image;
+    }
+
+    private static void fixAlpha8Bitmap(final BufferedImage image) {
+        int w = image.getWidth();
+        int h = image.getHeight();
+        int[] argb = new int[w * h];
+        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+        final int length = argb.length;
+        for (int i = 0 ; i < length; i++) {
+            argb[i] &= 0xFF000000;
+        }
+        image.setRGB(0, 0, w, h, argb, 0, w);
+    }
+
+    protected int save(int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        mSnapshot = mSnapshot.save(saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    protected int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
+        Paint_Delegate paint = new Paint_Delegate();
+        paint.setAlpha(alpha);
+        return saveLayer(rect, paint, saveFlags);
+    }
+
+    protected int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
+        // get the current save count
+        int count = mSnapshot.size();
+
+        mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
+
+        // return the old save count
+        return count;
+    }
+
+    /**
+     * Restores the {@link GcSnapshot} to <var>saveCount</var>
+     * @param saveCount the saveCount
+     */
+    protected void restoreTo(int saveCount) {
+        mSnapshot = mSnapshot.restoreTo(saveCount);
+    }
+
+    /**
+     * Restores the top {@link GcSnapshot}
+     */
+    protected void restore() {
+        mSnapshot = mSnapshot.restore();
+    }
+
+    protected boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
+        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 94152cd..43a0ff5 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -55,40 +55,27 @@
  * @see DelegateManager
  *
  */
-public final class Canvas_Delegate {
+public final class Canvas_Delegate extends BaseCanvas_Delegate {
 
     // ---- delegate manager ----
-    private static final DelegateManager<Canvas_Delegate> sManager =
-            new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
     private static long sFinalizer = -1;
 
-
-    // ---- delegate helper data ----
-
-    private final static boolean[] sBoolOut = new boolean[1];
-
-
-    // ---- delegate data ----
-    private Bitmap_Delegate mBitmap;
-    private GcSnapshot mSnapshot;
-
     private DrawFilter_Delegate mDrawFilter = null;
 
-
     // ---- Public Helper methods ----
 
     /**
      * Returns the native delegate associated to a given {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(Canvas canvas) {
-        return sManager.getDelegate(canvas.getNativeCanvasWrapper());
+        return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
     }
 
     /**
      * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(long native_canvas) {
-        return sManager.getDelegate(native_canvas);
+        return (Canvas_Delegate) sManager.getDelegate(native_canvas);
     }
 
     /**
@@ -143,7 +130,7 @@
 
     @LayoutlibDelegate
     public static void nSetBitmap(long canvas, Bitmap bitmap) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
         Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
         if (canvasDelegate == null || bitmapDelegate==null) {
             return;
@@ -155,7 +142,7 @@
     @LayoutlibDelegate
     public static boolean nIsOpaque(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -169,7 +156,7 @@
     @LayoutlibDelegate
     public static int nGetWidth(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -180,7 +167,7 @@
     @LayoutlibDelegate
     public static int nGetHeight(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -191,7 +178,7 @@
     @LayoutlibDelegate
     public static int nSave(long nativeCanvas, int saveFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -204,7 +191,7 @@
                                                float t, float r, float b,
                                                long paint, int layerFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -223,7 +210,7 @@
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -235,7 +222,7 @@
     public static void nRestore(long nativeCanvas, boolean throwOnUnderflow) {
         // FIXME: implement throwOnUnderflow.
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -248,7 +235,7 @@
             boolean throwOnUnderflow) {
         // FIXME: implement throwOnUnderflow.
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -259,7 +246,7 @@
     @LayoutlibDelegate
     public static int nGetSaveCount(long nativeCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return 0;
         }
@@ -270,7 +257,7 @@
     @LayoutlibDelegate
    public static void nTranslate(long nativeCanvas, float dx, float dy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -281,7 +268,7 @@
     @LayoutlibDelegate
        public static void nScale(long nativeCanvas, float sx, float sy) {
             // get the delegate from the native int.
-            Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
             if (canvasDelegate == null) {
                 return;
             }
@@ -292,7 +279,7 @@
     @LayoutlibDelegate
     public static void nRotate(long nativeCanvas, float degrees) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -303,7 +290,7 @@
     @LayoutlibDelegate
    public static void nSkew(long nativeCanvas, float kx, float ky) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -327,7 +314,7 @@
     @LayoutlibDelegate
     public static void nConcat(long nCanvas, long nMatrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -355,7 +342,7 @@
     @LayoutlibDelegate
     public static void nSetMatrix(long nCanvas, long nMatrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -388,7 +375,7 @@
                                                   float right, float bottom,
                                                   int regionOp) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -400,7 +387,7 @@
     public static boolean nClipPath(long nativeCanvas,
                                                   long nativePath,
                                                   int regionOp) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return true;
         }
@@ -417,7 +404,7 @@
     public static boolean nClipRegion(long nativeCanvas,
                                                     long nativeRegion,
                                                     int regionOp) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return true;
         }
@@ -432,7 +419,7 @@
 
     @LayoutlibDelegate
     public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -449,7 +436,7 @@
     public static boolean nGetClipBounds(long nativeCanvas,
                                                        Rect bounds) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
             return false;
         }
@@ -469,7 +456,7 @@
     @LayoutlibDelegate
     public static void nGetCTM(long canvas, long matrix) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
         if (canvasDelegate == null) {
             return;
         }
@@ -498,509 +485,11 @@
     }
 
     @LayoutlibDelegate
-    public static void nDrawColor(long nativeCanvas, final int color, final int mode) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        final int w = canvasDelegate.mBitmap.getImage().getWidth();
-        final int h = canvasDelegate.mBitmap.getImage().getHeight();
-        draw(nativeCanvas, new GcSnapshot.Drawable() {
-
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                // reset its transform just in case
-                graphics.setTransform(new AffineTransform());
-
-                // set the color
-                graphics.setColor(new Color(color, true /*alpha*/));
-
-                Composite composite = PorterDuffUtility.getComposite(
-                        PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
-                if (composite != null) {
-                    graphics.setComposite(composite);
-                }
-
-                graphics.fillRect(0, 0, w, h);
-            }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPaint(long nativeCanvas, long paint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPaint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPoint(long nativeCanvas, float x, float y,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawPoint is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawLine(long nativeCanvas,
-            final float startX, final float startY, final float stopX, final float stopY,
-            long paint) {
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawLines(long nativeCanvas,
-            final float[] pts, final int offset, final int count,
-            long nativePaint) {
-        draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
-                false /*forceSrcMode*/, new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        for (int i = 0; i < count; i += 4) {
-                            graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
-                                    (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
-                        }
-                    }
-                });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRect(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom, long paint) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        int style = paintDelegate.getStyle();
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fillRect((int)left, (int)top,
-                                    (int)(right-left), (int)(bottom-top));
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.drawRect((int)left, (int)top,
-                                    (int)(right-left), (int)(bottom-top));
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawOval(long nativeCanvas, final float left,
-            final float top, final float right, final float bottom, long paint) {
-        if (right > left && bottom > top) {
-            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                    new GcSnapshot.Drawable() {
-                        @Override
-                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                            int style = paintDelegate.getStyle();
-
-                            // draw
-                            if (style == Paint.Style.FILL.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.fillOval((int)left, (int)top,
-                                        (int)(right - left), (int)(bottom - top));
-                            }
-
-                            if (style == Paint.Style.STROKE.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.drawOval((int)left, (int)top,
-                                        (int)(right - left), (int)(bottom - top));
-                            }
-                        }
-            });
-        }
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawCircle(long nativeCanvas,
-            float cx, float cy, float radius, long paint) {
-        nDrawOval(nativeCanvas,
-                cx - radius, cy - radius, cx + radius, cy + radius,
-                paint);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawArc(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom,
-            final float startAngle, final float sweep,
-            final boolean useCenter, long paint) {
-        if (right > left && bottom > top) {
-            draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                    new GcSnapshot.Drawable() {
-                        @Override
-                        public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                            int style = paintDelegate.getStyle();
-
-                            Arc2D.Float arc = new Arc2D.Float(
-                                    left, top, right - left, bottom - top,
-                                    -startAngle, -sweep,
-                                    useCenter ? Arc2D.PIE : Arc2D.OPEN);
-
-                            // draw
-                            if (style == Paint.Style.FILL.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.fill(arc);
-                            }
-
-                            if (style == Paint.Style.STROKE.nativeInt ||
-                                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                                graphics.draw(arc);
-                            }
-                        }
-            });
-        }
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRoundRect(long nativeCanvas,
-            final float left, final float top, final float right, final float bottom,
-            final float rx, final float ry, long paint) {
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        int style = paintDelegate.getStyle();
-
-                        // draw
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fillRoundRect(
-                                    (int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top),
-                                    2 * (int)rx, 2 * (int)ry);
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.drawRoundRect(
-                                    (int)left, (int)top,
-                                    (int)(right - left), (int)(bottom - top),
-                                    2 * (int)rx, 2 * (int)ry);
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawPath(long nativeCanvas, long path, long paint) {
-        final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
-        if (pathDelegate == null) {
-            return;
-        }
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                        Shape shape = pathDelegate.getJavaShape();
-                        Rectangle2D bounds = shape.getBounds2D();
-                        if (bounds.isEmpty()) {
-                            // Apple JRE 1.6 doesn't like drawing empty shapes.
-                            // http://b.android.com/178278
-
-                            if (pathDelegate.isEmpty()) {
-                                // This means that the path doesn't have any lines or curves so
-                                // nothing to draw.
-                                return;
-                            }
-
-                            // The stroke width is not consider for the size of the bounds so,
-                            // for example, a horizontal line, would be considered as an empty
-                            // rectangle.
-                            // If the strokeWidth is not 0, we use it to consider the size of the
-                            // path as well.
-                            float strokeWidth = paintDelegate.getStrokeWidth();
-                            if (strokeWidth <= 0.0f) {
-                                return;
-                            }
-                            bounds.setRect(bounds.getX(), bounds.getY(),
-                                    Math.max(strokeWidth, bounds.getWidth()),
-                                    Math.max(strokeWidth, bounds.getHeight()));
-                        }
-
-                        int style = paintDelegate.getStyle();
-
-                        if (style == Paint.Style.FILL.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.fill(shape);
-                        }
-
-                        if (style == Paint.Style.STROKE.nativeInt ||
-                                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-                            graphics.draw(shape);
-                        }
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawRegion(long nativeCanvas, long nativeRegion,
-            long nativePaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Some canvas paths may not be drawn", null, null);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawNinePatch(Canvas thisCanvas, long nativeCanvas,
-            long nativeBitmap, long ninePatch, final float dstLeft, final float dstTop,
-            final float dstRight, final float dstBottom, long nativePaintOrZero,
-            final int screenDensity, final int bitmapDensity) {
-
-        // get the delegate from the native int.
-        final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        byte[] c = NinePatch_Delegate.getChunk(ninePatch);
-        if (c == null) {
-            // not a 9-patch?
-            BufferedImage image = bitmapDelegate.getImage();
-            drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
-                    image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
-                    (int) dstBottom);
-            return;
-        }
-
-        final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
-        assert chunkObject != null;
-        if (chunkObject == null) {
-            return;
-        }
-
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // this one can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
-                        (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
-                        bitmapDensity);
-            }
-        }, paintDelegate, true, false);
-
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap,
-                                                 float left, float top,
-                                                 long nativePaintOrZero,
-                                                 int canvasDensity,
-                                                 int screenDensity,
-                                                 int bitmapDensity) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        BufferedImage image = bitmapDelegate.getImage();
-        float right = left + image.getWidth();
-        float bottom = top + image.getHeight();
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
-                0, 0, image.getWidth(), image.getHeight(),
-                (int)left, (int)top, (int)right, (int)bottom);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap,
-                                 float srcLeft, float srcTop, float srcRight, float srcBottom,
-                                 float dstLeft, float dstTop, float dstRight, float dstBottom,
-                                 long nativePaintOrZero, int screenDensity, int bitmapDensity) {
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
-                (int)srcLeft, (int)srcTop, (int)srcRight, (int)srcBottom,
-                (int)dstLeft, (int)dstTop, (int)dstRight, (int)dstBottom);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawBitmap(long nativeCanvas, int[] colors,
-                                                int offset, int stride, final float x,
-                                                 final float y, int width, int height,
-                                                 boolean hasAlpha,
-                                                 long nativePaintOrZero) {
-        // create a temp BufferedImage containing the content.
-        final BufferedImage image = new BufferedImage(width, height,
-                hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
-        image.setRGB(0, 0, width, height, colors, offset, stride);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                        if (paint != null && paint.isFilterBitmap()) {
-                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                        }
-
-                        graphics.drawImage(image, (int) x, (int) y, null);
-                    }
-        });
-    }
-
-    @LayoutlibDelegate
-    public static void nativeDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
-                                                      long nMatrix, long nPaint) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the delegate from the native int, which can be null
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (bitmapDelegate == null) {
-            return;
-        }
-
-        final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
-
-        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
-        if (matrixDelegate == null) {
-            return;
-        }
-
-        final AffineTransform mtx = matrixDelegate.getAffineTransform();
-
-        canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
-                @Override
-                public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                    if (paint != null && paint.isFilterBitmap()) {
-                        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                    }
-
-                    //FIXME add support for canvas, screen and bitmap densities.
-                    graphics.drawImage(image, mtx, null);
-                }
-        }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nativeDrawBitmapMesh(long nCanvas, Bitmap bitmap,
-            int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
-            int colorOffset, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawBitmapMesh is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nativeDrawVertices(long nCanvas, int mode, int n,
-            float[] verts, int vertOffset,
-            float[] texs, int texOffset,
-            int[] colors, int colorOffset,
-            short[] indices, int indexOffset,
-            int indexCount, long nPaint) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawVertices is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawText(long nativeCanvas, char[] text, int index, int count,
-            float startX, float startY, int flags, long paint, long typeface) {
-        drawText(nativeCanvas, text, index, count, startX, startY, (flags & 1) != 0,
-                paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawText(long nativeCanvas, String text,
-            int start, int end, float x, float y, final int flags, long paint,
-            long typeface) {
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextRun(long nativeCanvas, String text,
-            int start, int end, int contextStart, int contextEnd,
-            float x, float y, boolean isRtl, long paint, long typeface) {
-        int count = end - start;
-        char[] buffer = TemporaryBuffer.obtain(count);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextRun(long nativeCanvas, char[] text,
-            int start, int count, int contextStart, int contextCount,
-            float x, float y, boolean isRtl, long paint, long typeface) {
-        drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextOnPath(long nativeCanvas,
-                                                     char[] text, int index,
-                                                     int count, long path,
-                                                     float hOffset,
-                                                     float vOffset, int bidiFlags,
-                                                     long paint, long typeface) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    public static void nDrawTextOnPath(long nativeCanvas,
-                                                     String text, long path,
-                                                     float hOffset,
-                                                     float vOffset,
-                                                     int bidiFlags, long paint,
-                                                     long typeface) {
-        // FIXME
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long getNativeFinalizer() {
+    /*package*/ static long nGetNativeFinalizer() {
         synchronized (Canvas_Delegate.class) {
             if (sFinalizer == -1) {
                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
-                    Canvas_Delegate delegate = sManager.getDelegate(nativePtr);
+                    Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
                     if (delegate != null) {
                         delegate.dispose();
                     }
@@ -1011,230 +500,12 @@
         return sFinalizer;
     }
 
-    // ---- Private delegate/helper methods ----
-
-    /**
-     * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
-     * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
-            GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the paint which can be null if nPaint is 0;
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
-        canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
-    }
-
-    /**
-     * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
-     * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
-     * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
-     */
-    private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        canvasDelegate.mSnapshot.draw(drawable);
-    }
-
-    private static void drawText(long nativeCanvas, final char[] text, final int index,
-            final int count, final float startX, final float startY, final boolean isRtl,
-            long paint, final long typeface) {
-
-        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
-                new GcSnapshot.Drawable() {
-            @Override
-            public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
-                // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
-                // Any change to this method should be reflected in Paint.measureText
-
-                // assert that the typeface passed is actually the one stored in paint.
-                assert (typeface == paintDelegate.mNativeTypeface);
-
-                // Paint.TextAlign indicates how the text is positioned relative to X.
-                // LEFT is the default and there's nothing to do.
-                float x = startX;
-                int limit = index + count;
-                if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                    RectF bounds = paintDelegate.measureText(text, index, count, null, 0,
-                            isRtl);
-                    float m = bounds.right - bounds.left;
-                    if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
-                        x -= m / 2;
-                    } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
-                        x -= m;
-                    }
-                }
-
-                new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x, startY)
-                        .renderText(index, limit, isRtl, null, 0, true);
-            }
-        });
-    }
-
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
+        super(bitmap);
     }
 
     private Canvas_Delegate() {
-        mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
-    }
-
-    /**
-     * Disposes of the {@link Graphics2D} stack.
-     */
-    private void dispose() {
-        mSnapshot.dispose();
-    }
-
-    private int save(int saveFlags) {
-        // get the current save count
-        int count = mSnapshot.size();
-
-        mSnapshot = mSnapshot.save(saveFlags);
-
-        // return the old save count
-        return count;
-    }
-
-    private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
-        Paint_Delegate paint = new Paint_Delegate();
-        paint.setAlpha(alpha);
-        return saveLayer(rect, paint, saveFlags);
-    }
-
-    private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
-        // get the current save count
-        int count = mSnapshot.size();
-
-        mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
-
-        // return the old save count
-        return count;
-    }
-
-    /**
-     * Restores the {@link GcSnapshot} to <var>saveCount</var>
-     * @param saveCount the saveCount
-     */
-    private void restoreTo(int saveCount) {
-        mSnapshot = mSnapshot.restoreTo(saveCount);
-    }
-
-    /**
-     * Restores the top {@link GcSnapshot}
-     */
-    private void restore() {
-        mSnapshot = mSnapshot.restore();
-    }
-
-    private boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
-        return mSnapshot.clipRect(left, top, right, bottom, regionOp);
-    }
-
-    private static void drawBitmap(
-            long nativeCanvas,
-            Bitmap_Delegate bitmap,
-            long nativePaintOrZero,
-            final int sleft, final int stop, final int sright, final int sbottom,
-            final int dleft, final int dtop, final int dright, final int dbottom) {
-        // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return;
-        }
-
-        // get the paint, which could be null if the int is 0
-        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
-        final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
-
-        draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
-                new GcSnapshot.Drawable() {
-                    @Override
-                    public void draw(Graphics2D graphics, Paint_Delegate paint) {
-                        if (paint != null && paint.isFilterBitmap()) {
-                            graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-                        }
-
-                        //FIXME add support for canvas, screen and bitmap densities.
-                        graphics.drawImage(image, dleft, dtop, dright, dbottom,
-                                sleft, stop, sright, sbottom, null);
-                    }
-        });
-    }
-
-
-    /**
-     * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
-     * The image returns, through a 1-size boolean array, whether the drawing code should
-     * use a SRC composite no matter what the paint says.
-     *
-     * @param bitmap the bitmap
-     * @param paint the paint that will be used to draw
-     * @param forceSrcMode whether the composite will have to be SRC
-     * @return the image to draw
-     */
-    private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
-            boolean[] forceSrcMode) {
-        BufferedImage image = bitmap.getImage();
-        forceSrcMode[0] = false;
-
-        // if the bitmap config is alpha_8, then we erase all color value from it
-        // before drawing it.
-        if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
-            fixAlpha8Bitmap(image);
-        } else if (!bitmap.hasAlpha()) {
-            // hasAlpha is merely a rendering hint. There can in fact be alpha values
-            // in the bitmap but it should be ignored at drawing time.
-            // There is two ways to do this:
-            // - override the composite to be SRC. This can only be used if the composite
-            //   was going to be SRC or SRC_OVER in the first place
-            // - Create a different bitmap to draw in which all the alpha channel values is set
-            //   to 0xFF.
-            if (paint != null) {
-                Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
-                if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
-                    PorterDuff.Mode mode =
-                        ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
-
-                    forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER ||
-                            mode == PorterDuff.Mode.SRC;
-                }
-            }
-
-            // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
-            if (!forceSrcMode[0]) {
-                image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
-            }
-        }
-
-        return image;
-    }
-
-    private static void fixAlpha8Bitmap(final BufferedImage image) {
-        int w = image.getWidth();
-        int h = image.getHeight();
-        int[] argb = new int[w * h];
-        image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
-        final int length = argb.length;
-        for (int i = 0 ; i < length; i++) {
-            argb[i] &= 0xFF000000;
-        }
-        image.setRGB(0, 0, w, h, argb, 0, w);
+        super();
     }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index 59ddcc6..a459734 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -63,16 +63,8 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate1(long native_shaderA, long native_shaderB,
-            long native_mode) {
-        // FIXME not supported yet.
-        ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate2(long native_shaderA, long native_shaderB,
-            int porterDuffMode) {
+    /*package*/ static long nativeCreate(long native_shaderA, long native_shaderB,
+            int native_mode) {
         // FIXME not supported yet.
         ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
         return sManager.addNewDelegate(newDelegate);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 0bbe33d..e68d8b3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -90,10 +90,11 @@
     private int mHintingMode = Paint.HINTING_ON;
     private int mHyphenEdit;
     private float mLetterSpacing;  // not used in actual text rendering.
+    private float mWordSpacing;  // not used in actual text rendering.
     // Variant of the font. A paint's variant can only be compact or elegant.
     private FontVariant mFontVariant = FontVariant.COMPACT;
 
-    private Xfermode_Delegate mXfermode;
+    private int mPorterDuffMode = Xfermode.DEFAULT;
     private ColorFilter_Delegate mColorFilter;
     private Shader_Delegate mShader;
     private PathEffect_Delegate mPathEffect;
@@ -206,12 +207,10 @@
     }
 
     /**
-     * Returns the {@link Xfermode} delegate or null if none have been set
-     *
-     * @return the delegate or null.
+     * Returns the {@link PorterDuff.Mode} as an int
      */
-    public Xfermode_Delegate getXfermode() {
-        return mXfermode;
+    public int getPorterDuffMode() {
+        return mPorterDuffMode;
     }
 
     /**
@@ -841,16 +840,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nSetXfermode(long native_object, long xfermode) {
-        // get the delegate from the native int.
+    /*package*/ static void nSetXfermode(long native_object, int xfermode) {
         Paint_Delegate delegate = sManager.getDelegate(native_object);
         if (delegate == null) {
-            return xfermode;
+            return;
         }
-
-        delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode);
-
-        return xfermode;
+        delegate.mPorterDuffMode = xfermode;
     }
 
     @LayoutlibDelegate
@@ -998,7 +993,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(long native_object, char[] text,
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
             int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1007,7 +1002,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(long native_object, String text,
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
             int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1086,6 +1081,26 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static float nGetWordSpacing(long nativePaint) {
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+        return delegate.mWordSpacing;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
+                "Paint.setWordSpacing() not supported.", null, null);
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return;
+        }
+        delegate.mWordSpacing = wordSpacing;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING,
                 "Paint.setFontFeatureSettings() not supported.", null, null);
@@ -1215,7 +1230,7 @@
 
         mStrokeWidth = paint.mStrokeWidth;
         mStrokeMiter = paint.mStrokeMiter;
-        mXfermode = paint.mXfermode;
+        mPorterDuffMode = paint.mPorterDuffMode;
         mColorFilter = paint.mColorFilter;
         mShader = paint.mShader;
         mPathEffect = paint.mPathEffect;
@@ -1242,7 +1257,7 @@
         mTextSize = 20.f;
         mTextScaleX = 1.f;
         mTextSkewX = 0.f;
-        mXfermode = null;
+        mPorterDuffMode = Xfermode.DEFAULT;
         mColorFilter = null;
         mShader = null;
         mPathEffect = null;
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
deleted file mode 100644
index 8825f84..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.PorterDuff.Mode;
-
-import java.awt.Composite;
-
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
-
-/**
- * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
- *
- * Through the layoutlib_create tool, the original native methods of PorterDuffXfermode have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffXfermode class.
- *
- * Because this extends {@link Xfermode_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
- * {@link Xfermode_Delegate}.
- *
- */
-public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
-
-    // ---- delegate data ----
-
-    private final Mode mMode;
-
-    // ---- Public Helper methods ----
-
-    public Mode getMode() {
-        return mMode;
-    }
-
-    @Override
-    public Composite getComposite(int alpha) {
-        return PorterDuffUtility.getComposite(mMode, alpha);
-    }
-
-    @Override
-    public boolean isSupported() {
-        return true;
-    }
-
-    @Override
-    public String getSupportMessage() {
-        // no message since isSupported returns true;
-        return null;
-    }
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreateXfermode(int mode) {
-        PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
-        return sManager.addNewDelegate(newDelegate);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-    private PorterDuffXfermode_Delegate(int mode) {
-        mMode = getPorterDuffMode(mode);
-    }
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
deleted file mode 100644
index 94a6d76..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Composite;
-
-/**
- * Delegate implementing the native methods of android.graphics.Xfermode
- *
- * Through the layoutlib_create tool, the original native methods of Xfermode have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Xfermode class.
- *
- * This also serve as a base class for all Xfermode delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Xfermode_Delegate {
-
-    // ---- delegate manager ----
-    protected static final DelegateManager<Xfermode_Delegate> sManager =
-            new DelegateManager<Xfermode_Delegate>(Xfermode_Delegate.class);
-
-    // ---- delegate helper data ----
-
-    // ---- delegate data ----
-
-    // ---- Public Helper methods ----
-
-    public static Xfermode_Delegate getDelegate(long native_instance) {
-        return sManager.getDelegate(native_instance);
-    }
-
-    public abstract Composite getComposite(int alpha);
-    public abstract boolean isSupported();
-    public abstract String getSupportMessage();
-
-
-    // ---- native methods ----
-
-    @LayoutlibDelegate
-    /*package*/ static void finalizer(long native_instance) {
-        sManager.removeJavaReferenceFor(native_instance);
-    }
-
-    // ---- Private delegate/helper methods ----
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index 200fe3b..ad2c564 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -58,8 +58,13 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
+        // TODO: implement
+    }
+    @LayoutlibDelegate
     /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
-            long nativeInterpolator, long startDelay, long duration, int repeatCount) {
+            long nativeInterpolator, long startDelay, long duration, int repeatCount,
+            int repeatMode) {
         PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
         if (holder == null || holder.getValues() == null) {
             return;
@@ -72,6 +77,7 @@
         animator.setStartDelay(startDelay);
         animator.setDuration(duration);
         animator.setRepeatCount(repeatCount);
+        animator.setRepeatMode(repeatMode);
         animator.setTarget(holder);
         animator.setPropertyName(holder.getValues().getPropertyName());
 
@@ -137,6 +143,14 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
+        PropertySetter setter = sHolders.getDelegate(nativePtr);
+        assert setter != null;
+
+        setter.setValues(data);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
         AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
         assert animatorSet != null;
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
index 3d78931..fc848d9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
@@ -18,6 +18,7 @@
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.Canvas;
 import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
 
 public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
@@ -25,4 +26,9 @@
     /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
         return true;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void onDraw(VectorDrawableAnimatorRT thisDrawableAnimator, Canvas canvas) {
+        // Do not attempt to record as we are not using a DisplayListCanvas
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 9904263..cee679a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -23,6 +23,7 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.BaseCanvas_Delegate;
 import android.graphics.Canvas_Delegate;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -1197,7 +1198,7 @@
                     fillPaintDelegate.setColorFilter(filterPtr);
                     fillPaintDelegate.setShader(fullPath.mFillGradient);
                     Path_Delegate.native_setFillType(mRenderPath.mNativePath, fullPath.mFillType);
-                    Canvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
+                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
                             .getNativeInstance());
                 }
 
@@ -1228,7 +1229,7 @@
                     final float finalStrokeScale = minScale * matrixScale;
                     strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
                     strokePaintDelegate.setShader(fullPath.mStrokeGradient);
-                    Canvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
+                    BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
                             .getNativeInstance());
                 }
             }
diff --git a/tools/layoutlib/bridge/src/android/os/ServiceManager.java b/tools/layoutlib/bridge/src/android/os/ServiceManager.java
index 3e1ec07..c96d2ca 100644
--- a/tools/layoutlib/bridge/src/android/os/ServiceManager.java
+++ b/tools/layoutlib/bridge/src/android/os/ServiceManager.java
@@ -31,6 +31,13 @@
     }
 
     /**
+     * Is not supposed to return null, but that is fine for layoutlib.
+     */
+    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
+        return null;
+    }
+
+    /**
      * Place a new @a service called @a name into the service
      * manager.
      *
diff --git a/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
index af0c456..d299add 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
@@ -102,4 +102,9 @@
     /*package*/ static void native_add_change_callback() {
         // pass.
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_report_sysprop_change() {
+        // pass.
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
index 24f7887..a801cb0 100644
--- a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
@@ -21,6 +21,8 @@
 
 import android.graphics.Matrix;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Delegate implementing the native methods of {@link RenderNode}
  * <p/>
@@ -35,7 +37,7 @@
     // ---- delegate manager ----
     private static final DelegateManager<RenderNode_Delegate> sManager =
             new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
-
+    private static long sFinalizer = -1;
 
     private float mLift;
     private float mTranslationX;
@@ -62,8 +64,13 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nDestroyRenderNode(long renderNode) {
-        sManager.removeJavaReferenceFor(renderNode);
+    /*package*/ static long nGetNativeFinalizer() {
+        synchronized (RenderNode_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index e80cbf2..7526e09 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -24,12 +24,13 @@
 import android.graphics.ColorFilter_Delegate;
 import android.graphics.Paint;
 import android.graphics.Paint_Delegate;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.Region_Delegate;
 import android.graphics.Shader_Delegate;
-import android.graphics.Xfermode_Delegate;
 
 import java.awt.AlphaComposite;
 import java.awt.Color;
@@ -827,28 +828,9 @@
             g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
             return;
         }
-        Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
-        if (xfermodeDelegate != null) {
-            if (xfermodeDelegate.isSupported()) {
-                Composite composite = xfermodeDelegate.getComposite(alpha);
-                assert composite != null;
-                if (composite != null) {
-                    g.setComposite(composite);
-                    return;
-                }
-            } else {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
-                        xfermodeDelegate.getSupportMessage(),
-                        null /*throwable*/, null /*data*/);
-            }
-        }
-        // if there was no custom xfermode, but we have alpha (due to a shader and a non
-        // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
-        // that will handle the alpha.
-        if (alpha != 0xFF) {
-            g.setComposite(AlphaComposite.getInstance(
-                    AlphaComposite.SRC_OVER, (float) alpha / 255.f));
-        }
+        Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
+        Composite composite = PorterDuffUtility.getComposite(mode, alpha);
+        g.setComposite(composite);
     }
 
     private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index 80d7c68..70e2eb1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -24,14 +24,12 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter_Delegate;
-import android.graphics.PorterDuffXfermode_Delegate;
 
 import java.awt.AlphaComposite;
 import java.awt.Composite;
 
 /**
- * Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
- * PorterDuffXfermode_Delegate}.
+ * Provides various utility methods for {@link PorterDuffColorFilter_Delegate}.
  */
 public final class PorterDuffUtility {
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bf61f7e..b7d08f2 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -180,6 +180,7 @@
         "android.graphics.BitmapFactory#finishDecode",
         "android.graphics.BitmapFactory#setDensityFromOptions",
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.FontFamily#addFont",
         "android.graphics.Typeface#getSystemFontConfigLocation",
@@ -206,7 +207,7 @@
         "android.view.MenuInflater#registerMenu",
         "android.view.RenderNode#getMatrix",
         "android.view.RenderNode#nCreate",
-        "android.view.RenderNode#nDestroyRenderNode",
+        "android.view.RenderNode#nGetNativeFinalizer",
         "android.view.RenderNode#nSetElevation",
         "android.view.RenderNode#nGetElevation",
         "android.view.RenderNode#nSetTranslationX",
@@ -247,6 +248,7 @@
      */
     public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
         "android.animation.PropertyValuesHolder",
+        "android.graphics.BaseCanvas",
         "android.graphics.Bitmap",
         "android.graphics.BitmapFactory",
         "android.graphics.BitmapShader",
@@ -275,7 +277,6 @@
         "android.graphics.PathEffect",
         "android.graphics.PathMeasure",
         "android.graphics.PorterDuffColorFilter",
-        "android.graphics.PorterDuffXfermode",
         "android.graphics.RadialGradient",
         "android.graphics.Rasterizer",
         "android.graphics.Region",
@@ -283,7 +284,6 @@
         "android.graphics.SumPathEffect",
         "android.graphics.SweepGradient",
         "android.graphics.Typeface",
-        "android.graphics.Xfermode",
         "android.graphics.drawable.AnimatedVectorDrawable",
         "android.graphics.drawable.VectorDrawable",
         "android.os.SystemClock",
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index 9bb91e5..4b6cf43 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -124,6 +124,7 @@
                         "android.annotation.NonNull",       // annotations
                         "android.annotation.Nullable",      // annotations
                         "com.android.internal.transition.EpicenterTranslateClipReveal",
+                        "com.android.internal.graphics.drawable.AnimationScaleListDrawable",
                     },
                     excludeClasses,
                     new String[] {