Merge "Always call finishBackup() if performFullBackup() succeeded" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 06e98ef..b2689bb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25492,6 +25492,8 @@
   public static final class Telephony.Mms.Intents {
     field public static final java.lang.String CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
     field public static final java.lang.String DELETED_CONTENTS = "deleted_contents";
+    field public static final java.lang.String MMS_DOWNLOAD_ACTION = "android.provider.Telephony.MMS_DOWNLOAD";
+    field public static final java.lang.String MMS_SEND_ACTION = "android.provider.Telephony.MMS_SEND";
   }
 
   public static final class Telephony.Mms.Outbox implements android.provider.Telephony.BaseMmsColumns {
@@ -25595,8 +25597,10 @@
     field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
     field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
     field public static final java.lang.String SMS_EMERGENCY_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
+    field public static final java.lang.String SMS_FILTER_ACTION = "android.provider.Telephony.SMS_FILTER";
     field public static final java.lang.String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
     field public static final java.lang.String SMS_REJECTED_ACTION = "android.provider.Telephony.SMS_REJECTED";
+    field public static final java.lang.String SMS_SEND_ACTION = "android.provider.Telephony.SMS_SEND";
     field public static final java.lang.String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION = "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED";
     field public static final java.lang.String WAP_PUSH_DELIVER_ACTION = "android.provider.Telephony.WAP_PUSH_DELIVER";
     field public static final java.lang.String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
@@ -28782,9 +28786,13 @@
   public final class SmsManager {
     method public java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
     method public static android.telephony.SmsManager getDefault();
+    method public void injectSmsPdu(byte[], java.lang.String, android.app.PendingIntent);
     method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
     method public void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+    method public void updateMmsDownloadStatus(int, byte[]);
+    method public void updateMmsSendStatus(int, boolean);
+    method public void updateSmsSendStatus(int, boolean);
     field public static final int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
     field public static final int RESULT_ERROR_NO_SERVICE = 4; // 0x4
     field public static final int RESULT_ERROR_NULL_PDU = 3; // 0x3
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b7f1ff9..d6c17ae 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -998,8 +998,8 @@
 
         final InstallSessionParams params = new InstallSessionParams();
         params.installFlags = PackageManager.INSTALL_ALL_USERS;
-        params.mode = InstallSessionParams.MODE_FULL_INSTALL;
-        params.progressMax = -1;
+        params.setModeFullInstall();
+        params.setProgressMax(-1);
 
         String opt;
         while ((opt = nextOption()) != null) {
@@ -1021,10 +1021,11 @@
             } else if (opt.equals("-d")) {
                 params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
             } else if (opt.equals("-p")) {
-                params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
+                params.setModeInheritExisting();
             } else if (opt.equals("-S")) {
-                params.deltaSize = Long.parseLong(nextOptionData());
-                params.progressMax = (int) params.deltaSize;
+                final long deltaSize = Long.parseLong(nextOptionData());
+                params.setDeltaSize(deltaSize);
+                params.setProgressMax((int) params.deltaSize);
             } else if (opt.equals("--abi")) {
                 params.abiOverride = checkAbiArgument(nextOptionData());
             } else {
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index ac74ca1..8a53e08 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -64,6 +64,8 @@
     int mInitialMinute;
     boolean mIs24HourView;
 
+    private boolean mIsCanceled;
+
     /**
      * @param context Parent.
      * @param callBack How parent is notified.
@@ -124,10 +126,13 @@
         mTimePicker.setDismissCallback(new TimePicker.TimePickerDismissCallback() {
             @Override
             public void dismiss(TimePicker view, boolean isCancel, int hourOfDay, int minute) {
+                mIsCanceled = isCancel;
                 if (!isCancel) {
                     mTimeSetCallback.onTimeSet(view, hourOfDay, minute);
+                    TimePickerDialog.this.dismiss();
+                } else {
+                    TimePickerDialog.this.cancel();
                 }
-                TimePickerDialog.this.dismiss();
             }
         });
         mTimePicker.setIs24HourView(mIs24HourView);
@@ -150,7 +155,7 @@
     }
 
     private void tryNotifyTimeSet() {
-        if (mTimeSetCallback != null) {
+        if (mTimeSetCallback != null && !mIsCanceled) {
             mTimePicker.clearFocus();
             mTimeSetCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
                     mTimePicker.getCurrentMinute());
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 6cda905..3f10b92 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -75,9 +75,7 @@
     public static final int DENSITY_400 = 400;
 
     /**
-     * Standard quantized DPI for extra-extra-high-density screens.  Applications
-     * should not generally worry about this density; relying on XHIGH graphics
-     * being scaled up to it should be sufficient for almost all cases.
+     * Standard quantized DPI for extra-extra-high-density screens.
      */
     public static final int DENSITY_XXHIGH = 480;
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 64366e5..1f7acec 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -39,6 +39,7 @@
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
 #include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <selinux/android.h>
 #include <processgroup/processgroup.h>
@@ -536,8 +537,15 @@
         jint debug_flags, jobjectArray rlimits,
         jint mount_external, jstring se_info, jstring se_name,
         jintArray fdsToClose) {
+    // Grant CAP_WAKE_ALARM to the Bluetooth process.
+    jlong capabilities = 0;
+    if (uid == AID_BLUETOOTH) {
+        capabilities |= (1LL << CAP_WAKE_ALARM);
+    }
+
     return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
-            rlimits, 0, 0, mount_external, se_info, se_name, false, fdsToClose);
+            rlimits, capabilities, capabilities, mount_external, se_info,
+            se_name, false, fdsToClose);
 }
 
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
diff --git a/core/res/res/drawable/btn_check_material_anim.xml b/core/res/res/drawable/btn_check_material_anim.xml
index 73b8a3e..1e05e84 100644
--- a/core/res/res/drawable/btn_check_material_anim.xml
+++ b/core/res/res/drawable/btn_check_material_anim.xml
@@ -30,28 +30,28 @@
     <transition android:fromId="@+id/off" android:toId="@+id/on">
         <animation-list>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_001" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_001" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_002" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_002" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_003" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_003" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_004" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_004" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_005" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_005" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_006" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_006" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_007" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_on_mtrl_007" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
                 <bitmap android:src="@drawable/btn_check_to_on_mtrl_008" android:tint="?attr/colorControlActivated" />
@@ -106,28 +106,28 @@
                 <bitmap android:src="@drawable/btn_check_to_off_mtrl_007" android:tint="?attr/colorControlActivated" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_008" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_008" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_009" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_009" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_010" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_010" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_011" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_011" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_012" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_012" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_013" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_013" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_014" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_014" android:tint="?attr/colorControlNormal" />
             </item>
             <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_015" android:tint="?attr/colorControlActivated" />
+                <bitmap android:src="@drawable/btn_check_to_off_mtrl_015" android:tint="?attr/colorControlNormal" />
             </item>
         </animation-list>
     </transition>
diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd
index 301d911..12f8c9a 100644
--- a/docs/html/google/play/billing/billing_overview.jd
+++ b/docs/html/google/play/billing/billing_overview.jd
@@ -38,6 +38,16 @@
 features that you need to understand in order to add In-app 
 Billing features into your application.</p>
 
+<p class="note"><b>Note</b>: Ensure that you comply with applicable laws in the countries where you
+distribute apps. For example, in EU countries, laws based on the
+<a href="http://ec.europa.eu/justice/consumer-marketing/unfair-trade/unfair-practices/">Unfair
+Commercial Practices Directive</a> prohibit direct exhortations to children to buy advertised
+products or to persuade their parents or other adults to buy advertised products for them.
+See the
+<a href="http://ec.europa.eu/consumers/enforcement/docs/common_position_on_online_games_en.pdf">position
+of the EU consumer protection authorities</a> for more information on this and other topics.
+</p>
+
 <h2 id="api">In-app Billing API</h2>
 <p>Your application accesses the In-app Billing service using an API that is 
 exposed by the Google Play app that is installed on the device. The Google Play 
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index b77a72a..71b15d5 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -349,8 +349,7 @@
 <input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" />
 <label id="agreeLabel" for="agree">I have read and agree with the above terms and conditions</label>
 </p>
-<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return
-onDownloadNdkForRealz(this);"></a></p>
+<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return onDownloadNdkForRealz(this);"></a></p>
 </div>
 
 
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index d5aa5f8..2cb7b03 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -16,8 +16,6 @@
 
 package android.graphics.drawable;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.annotation.NonNull;
@@ -160,48 +158,51 @@
     }
 
     private boolean selectTransition(int toIndex) {
-        if (toIndex == mTransitionToIndex) {
-            // Already animating to that keyframe.
-            return true;
-        }
-
+        final int fromIndex;
         final Transition currentTransition = mTransition;
         if (currentTransition != null) {
             if (toIndex == mTransitionToIndex) {
+                // Already animating to that keyframe.
                 return true;
-            } else if (toIndex == mTransitionFromIndex) {
+            } else if (toIndex == mTransitionFromIndex && currentTransition.canReverse()) {
                 // Reverse the current animation.
                 currentTransition.reverse();
-                mTransitionFromIndex = mTransitionToIndex;
-                mTransitionToIndex = toIndex;
+                mTransitionToIndex = mTransitionFromIndex;
+                mTransitionFromIndex = toIndex;
                 return true;
             }
 
+            // Start the next transition from the end of the current one.
+            fromIndex = mTransitionToIndex;
+
             // Changing animation, end the current animation.
             currentTransition.stop();
-            mTransition = null;
+        } else {
+            fromIndex = getCurrentIndex();
         }
 
         // Reset state.
+        mTransition = null;
         mTransitionFromIndex = -1;
         mTransitionToIndex = -1;
 
         final AnimatedStateListState state = mState;
-        final int fromIndex = getCurrentIndex();
         final int fromId = state.getKeyframeIdAt(fromIndex);
         final int toId = state.getKeyframeIdAt(toIndex);
-
         if (toId == 0 || fromId == 0) {
             // Missing a keyframe ID.
             return false;
         }
 
         final int transitionIndex = state.indexOfTransition(fromId, toId);
-        if (transitionIndex < 0 || !selectDrawable(transitionIndex)) {
+        if (transitionIndex < 0) {
             // Couldn't select a transition.
             return false;
         }
 
+        // This may fail if we're already on the transition, but that's okay!
+        selectDrawable(transitionIndex);
+
         final Transition transition;
         final Drawable d = getCurrent();
         if (d instanceof AnimationDrawable) {
@@ -500,17 +501,6 @@
         return this;
     }
 
-    private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator anim) {
-            selectDrawable(mTransitionToIndex);
-
-            mTransitionToIndex = -1;
-            mTransitionFromIndex = -1;
-            mTransition = null;
-        }
-    };
-
     static class AnimatedStateListState extends StateListState {
         private static final int REVERSE_SHIFT = 32;
         private static final int REVERSE_MASK = 0x1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b32d3dd..78a99e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -456,9 +456,11 @@
                 } else {
                     // Launch the activity anew with the desired animation
                     Intent i = new Intent(task.key.baseIntent);
-                    i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
-                            | Intent.FLAG_ACTIVITY_TASK_ON_HOME
-                            | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                            | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+                    if ((i.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == 0) {
+                        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    }
                     try {
                         UserHandle taskUser = new UserHandle(task.userId);
                         if (launchOpts != null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 190e87c..db915e2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -76,6 +76,10 @@
     @GuardedBy("mSessions")
     private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
 
+    /** Historical sessions kept around for debugging purposes */
+    @GuardedBy("mSessions")
+    private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+
     private RemoteCallbackList<IPackageInstallerObserver> mObservers = new RemoteCallbackList<>();
 
     private static final FilenameFilter sStageFilter = new FilenameFilter() {
@@ -344,18 +348,29 @@
     }
 
     void dump(IndentingPrintWriter pw) {
-        pw.println("Active install sessions:");
-        pw.increaseIndent();
         synchronized (mSessions) {
-            final int N = mSessions.size();
+            pw.println("Active install sessions:");
+            pw.increaseIndent();
+            int N = mSessions.size();
             for (int i = 0; i < N; i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
                 session.dump(pw);
                 pw.println();
             }
+            pw.println();
+            pw.decreaseIndent();
+
+            pw.println("Historical install sessions:");
+            pw.increaseIndent();
+            N = mHistoricalSessions.size();
+            for (int i = 0; i < N; i++) {
+                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
+                session.dump(pw);
+                pw.println();
+            }
+            pw.println();
+            pw.decreaseIndent();
         }
-        pw.println();
-        pw.decreaseIndent();
     }
 
     class Callback {
@@ -367,6 +382,7 @@
             notifySessionFinished(session.sessionId, success);
             synchronized (mSessions) {
                 mSessions.remove(session.sessionId);
+                mHistoricalSessions.put(session.sessionId, session);
             }
             writeSessionsAsync();
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 31d9704..0e6a3f0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -65,6 +65,7 @@
 
 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     private static final String TAG = "PackageInstaller";
+    private static final boolean LOGD = true;
 
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -435,35 +436,25 @@
      */
     private void spliceExistingFilesIntoStage() throws PackageManagerException {
         final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
-        final File existingDir = new File(app.getBaseCodePath());
 
-        try {
-            linkTreeIgnoringExisting(existingDir, sessionStageDir);
-        } catch (ErrnoException e) {
-            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                    "Failed to splice into stage");
-        }
-    }
+        int n = 0;
+        final File[] oldFiles = new File(app.getCodePath()).listFiles();
+        if (!ArrayUtils.isEmpty(oldFiles)) {
+            for (File oldFile : oldFiles) {
+                if (!PackageParser.isApkFile(oldFile)) continue;
 
-    /**
-     * Recursively hard link all files from source directory tree to target.
-     * When a file already exists in the target tree, it leaves that file
-     * intact.
-     */
-    private void linkTreeIgnoringExisting(File sourceDir, File targetDir) throws ErrnoException {
-        final File[] sourceContents = sourceDir.listFiles();
-        if (ArrayUtils.isEmpty(sourceContents)) return;
-
-        for (File sourceFile : sourceContents) {
-            final File targetFile = new File(targetDir, sourceFile.getName());
-
-            if (sourceFile.isDirectory()) {
-                targetFile.mkdir();
-                linkTreeIgnoringExisting(sourceFile, targetFile);
-            } else {
-                Libcore.os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
+                final File newFile = new File(sessionStageDir, oldFile.getName());
+                try {
+                    Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
+                    n++;
+                } catch (ErrnoException e) {
+                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Failed to splice into stage", e);
+                }
             }
         }
+
+        if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0ad3a68..727cff0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10026,9 +10026,6 @@
             }
         }
 
-        // Nuke any cached code
-        deleteCodeCacheDirsLI(pkgName);
-
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
@@ -10066,6 +10063,7 @@
             deletedPkg = false;
         } else {
             // Successfully deleted the old package. Now proceed with re-installation
+            deleteCodeCacheDirsLI(pkgName);
             try {
                 final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
                         scanMode | SCAN_UPDATE_TIME, System.currentTimeMillis(), user, abiOverride);
@@ -10177,6 +10175,8 @@
         }
 
         // Successfully disabled the old package. Now proceed with re-installation
+        deleteCodeCacheDirsLI(packageName);
+
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java
new file mode 100644
index 0000000..a3ec2cc
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BlendComposite.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import java.awt.Composite;
+import java.awt.CompositeContext;
+import java.awt.RenderingHints;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+
+/*
+ * (non-Javadoc)
+ * The class is adapted from a demo tool for Blending Modes written by
+ * Romain Guy (romainguy@android.com). The tool is available at
+ * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
+ */
+public final class BlendComposite implements Composite {
+    public enum BlendingMode {
+        NORMAL,
+        AVERAGE,
+        MULTIPLY,
+        SCREEN,
+        DARKEN,
+        LIGHTEN,
+        OVERLAY,
+        HARD_LIGHT,
+        SOFT_LIGHT,
+        DIFFERENCE,
+        NEGATION,
+        EXCLUSION,
+        COLOR_DODGE,
+        INVERSE_COLOR_DODGE,
+        SOFT_DODGE,
+        COLOR_BURN,
+        INVERSE_COLOR_BURN,
+        SOFT_BURN,
+        REFLECT,
+        GLOW,
+        FREEZE,
+        HEAT,
+        ADD,
+        SUBTRACT,
+        STAMP,
+        RED,
+        GREEN,
+        BLUE,
+        HUE,
+        SATURATION,
+        COLOR,
+        LUMINOSITY
+    }
+
+    public static final BlendComposite Normal = new BlendComposite(BlendingMode.NORMAL);
+    public static final BlendComposite Average = new BlendComposite(BlendingMode.AVERAGE);
+    public static final BlendComposite Multiply = new BlendComposite(BlendingMode.MULTIPLY);
+    public static final BlendComposite Screen = new BlendComposite(BlendingMode.SCREEN);
+    public static final BlendComposite Darken = new BlendComposite(BlendingMode.DARKEN);
+    public static final BlendComposite Lighten = new BlendComposite(BlendingMode.LIGHTEN);
+    public static final BlendComposite Overlay = new BlendComposite(BlendingMode.OVERLAY);
+    public static final BlendComposite HardLight = new BlendComposite(BlendingMode.HARD_LIGHT);
+    public static final BlendComposite SoftLight = new BlendComposite(BlendingMode.SOFT_LIGHT);
+    public static final BlendComposite Difference = new BlendComposite(BlendingMode.DIFFERENCE);
+    public static final BlendComposite Negation = new BlendComposite(BlendingMode.NEGATION);
+    public static final BlendComposite Exclusion = new BlendComposite(BlendingMode.EXCLUSION);
+    public static final BlendComposite ColorDodge = new BlendComposite(BlendingMode.COLOR_DODGE);
+    public static final BlendComposite InverseColorDodge = new BlendComposite(BlendingMode.INVERSE_COLOR_DODGE);
+    public static final BlendComposite SoftDodge = new BlendComposite(BlendingMode.SOFT_DODGE);
+    public static final BlendComposite ColorBurn = new BlendComposite(BlendingMode.COLOR_BURN);
+    public static final BlendComposite InverseColorBurn = new BlendComposite(BlendingMode.INVERSE_COLOR_BURN);
+    public static final BlendComposite SoftBurn = new BlendComposite(BlendingMode.SOFT_BURN);
+    public static final BlendComposite Reflect = new BlendComposite(BlendingMode.REFLECT);
+    public static final BlendComposite Glow = new BlendComposite(BlendingMode.GLOW);
+    public static final BlendComposite Freeze = new BlendComposite(BlendingMode.FREEZE);
+    public static final BlendComposite Heat = new BlendComposite(BlendingMode.HEAT);
+    public static final BlendComposite Add = new BlendComposite(BlendingMode.ADD);
+    public static final BlendComposite Subtract = new BlendComposite(BlendingMode.SUBTRACT);
+    public static final BlendComposite Stamp = new BlendComposite(BlendingMode.STAMP);
+    public static final BlendComposite Red = new BlendComposite(BlendingMode.RED);
+    public static final BlendComposite Green = new BlendComposite(BlendingMode.GREEN);
+    public static final BlendComposite Blue = new BlendComposite(BlendingMode.BLUE);
+    public static final BlendComposite Hue = new BlendComposite(BlendingMode.HUE);
+    public static final BlendComposite Saturation = new BlendComposite(BlendingMode.SATURATION);
+    public static final BlendComposite Color = new BlendComposite(BlendingMode.COLOR);
+    public static final BlendComposite Luminosity = new BlendComposite(BlendingMode.LUMINOSITY);
+
+    private float alpha;
+    private BlendingMode mode;
+
+    private BlendComposite(BlendingMode mode) {
+        this(mode, 1.0f);
+    }
+
+    private BlendComposite(BlendingMode mode, float alpha) {
+        this.mode = mode;
+        setAlpha(alpha);
+    }
+
+    public static BlendComposite getInstance(BlendingMode mode) {
+        return new BlendComposite(mode);
+    }
+
+    public static BlendComposite getInstance(BlendingMode mode, float alpha) {
+        return new BlendComposite(mode, alpha);
+    }
+
+    public BlendComposite derive(BlendingMode mode) {
+        return this.mode == mode ? this : new BlendComposite(mode, getAlpha());
+    }
+
+    public BlendComposite derive(float alpha) {
+        return this.alpha == alpha ? this : new BlendComposite(getMode(), alpha);
+    }
+
+    public float getAlpha() {
+        return alpha;
+    }
+
+    public BlendingMode getMode() {
+        return mode;
+    }
+
+    private void setAlpha(float alpha) {
+        if (alpha < 0.0f || alpha > 1.0f) {
+            throw new IllegalArgumentException(
+                    "alpha must be comprised between 0.0f and 1.0f");
+        }
+
+        this.alpha = alpha;
+    }
+
+    @Override
+    public int hashCode() {
+        return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof BlendComposite)) {
+            return false;
+        }
+
+        BlendComposite bc = (BlendComposite) obj;
+
+        if (mode != bc.mode) {
+            return false;
+        }
+
+        return alpha == bc.alpha;
+    }
+
+    public CompositeContext createContext(ColorModel srcColorModel,
+                                          ColorModel dstColorModel,
+                                          RenderingHints hints) {
+        return new BlendingContext(this);
+    }
+
+    private static final class BlendingContext implements CompositeContext {
+        private final Blender blender;
+        private final BlendComposite composite;
+
+        private BlendingContext(BlendComposite composite) {
+            this.composite = composite;
+            this.blender = Blender.getBlenderFor(composite);
+        }
+
+        public void dispose() {
+        }
+
+        public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
+            if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
+                dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
+                dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
+                throw new IllegalStateException(
+                        "Source and destination must store pixels as INT.");
+            }
+
+            int width = Math.min(src.getWidth(), dstIn.getWidth());
+            int height = Math.min(src.getHeight(), dstIn.getHeight());
+
+            float alpha = composite.getAlpha();
+
+            int[] srcPixel = new int[4];
+            int[] dstPixel = new int[4];
+            int[] result = new int[4];
+            int[] srcPixels = new int[width];
+            int[] dstPixels = new int[width];
+
+            for (int y = 0; y < height; y++) {
+                dstIn.getDataElements(0, y, width, 1, dstPixels);
+                if (alpha != 0) {
+                    src.getDataElements(0, y, width, 1, srcPixels);
+                    for (int x = 0; x < width; x++) {
+                        // pixels are stored as INT_ARGB
+                        // our arrays are [R, G, B, A]
+                        int pixel = srcPixels[x];
+                        srcPixel[0] = (pixel >> 16) & 0xFF;
+                        srcPixel[1] = (pixel >>  8) & 0xFF;
+                        srcPixel[2] = (pixel      ) & 0xFF;
+                        srcPixel[3] = (pixel >> 24) & 0xFF;
+
+                        pixel = dstPixels[x];
+                        dstPixel[0] = (pixel >> 16) & 0xFF;
+                        dstPixel[1] = (pixel >>  8) & 0xFF;
+                        dstPixel[2] = (pixel      ) & 0xFF;
+                        dstPixel[3] = (pixel >> 24) & 0xFF;
+
+                        result = blender.blend(srcPixel, dstPixel, result);
+
+                        // mixes the result with the opacity
+                        if (alpha == 1) {
+                            dstPixels[x] = (result[3] & 0xFF) << 24 |
+                                           (result[0] & 0xFF) << 16 |
+                                           (result[1] & 0xFF) <<  8 |
+                                           result[2] & 0xFF;
+                        } else {
+                            dstPixels[x] =
+                                    ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
+                                    ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
+                                    ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) <<  8 |
+                                    (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
+                        }
+
+                    }
+            }
+                dstOut.setDataElements(0, y, width, 1, dstPixels);
+            }
+        }
+    }
+
+    private static abstract class Blender {
+        public abstract int[] blend(int[] src, int[] dst, int[] result);
+
+        private static void RGBtoHSL(int r, int g, int b, float[] hsl) {
+            float var_R = (r / 255f);
+            float var_G = (g / 255f);
+            float var_B = (b / 255f);
+
+            float var_Min;
+            float var_Max;
+            float del_Max;
+
+            if (var_R > var_G) {
+                var_Min = var_G;
+                var_Max = var_R;
+            } else {
+                var_Min = var_R;
+                var_Max = var_G;
+            }
+            if (var_B > var_Max) {
+                var_Max = var_B;
+            }
+            if (var_B < var_Min) {
+                var_Min = var_B;
+            }
+
+            del_Max = var_Max - var_Min;
+
+            float H, S, L;
+            L = (var_Max + var_Min) / 2f;
+
+            if (del_Max - 0.01f <= 0.0f) {
+                H = 0;
+                S = 0;
+            } else {
+                if (L < 0.5f) {
+                    S = del_Max / (var_Max + var_Min);
+                } else {
+                    S = del_Max / (2 - var_Max - var_Min);
+                }
+
+                float del_R = (((var_Max - var_R) / 6f) + (del_Max / 2f)) / del_Max;
+                float del_G = (((var_Max - var_G) / 6f) + (del_Max / 2f)) / del_Max;
+                float del_B = (((var_Max - var_B) / 6f) + (del_Max / 2f)) / del_Max;
+
+                if (var_R == var_Max) {
+                    H = del_B - del_G;
+                } else if (var_G == var_Max) {
+                    H = (1 / 3f) + del_R - del_B;
+                } else {
+                    H = (2 / 3f) + del_G - del_R;
+                }
+                if (H < 0) {
+                    H += 1;
+                }
+                if (H > 1) {
+                    H -= 1;
+                }
+            }
+
+            hsl[0] = H;
+            hsl[1] = S;
+            hsl[2] = L;
+        }
+
+        private static void HSLtoRGB(float h, float s, float l, int[] rgb) {
+            int R, G, B;
+
+            if (s - 0.01f <= 0.0f) {
+                R = (int) (l * 255.0f);
+                G = (int) (l * 255.0f);
+                B = (int) (l * 255.0f);
+            } else {
+                float var_1, var_2;
+                if (l < 0.5f) {
+                    var_2 = l * (1 + s);
+                } else {
+                    var_2 = (l + s) - (s * l);
+                }
+                var_1 = 2 * l - var_2;
+
+                R = (int) (255.0f * hue2RGB(var_1, var_2, h + (1.0f / 3.0f)));
+                G = (int) (255.0f * hue2RGB(var_1, var_2, h));
+                B = (int) (255.0f * hue2RGB(var_1, var_2, h - (1.0f / 3.0f)));
+            }
+
+            rgb[0] = R;
+            rgb[1] = G;
+            rgb[2] = B;
+        }
+
+        private static float hue2RGB(float v1, float v2, float vH) {
+            if (vH < 0.0f) {
+                vH += 1.0f;
+            }
+            if (vH > 1.0f) {
+                vH -= 1.0f;
+            }
+            if ((6.0f * vH) < 1.0f) {
+                return (v1 + (v2 - v1) * 6.0f * vH);
+            }
+            if ((2.0f * vH) < 1.0f) {
+                return (v2);
+            }
+            if ((3.0f * vH) < 2.0f) {
+                return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
+            }
+            return (v1);
+        }
+
+        public static Blender getBlenderFor(BlendComposite composite) {
+            switch (composite.getMode()) {
+                case NORMAL:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            System.arraycopy(src, 0, result, 0, 4);
+                            return result;
+                        }
+                    };
+                case ADD:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 4; i++) {
+                                result[i] = Math.min(255, src[i] + dst[i]);
+                            }
+                            return result;
+                        }
+                    };
+                case AVERAGE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = (src[i] + dst[i]) >> 1;
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case BLUE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            System.arraycopy(dst, 0, result, 0, 3);
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case COLOR:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            float[] srcHSL = new float[3];
+                            RGBtoHSL(src[0], src[1], src[2], srcHSL);
+                            float[] dstHSL = new float[3];
+                            RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+                            HSLtoRGB(srcHSL[0], srcHSL[1], dstHSL[2], result);
+                            result[3] = Math.min(255, src[3] + dst[3]);
+
+                            return result;
+                        }
+                    };
+                case COLOR_BURN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = src[i] == 0 ? 0 :
+                                    Math.max(0, 255 - (((255 - dst[i]) << 8) / src[i]));
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case COLOR_DODGE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = src[i] == 255 ? 255 :
+                                    Math.min((dst[i] << 8) / (255 - src[i]), 255);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case DARKEN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = Math.min(src[i], dst[i]);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case DIFFERENCE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case EXCLUSION:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = dst[i] + src[i] - (dst[i] * src[i] >> 7);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case FREEZE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = src[i] == 0 ? 0 :
+                                    Math.max(0, 255 - (255 - dst[i]) * (255 - dst[i]) / src[i]);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case GLOW:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = dst[i] == 255 ? 255 :
+                                    Math.min(255, src[i] * src[i] / (255 - dst[i]));
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case GREEN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0],
+                                dst[1],
+                                src[2],
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case HARD_LIGHT:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                src[0] < 128 ? dst[0] * src[0] >> 7 :
+                                    255 - ((255 - src[0]) * (255 - dst[0]) >> 7),
+                                src[1] < 128 ? dst[1] * src[1] >> 7 :
+                                    255 - ((255 - src[1]) * (255 - dst[1]) >> 7),
+                                src[2] < 128 ? dst[2] * src[2] >> 7 :
+                                    255 - ((255 - src[2]) * (255 - dst[2]) >> 7),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case HEAT:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0] == 0 ? 0 : Math.max(0, 255 - (255 - src[0]) * (255 - src[0]) / dst[0]),
+                                dst[1] == 0 ? 0 : Math.max(0, 255 - (255 - src[1]) * (255 - src[1]) / dst[1]),
+                                dst[2] == 0 ? 0 : Math.max(0, 255 - (255 - src[2]) * (255 - src[2]) / dst[2]),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case HUE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            float[] srcHSL = new float[3];
+                            RGBtoHSL(src[0], src[1], src[2], srcHSL);
+                            float[] dstHSL = new float[3];
+                            RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+                            HSLtoRGB(srcHSL[0], dstHSL[1], dstHSL[2], result);
+                            result[3] = Math.min(255, src[3] + dst[3]);
+
+                            return result;
+                        }
+                    };
+                case INVERSE_COLOR_BURN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0] == 0 ? 0 :
+                                    Math.max(0, 255 - (((255 - src[0]) << 8) / dst[0])),
+                                dst[1] == 0 ? 0 :
+                                    Math.max(0, 255 - (((255 - src[1]) << 8) / dst[1])),
+                                dst[2] == 0 ? 0 :
+                                    Math.max(0, 255 - (((255 - src[2]) << 8) / dst[2])),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case INVERSE_COLOR_DODGE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0] == 255 ? 255 :
+                                    Math.min((src[0] << 8) / (255 - dst[0]), 255),
+                                dst[1] == 255 ? 255 :
+                                    Math.min((src[1] << 8) / (255 - dst[1]), 255),
+                                dst[2] == 255 ? 255 :
+                                    Math.min((src[2] << 8) / (255 - dst[2]), 255),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case LIGHTEN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = Math.max(src[i], dst[i]);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case LUMINOSITY:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            float[] srcHSL = new float[3];
+                            RGBtoHSL(src[0], src[1], src[2], srcHSL);
+                            float[] dstHSL = new float[3];
+                            RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+                            HSLtoRGB(dstHSL[0], dstHSL[1], srcHSL[2], result);
+                            result[3] = Math.min(255, src[3] + dst[3]);
+
+                            return result;
+                        }
+                    };
+                case MULTIPLY:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = (src[i] * dst[i]) >> 8;
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case NEGATION:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                255 - Math.abs(255 - dst[0] - src[0]),
+                                255 - Math.abs(255 - dst[1] - src[1]),
+                                255 - Math.abs(255 - dst[2] - src[2]),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case OVERLAY:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            for (int i = 0; i < 3; i++) {
+                                result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
+                                    255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
+                            }
+                            result[3] = Math.min(255, src[3] + dst[3]);
+                            return result;
+                        }
+                    };
+                case RED:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                src[0],
+                                dst[1],
+                                dst[2],
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case REFLECT:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                src[0] == 255 ? 255 : Math.min(255, dst[0] * dst[0] / (255 - src[0])),
+                                src[1] == 255 ? 255 : Math.min(255, dst[1] * dst[1] / (255 - src[1])),
+                                src[2] == 255 ? 255 : Math.min(255, dst[2] * dst[2] / (255 - src[2])),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case SATURATION:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            float[] srcHSL = new float[3];
+                            RGBtoHSL(src[0], src[1], src[2], srcHSL);
+                            float[] dstHSL = new float[3];
+                            RGBtoHSL(dst[0], dst[1], dst[2], dstHSL);
+
+                            HSLtoRGB(dstHSL[0], srcHSL[1], dstHSL[2], result);
+                            result[3] = Math.min(255, src[3] + dst[3]);
+
+                            return result;
+                        }
+                    };
+                case SCREEN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                255 - ((255 - src[0]) * (255 - dst[0]) >> 8),
+                                255 - ((255 - src[1]) * (255 - dst[1]) >> 8),
+                                255 - ((255 - src[2]) * (255 - dst[2]) >> 8),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case SOFT_BURN:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0] + src[0] < 256 ?
+	                                (dst[0] == 255 ? 255 :
+                                        Math.min(255, (src[0] << 7) / (255 - dst[0]))) :
+                                            Math.max(0, 255 - (((255 - dst[0]) << 7) / src[0])),
+                                dst[1] + src[1] < 256 ?
+	                                (dst[1] == 255 ? 255 :
+                                        Math.min(255, (src[1] << 7) / (255 - dst[1]))) :
+                                            Math.max(0, 255 - (((255 - dst[1]) << 7) / src[1])),
+                                dst[2] + src[2] < 256 ?
+	                                (dst[2] == 255 ? 255 :
+                                        Math.min(255, (src[2] << 7) / (255 - dst[2]))) :
+                                            Math.max(0, 255 - (((255 - dst[2]) << 7) / src[2])),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case SOFT_DODGE:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                dst[0] + src[0] < 256 ?
+                                    (src[0] == 255 ? 255 :
+                                        Math.min(255, (dst[0] << 7) / (255 - src[0]))) :
+                                            Math.max(0, 255 - (((255 - src[0]) << 7) / dst[0])),
+                                dst[1] + src[1] < 256 ?
+                                    (src[1] == 255 ? 255 :
+                                        Math.min(255, (dst[1] << 7) / (255 - src[1]))) :
+                                            Math.max(0, 255 - (((255 - src[1]) << 7) / dst[1])),
+                                dst[2] + src[2] < 256 ?
+                                    (src[2] == 255 ? 255 :
+                                        Math.min(255, (dst[2] << 7) / (255 - src[2]))) :
+                                            Math.max(0, 255 - (((255 - src[2]) << 7) / dst[2])),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case SOFT_LIGHT:
+                    break;
+                case STAMP:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                Math.max(0, Math.min(255, dst[0] + 2 * src[0] - 256)),
+                                Math.max(0, Math.min(255, dst[1] + 2 * src[1] - 256)),
+                                Math.max(0, Math.min(255, dst[2] + 2 * src[2] - 256)),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+                case SUBTRACT:
+                    return new Blender() {
+                        @Override
+                        public int[] blend(int[] src, int[] dst, int[] result) {
+                            return new int[] {
+                                Math.max(0, src[0] + dst[0] - 256),
+                                Math.max(0, src[1] + dst[1] - 256),
+                                Math.max(0, src[2] + dst[2] - 256),
+                                Math.min(255, src[3] + dst[3])
+                            };
+                        }
+                    };
+            }
+            throw new IllegalArgumentException("Blender not implement for " +
+                                               composite.getMode().name());
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 7c8ef70..2ff0fc1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,6 +20,7 @@
 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.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Bitmap.Config;
@@ -534,7 +535,8 @@
                 // set the color
                 graphics.setColor(new Color(color, true /*alpha*/));
 
-                Composite composite = PorterDuffXfermode_Delegate.getComposite(mode, 0xFF);
+                Composite composite = PorterDuffUtility.getComposite(
+                        PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
                 if (composite != null) {
                     graphics.setComposite(composite);
                 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index ee90595..4ac376c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -21,11 +21,10 @@
 
 import android.graphics.PorterDuff.Mode;
 
-import java.awt.AlphaComposite;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getComposite;
 import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
 
 /**
@@ -57,7 +56,7 @@
 
     @Override
     public boolean isSupported() {
-        return getAlphaCompositeRule(mMode) != -1;
+        return true;
     }
 
     @Override
@@ -68,7 +67,7 @@
     @Override
     public void applyFilter(Graphics2D g, int width, int height) {
         BufferedImage image = createFilterImage(width, height);
-        g.setComposite(getComposite());
+        g.setComposite(getComposite(mMode, 0xFF));
         g.drawImage(image, 0, 0, null);
     }
 
@@ -101,49 +100,36 @@
         return image;
     }
 
-    private AlphaComposite getComposite() {
-        return AlphaComposite.getInstance(getAlphaCompositeRule(mMode));
-    }
-
     // For filtering the colors, the src image should contain the "color" only for pixel values
     // which are not transparent in the target image. But, we are using a simple rectangular image
-    // completely filled with color. Hence some AlphaComposite rules do not apply as intended.
-    // However, in such cases, they can usually be mapped to some other mode, which produces an
+    // completely filled with color. Hence some Composite rules do not apply as intended. However,
+    // in such cases, they can usually be mapped to some other mode, which produces an
     // equivalent result.
     private Mode getCompatibleMode(Mode mode) {
         Mode m = mode;
+        // Modes that are directly supported:
+        // CLEAR, DST, SRC_IN, DST_IN, DST_OUT, SRC_ATOP, DARKEN, LIGHTEN, MULTIPLY, SCREEN,
+        // ADD, OVERLAY
         switch (mode) {
-            // Modes that are directly supported.
-            case CLEAR:
-            case DST:
-            case SRC_IN:
-            case DST_IN:
-            case DST_OUT:
-            case SRC_ATOP:
-                break;
-            // Modes that can be mapped to one of the supported modes.
-            case SRC:
-                m = Mode.SRC_IN;
-                break;
-            case SRC_OVER:
-                m = Mode.SRC_ATOP;
-                break;
-            case DST_OVER:
-                m = Mode.DST;
-                break;
-            case SRC_OUT:
-                m = Mode.CLEAR;
-                break;
-            case DST_ATOP:
-                m = Mode.DST_IN;
-                break;
-            case XOR:
-                m = Mode.DST_OUT;
-                break;
-            // This mode is not supported, but used by Action Bar Overflow Popup Menus. We map this
-            // to the closest supported mode, to prevent showing excessive warnings to the user.
-            case MULTIPLY:
-                m = Mode.SRC_IN;
+        // Modes that can be mapped to one of the supported modes.
+        case SRC:
+            m = Mode.SRC_IN;
+            break;
+        case SRC_OVER:
+            m = Mode.SRC_ATOP;
+            break;
+        case DST_OVER:
+            m = Mode.DST;
+            break;
+        case SRC_OUT:
+            m = Mode.CLEAR;
+            break;
+        case DST_ATOP:
+            m = Mode.DST_IN;
+            break;
+        case XOR:
+            m = Mode.DST_OUT;
+            break;
         }
         return m;
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index f6c36b6..8825f84 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -16,17 +16,14 @@
 
 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.PorterDuffUtility;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.PorterDuff.Mode;
 
-import java.awt.AlphaComposite;
 import java.awt.Composite;
 
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
 import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
 
 /**
@@ -58,7 +55,7 @@
 
     @Override
     public Composite getComposite(int alpha) {
-        return getComposite(mMode, alpha);
+        return PorterDuffUtility.getComposite(mMode, alpha);
     }
 
     @Override
@@ -72,9 +69,6 @@
         return null;
     }
 
-    public static Composite getComposite(int mode, int alpha) {
-        return getComposite(getPorterDuffMode(mode), alpha);
-    }
 
     // ---- native methods ----
 
@@ -90,17 +84,4 @@
         mMode = getPorterDuffMode(mode);
     }
 
-    private static Composite getComposite(Mode mode, int alpha255) {
-        float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
-        int rule = getAlphaCompositeRule(mode);
-        if (rule >= 0) {
-            return AlphaComposite.getInstance(rule, alpha1);
-        }
-
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
-                null, null /*data*/);
-
-        return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
-    }
 }
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 bc53e93..9588035 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
@@ -19,11 +19,14 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 
+import android.graphics.BlendComposite;
+import android.graphics.BlendComposite.BlendingMode;
 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
@@ -51,46 +54,54 @@
     }
 
     /**
-     * A utility method to convert the porterDuffMode to an int to be used as a rule for {@link
-     * AlphaComposite}. If {@code AlphaComposite} doesn't support the mode, -1 is returned.
+     * A utility method to get the {@link Composite} that represents the filter for the given
+     * PorterDuff mode and the alpha. Defaults to {@link Mode#SRC_OVER} for invalid modes.
      */
-    public static int getAlphaCompositeRule(Mode porterDuffMode) {
-        switch (porterDuffMode) {
+    public static Composite getComposite(Mode mode, int alpha255) {
+        float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
+        switch (mode) {
             case CLEAR:
-                return AlphaComposite.CLEAR;
-            case DARKEN:
-                break;
-            case DST:
-                return AlphaComposite.DST;
-            case DST_ATOP:
-                return AlphaComposite.DST_ATOP;
-            case DST_IN:
-                return AlphaComposite.DST_IN;
-            case DST_OUT:
-                return AlphaComposite.DST_OUT;
-            case DST_OVER:
-                return AlphaComposite.DST_OVER;
-            case LIGHTEN:
-                break;
-            case MULTIPLY:
-                break;
-            case SCREEN:
-                break;
+                return AlphaComposite.getInstance(AlphaComposite.CLEAR, alpha1);
             case SRC:
-                return AlphaComposite.SRC;
-            case SRC_ATOP:
-                return AlphaComposite.SRC_ATOP;
-            case SRC_IN:
-                return AlphaComposite.SRC_IN;
-            case SRC_OUT:
-                return AlphaComposite.SRC_OUT;
+                return AlphaComposite.getInstance(AlphaComposite.SRC, alpha1);
+            case DST:
+                return AlphaComposite.getInstance(AlphaComposite.DST, alpha1);
             case SRC_OVER:
-                return AlphaComposite.SRC_OVER;
+                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
+            case DST_OVER:
+                return AlphaComposite.getInstance(AlphaComposite.DST_OVER, alpha1);
+            case SRC_IN:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha1);
+            case DST_IN:
+                return AlphaComposite.getInstance(AlphaComposite.DST_IN, alpha1);
+            case SRC_OUT:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, alpha1);
+            case DST_OUT:
+                return AlphaComposite.getInstance(AlphaComposite.DST_OUT, alpha1);
+            case SRC_ATOP:
+                return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha1);
+            case DST_ATOP:
+                return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, alpha1);
             case XOR:
-                return AlphaComposite.XOR;
-        }
-        // This is an unsupported mode.
-        return -1;
+                return AlphaComposite.getInstance(AlphaComposite.XOR, alpha1);
+            case DARKEN:
+                return BlendComposite.getInstance(BlendingMode.DARKEN, alpha1);
+            case LIGHTEN:
+                return BlendComposite.getInstance(BlendingMode.LIGHTEN, alpha1);
+            case MULTIPLY:
+                return BlendComposite.getInstance(BlendingMode.MULTIPLY, alpha1);
+            case SCREEN:
+                return BlendComposite.getInstance(BlendingMode.SCREEN, alpha1);
+            case ADD:
+                return BlendComposite.getInstance(BlendingMode.ADD, alpha1);
+            case OVERLAY:
+                return BlendComposite.getInstance(BlendingMode.OVERLAY, alpha1);
+            default:
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                        String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
+                        null, null /*data*/);
 
+                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
+        }
     }
 }