Merge "Fix bugs when system apps reclaim permissions." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 17588c7..90d63b0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -409,6 +409,7 @@
     field public static final int colorControlActivated = 16843818; // 0x101042a
     field public static final int colorControlHighlight = 16843820; // 0x101042c
     field public static final int colorControlNormal = 16843817; // 0x1010429
+    field public static final int colorEdgeEffect = 16843982; // 0x10104ce
     field public static final int colorFocusedHighlight = 16843663; // 0x101038f
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
@@ -28170,6 +28171,7 @@
   public final class DisconnectCause implements android.os.Parcelable {
     ctor public DisconnectCause(int);
     ctor public DisconnectCause(int, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
     ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String, int);
     method public int describeContents();
     method public int getCode();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 53912e1..a19fbd3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3587,7 +3587,7 @@
      * creating new document tasks.
      *
      * <p>This flag is ignored if one of {@link #FLAG_ACTIVITY_NEW_TASK} or
-     * {@link #FLAG_ACTIVITY_NEW_TASK} is not also set.
+     * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} is not also set.
      *
      * <p>See
      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index 5402e75..ed51402 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -29,6 +29,7 @@
     private final String mDescriptor;
     private final String mLabel;
     private final String mCollection;
+    private final int mPriority;
 
     public static final Parcelable.Creator<KeyboardLayout> CREATOR =
             new Parcelable.Creator<KeyboardLayout>() {
@@ -40,16 +41,18 @@
         }
     };
 
-    public KeyboardLayout(String descriptor, String label, String collection) {
+    public KeyboardLayout(String descriptor, String label, String collection, int priority) {
         mDescriptor = descriptor;
         mLabel = label;
         mCollection = collection;
+        mPriority = priority;
     }
 
     private KeyboardLayout(Parcel source) {
         mDescriptor = source.readString();
         mLabel = source.readString();
         mCollection = source.readString();
+        mPriority = source.readInt();
     }
 
     /**
@@ -90,11 +93,17 @@
         dest.writeString(mDescriptor);
         dest.writeString(mLabel);
         dest.writeString(mCollection);
+        dest.writeInt(mPriority);
     }
 
     @Override
     public int compareTo(KeyboardLayout another) {
-        int result = mLabel.compareToIgnoreCase(another.mLabel);
+        // Note that these arguments are intentionally flipped since you want higher priority
+        // keyboards to be listed before lower priority keyboards.
+        int result = Integer.compare(another.mPriority, mPriority);
+        if (result == 0) {
+            result = mLabel.compareToIgnoreCase(another.mLabel);
+        }
         if (result == 0) {
             result = mCollection.compareToIgnoreCase(another.mCollection);
         }
@@ -108,4 +117,4 @@
         }
         return mLabel + " - " + mCollection;
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 033b99a..6925756 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -120,7 +120,7 @@
         final TypedArray a = context.obtainStyledAttributes(
                 com.android.internal.R.styleable.EdgeEffect);
         final int themeColor = a.getColor(
-                com.android.internal.R.styleable.EdgeEffect_colorPrimary, 0xff666666);
+                com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666);
         a.recycle();
         mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
         mPaint.setStyle(Paint.Style.FILL);
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 0460282..56f126c 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -557,7 +557,7 @@
             if (mIsOnInnerCircle && hour == 0) {
                 // Inner circle is 1 through 12.
                 hour = 12;
-            } else if (hour != 0) {
+            } else if (!mIsOnInnerCircle && hour != 0) {
                 // Outer circle is 13 through 23 and 0.
                 hour += 12;
             }
diff --git a/core/res/res/values-sw600dp/dimens_material.xml b/core/res/res/values-sw600dp/dimens_material.xml
index e0aac38..2547b7a 100644
--- a/core/res/res/values-sw600dp/dimens_material.xml
+++ b/core/res/res/values-sw600dp/dimens_material.xml
@@ -16,9 +16,9 @@
 <resources>
 
     <!-- Use the default title sizes on tablets. -->
-    <dimen name="text_size_title_material_toolbar">@dimen/text_size_title_material</dimen>
+    <dimen name="text_size_title_material_toolbar">20dp</dimen>
     <!-- Use the default subtitle sizes on tablets. -->
-    <dimen name="text_size_subtitle_material_toolbar">@dimen/text_size_subhead_material</dimen>
+    <dimen name="text_size_subtitle_material_toolbar">16dp</dimen>
     <!-- Default height of an action bar. -->
     <dimen name="action_bar_default_height_material">64dp</dimen>
     <!-- Default padding of an action bar. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 85c1072..90217e5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1020,6 +1020,9 @@
         <!-- The color applied to framework switch thumbs in their normal state. -->
         <attr name="colorSwitchThumbNormal" format="color" />
 
+        <!-- The color applied to the edge effect on scrolling containers. -->
+        <attr name="colorEdgeEffect" format="color" />
+
         <!-- =================== -->
         <!-- Lighting properties -->
         <!-- =================== -->
@@ -7435,7 +7438,7 @@
 
     <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
     <declare-styleable name="EdgeEffect">
-        <attr name="colorPrimary" />
+        <attr name="colorEdgeEffect" />
     </declare-styleable>
 
     <!-- Use <code>tv-input</code> as the root tag of the XML resource that describes a
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index ac5890a..9836757 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -42,8 +42,8 @@
     <dimen name="text_size_headline_material">24sp</dimen>
     <dimen name="text_size_title_material">20sp</dimen>
     <dimen name="text_size_subhead_material">16sp</dimen>
-    <dimen name="text_size_title_material_toolbar">@dimen/text_size_title_material</dimen>
-    <dimen name="text_size_subtitle_material_toolbar">@dimen/text_size_subhead_material</dimen>
+    <dimen name="text_size_title_material_toolbar">20dp</dimen>
+    <dimen name="text_size_subtitle_material_toolbar">16dp</dimen>
     <dimen name="text_size_menu_material">16sp</dimen>
     <dimen name="text_size_body_2_material">14sp</dimen>
     <dimen name="text_size_body_1_material">14sp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index bbd40a1..a794b62 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2291,6 +2291,7 @@
     <public type="attr" name="strokeAlpha" id="0x010104cb" />
     <public type="attr" name="fillAlpha" id="0x010104cc" />
     <public type="attr" name="windowActivityTransitions" id="0x010104cd" />
+    <public type="attr" name="colorEdgeEffect" id="0x010104ce" />
 
     <public type="id" name="mask" id="0x0102002e" />
     <public type="id" name="statusBarBackground" id="0x0102002f" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 0577659..3a268a3 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -59,6 +59,7 @@
         <item name="colorControlNormal">@color/legacy_control_normal</item>
         <item name="colorControlHighlight">@color/legacy_button_pressed</item>
         <item name="colorButtonNormal">@color/legacy_button_normal</item>
+        <item name="colorEdgeEffect">?attr/colorPrimary</item>
 
         <item name="disabledAlpha">0.5</item>
         <item name="backgroundDimAmount">0.6</item>
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index 14853b8..208db97 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -81,6 +81,7 @@
         <item name="colorControlNormal">@color/holo_control_normal</item>
         <item name="colorControlHighlight">@color/holo_button_pressed</item>
         <item name="colorButtonNormal">@color/holo_button_normal</item>
+        <item name="colorEdgeEffect">?attr/colorPrimary</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Holo</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 615ef52..ebf0571 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -375,6 +375,7 @@
         <item name="colorPrimaryDark">@color/material_blue_grey_900</item>
         <item name="colorPrimary">@color/material_blue_grey_800</item>
         <item name="colorAccent">@color/material_deep_teal_200</item>
+        <item name="colorEdgeEffect">?attr/colorPrimary</item>
 
         <item name="colorControlNormal">?attr/textColorSecondary</item>
         <item name="colorControlActivated">?attr/colorAccent</item>
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index c65efe4..ac5eca08 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1521,6 +1521,8 @@
 
     bool getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const;
 
+    bool getResourceFlags(uint32_t resID, uint32_t* outFlags) const;
+
     /**
      * Retrieve the value of a resource.  If the resource is found, returns a
      * value >= 0 indicating the table it is in (for use with
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 690b1d6..8cef137 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -5393,6 +5393,44 @@
     return NULL;
 }
 
+bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
+    if (mError != NO_ERROR) {
+        return false;
+    }
+
+    const ssize_t p = getResourcePackageIndex(resID);
+    const int t = Res_GETTYPE(resID);
+    const int e = Res_GETENTRY(resID);
+
+    if (p < 0) {
+        if (Res_GETPACKAGE(resID)+1 == 0) {
+            ALOGW("No package identifier when getting flags for resource number 0x%08x", resID);
+        } else {
+            ALOGW("No known package when getting flags for resource number 0x%08x", resID);
+        }
+        return false;
+    }
+    if (t < 0) {
+        ALOGW("No type identifier when getting flags for resource number 0x%08x", resID);
+        return false;
+    }
+
+    const PackageGroup* const grp = mPackageGroups[p];
+    if (grp == NULL) {
+        ALOGW("Bad identifier when getting flags for resource number 0x%08x", resID);
+        return false;
+    }
+
+    Entry entry;
+    status_t err = getEntry(grp, t, e, NULL, &entry);
+    if (err != NO_ERROR) {
+        return false;
+    }
+
+    *outFlags = entry.specFlags;
+    return true;
+}
+
 status_t ResTable::getEntry(
         const PackageGroup* packageGroup, int typeIndex, int entryIndex,
         const ResTable_config* config,
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
index fa4a9fe0..036e468 100755
--- a/libs/androidfw/tests/data/basic/build
+++ b/libs/androidfw/tests/data/basic/build
@@ -1,6 +1,8 @@
 #!/bin/bash
 
-aapt package -M AndroidManifest.xml -S res --split fr,de -F bundle.apk -f && \
+PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split fr,de -F bundle.apk -f && \
 unzip bundle.apk resources.arsc && \
 mv resources.arsc basic.arsc && \
 xxd -i basic.arsc > basic_arsc.h && \
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 29fec41..6d3f976 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,7 +26,7 @@
     android:fitsSystemWindows="true"
     android:descendantFocusability="afterDescendants">
 
-    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+    <com.android.systemui.statusbar.BackDropView
             android:id="@+id/backdrop"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -41,9 +41,9 @@
                    android:layout_height="match_parent"
                    android:scaleType="centerCrop"
                    android:visibility="invisible" />
-    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+    </com.android.systemui.statusbar.BackDropView>
 
-    <View android:id="@+id/scrim_behind"
+    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
@@ -80,7 +80,7 @@
             android:visibility="gone" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
-    <View android:id="@+id/scrim_in_front"
+    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java b/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java
new file mode 100644
index 0000000..f1eb9fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BackDropView.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * A view who contains media artwork.
+ */
+public class BackDropView extends FrameLayout
+{
+    private Runnable mOnVisibilityChangedRunnable;
+
+    public BackDropView(Context context) {
+        super(context);
+    }
+
+    public BackDropView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BackDropView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public BackDropView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (changedView == this && mOnVisibilityChangedRunnable != null) {
+            mOnVisibilityChangedRunnable.run();
+        }
+    }
+
+    public void setOnVisibilityChangedRunnable(Runnable runnable) {
+        mOnVisibilityChangedRunnable = runnable;
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
new file mode 100644
index 0000000..2353425
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+/**
+ * A view which can draw a scrim
+ */
+public class ScrimView extends View
+{
+    private int mScrimColor;
+    private boolean mIsEmpty;
+    private boolean mDrawAsSrc;
+    private float mViewAlpha = 1.0f;
+    private ValueAnimator mAlphaAnimator;
+    private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
+            = new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            mViewAlpha = (float) animation.getAnimatedValue();
+            invalidate();
+        }
+    };
+    private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mAlphaAnimator = null;
+        }
+    };
+
+    public ScrimView(Context context) {
+        this(context, null);
+    }
+
+    public ScrimView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ScrimView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mDrawAsSrc || !mIsEmpty) {
+            PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER;
+            int color = mScrimColor;
+            color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color),
+                    Color.green(color), Color.blue(color));
+            canvas.drawColor(color, mode);
+        }
+    }
+
+    public void setDrawAsSrc(boolean asSrc) {
+        mDrawAsSrc = asSrc;
+        invalidate();
+    }
+
+    public void setScrimColor(int color) {
+        if (color != mScrimColor) {
+            mIsEmpty = Color.alpha(color) == 0;
+            mScrimColor = color;
+            invalidate();
+        }
+    }
+
+    public int getScrimColor() {
+        return mScrimColor;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    public void setViewAlpha(float alpha) {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+        mViewAlpha = alpha;
+        invalidate();
+    }
+
+    public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+        mAlphaAnimator = ValueAnimator.ofFloat(mViewAlpha, alpha);
+        mAlphaAnimator.addUpdateListener(mAlphaUpdateListener);
+        mAlphaAnimator.addListener(mClearAnimatorListener);
+        mAlphaAnimator.setInterpolator(interpolator);
+        mAlphaAnimator.setDuration(durationOut);
+        mAlphaAnimator.start();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 624fea5..5e9a64c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -51,7 +51,9 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.Xfermode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
@@ -120,6 +122,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DismissView;
@@ -131,6 +134,7 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.NotificationOverflowContainer;
+import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.SpeedBumpView;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -431,8 +435,10 @@
     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
 
-    private FrameLayout mBackdrop;
+    private BackDropView mBackdrop;
     private ImageView mBackdropFront, mBackdropBack;
+    private PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+    private PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
 
     private MediaSessionManager mMediaSessionManager;
     private MediaController mMediaController;
@@ -723,8 +729,14 @@
         mStackScroller.setDismissView(mDismissView);
         mExpandedContents = mStackScroller;
 
-        mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
-                mStatusBarWindow.findViewById(R.id.scrim_in_front));
+        mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
+        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
+        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
+
+        ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
+        ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
+        mScrimController = new ScrimController(scrimBehind, scrimInFront);
+        mScrimController.setBackDropView(mBackdrop);
         mStatusBarView.setScrimController(mScrimController);
 
         mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
@@ -863,10 +875,6 @@
             });
         }
 
-        mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop);
-        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
-        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
-
         // User info. Trigger first load.
         mHeader.setUserInfoController(mUserInfoController);
         mKeyguardStatusBar.setUserInfoController(mUserInfoController);
@@ -1852,7 +1860,9 @@
             }
             if (metaDataChanged) {
                 if (mBackdropBack.getDrawable() != null) {
-                    mBackdropFront.setImageDrawable(mBackdropBack.getDrawable());
+                    Drawable drawable = mBackdropBack.getDrawable();
+                    mBackdropFront.setImageDrawable(drawable);
+                    mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
                     mBackdropFront.setAlpha(1f);
                     mBackdropFront.setVisibility(View.VISIBLE);
                 } else {
@@ -1867,6 +1877,7 @@
                 } else {
                     mBackdropBack.setImageBitmap(artworkBitmap);
                 }
+                mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
 
                 if (mBackdropFront.getVisibility() == View.VISIBLE) {
                     if (DEBUG_MEDIA) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index ddb03e7..a502470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -21,7 +21,6 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -31,6 +30,8 @@
 
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.statusbar.BackDropView;
+import com.android.systemui.statusbar.ScrimView;
 
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
@@ -48,8 +49,8 @@
     private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
     private static final int TAG_KEY_ANIM = R.id.scrim;
 
-    private final View mScrimBehind;
-    private final View mScrimInFront;
+    private final ScrimView mScrimBehind;
+    private final ScrimView mScrimInFront;
     private final UnlockMethodCache mUnlockMethodCache;
     private final DozeParameters mDozeParameters;
 
@@ -70,8 +71,9 @@
     private long mPulseEndTime;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
     private final Interpolator mLinearOutSlowInInterpolator;
+    private BackDropView mBackDropView;
 
-    public ScrimController(View scrimBehind, View scrimInFront) {
+    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
         final Context context = scrimBehind.getContext();
@@ -230,17 +232,17 @@
         }
     }
 
-    private void setScrimColor(View scrim, float alpha) {
+    private void setScrimColor(ScrimView scrim, float alpha) {
         int color = Color.argb((int) (alpha * 255), 0, 0, 0);
         if (mAnimateChange) {
             startScrimAnimation(scrim, color);
         } else {
-            scrim.setBackgroundColor(color);
+            scrim.setScrimColor(color);
         }
     }
 
-    private void startScrimAnimation(final View scrim, int targetColor) {
-        int current = getBackgroundAlpha(scrim);
+    private void startScrimAnimation(final ScrimView scrim, int targetColor) {
+        int current = Color.alpha(scrim.getScrimColor());
         int target = Color.alpha(targetColor);
         if (current == targetColor) {
             return;
@@ -254,7 +256,7 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 int value = (int) animation.getAnimatedValue();
-                scrim.setBackgroundColor(Color.argb(value, 0, 0, 0));
+                scrim.setScrimColor(Color.argb(value, 0, 0, 0));
             }
         });
         anim.setInterpolator(mAnimateKeyguardFadingOut
@@ -278,15 +280,6 @@
         mAnimationStarted = true;
     }
 
-    private int getBackgroundAlpha(View scrim) {
-        if (scrim.getBackground() instanceof ColorDrawable) {
-            ColorDrawable drawable = (ColorDrawable) scrim.getBackground();
-            return Color.alpha(drawable.getColor());
-        } else {
-            return 0;
-        }
-    }
-
     @Override
     public boolean onPreDraw() {
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
@@ -349,4 +342,20 @@
             mPulseEndTime = 0;
         }
     };
+
+    public void setBackDropView(BackDropView backDropView) {
+        mBackDropView = backDropView;
+        mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
+            @Override
+            public void run() {
+                updateScrimBehindDrawingMode();
+            }
+        });
+        updateScrimBehindDrawingMode();
+    }
+
+    private void updateScrimBehindDrawingMode() {
+        boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE;
+        mScrimBehind.setDrawAsSrc(asSrc);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 78554525..89ce257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -20,12 +20,17 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.os.IBinder;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
@@ -45,11 +50,14 @@
     private View mBrightnessMirror;
 
     PhoneStatusBar mService;
+    private final Paint mTransparentSrcPaint = new Paint();
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setMotionEventSplittingEnabled(false);
-        setWillNotDraw(!DEBUG);
+        setWillNotDraw(false);
+        mTransparentSrcPaint.setColor(0);
+        mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
     }
 
     @Override
@@ -93,6 +101,15 @@
         if (root != null) {
             root.setDrawDuringWindowsAnimating(true);
         }
+
+        // We need to ensure that our window doesn't suffer from overdraw which would normally
+        // occur if our window is translucent. Since we are drawing the whole window anyway with
+        // the scrim, we don't need the window to be cleared in the beginning.
+        IBinder windowToken = getWindowToken();
+        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
+        lp.token = windowToken;
+        setLayoutParams(lp);
+        WindowManagerGlobal.getInstance().changeCanvasOpacity(windowToken, true);
     }
 
     @Override
@@ -182,6 +199,24 @@
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
+        // We need to ensure that our window is always drawn fully even when we have paddings,
+        // since we simulate it to be opaque.
+        int paddedBottom = getHeight() - getPaddingBottom();
+        int paddedRight = getWidth() - getPaddingRight();
+        if (getPaddingTop() != 0) {
+            canvas.drawRect(0, 0, getWidth(), getPaddingTop(), mTransparentSrcPaint);
+        }
+        if (getPaddingBottom() != 0) {
+            canvas.drawRect(0, paddedBottom, getWidth(), getHeight(), mTransparentSrcPaint);
+        }
+        if (getPaddingLeft() != 0) {
+            canvas.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom,
+                    mTransparentSrcPaint);
+        }
+        if (getPaddingRight() != 0) {
+            canvas.drawRect(paddedRight, getPaddingTop(), getWidth(), paddedBottom,
+                    mTransparentSrcPaint);
+        }
         if (DEBUG) {
             Paint pt = new Paint();
             pt.setColor(0x80FFFF00);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 7bd2e5c..895af62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.widget.FrameLayout;
 
@@ -33,26 +33,26 @@
     public long TRANSITION_DURATION_OUT = 150;
     public long TRANSITION_DURATION_IN = 200;
 
-    private final View mScrimBehind;
+    private final ScrimView mScrimBehind;
     private final View mBrightnessMirror;
     private final View mPanelHolder;
     private final int[] mInt2Cache = new int[2];
 
     public BrightnessMirrorController(StatusBarWindowView statusBarWindow) {
-        mScrimBehind = statusBarWindow.findViewById(R.id.scrim_behind);
+        mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
         mPanelHolder = statusBarWindow.findViewById(R.id.panel_holder);
     }
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
-        outAnimation(mScrimBehind.animate());
+        mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT);
         outAnimation(mPanelHolder.animate())
                 .withLayer();
     }
 
     public void hideMirror() {
-        inAnimation(mScrimBehind.animate());
+        mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN);
         inAnimation(mPanelHolder.animate())
                 .withLayer()
                 .withEndAction(new Runnable() {
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 63178eb..07fe7ba 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -71,7 +71,7 @@
     public static final String KEY_PROXY = "keyProxy";
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
-    private Uri mPacUrl;
+    private Uri mPacUrl = Uri.EMPTY;
 
     private AlarmManager mAlarmManager;
     @GuardedBy("mProxyLock")
@@ -175,7 +175,7 @@
         } else {
             getAlarmManager().cancel(mPacRefreshIntent);
             synchronized (mProxyLock) {
-                mPacUrl = null;
+                mPacUrl = Uri.EMPTY;
                 mCurrentPac = null;
                 if (mProxyService != null) {
                     try {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 81b579d..7f89947 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -806,8 +807,8 @@
         final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
+                    String collection, int keyboardLayoutResId, int priority) {
                 availableKeyboardLayouts.add(descriptor);
             }
         });
@@ -840,9 +841,9 @@
         final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, String collection, int keyboardLayoutResId) {
-                list.add(new KeyboardLayout(descriptor, label, collection));
+            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
+                    String collection, int keyboardLayoutResId, int priority) {
+                list.add(new KeyboardLayout(descriptor, label, collection, priority));
             }
         });
         return list.toArray(new KeyboardLayout[list.size()]);
@@ -857,9 +858,9 @@
         final KeyboardLayout[] result = new KeyboardLayout[1];
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, String collection, int keyboardLayoutResId) {
-                result[0] = new KeyboardLayout(descriptor, label, collection);
+            public void visitKeyboardLayout(Resources resources, String descriptor,
+                    String label, String collection, int keyboardLayoutResId, int priority) {
+                result[0] = new KeyboardLayout(descriptor, label, collection, priority);
             }
         });
         if (result[0] == null) {
@@ -874,7 +875,9 @@
         Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
         for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA)) {
-            visitKeyboardLayoutsInPackage(pm, resolveInfo.activityInfo, null, visitor);
+            final ActivityInfo activityInfo = resolveInfo.activityInfo;
+            final int priority = resolveInfo.priority;
+            visitKeyboardLayoutsInPackage(pm, activityInfo, null, priority, visitor);
         }
     }
 
@@ -887,14 +890,14 @@
                 ActivityInfo receiver = pm.getReceiverInfo(
                         new ComponentName(d.packageName, d.receiverName),
                         PackageManager.GET_META_DATA);
-                visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, visitor);
+                visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, 0, visitor);
             } catch (NameNotFoundException ex) {
             }
         }
     }
 
     private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
-            String keyboardName, KeyboardLayoutVisitor visitor) {
+            String keyboardName, int requestedPriority, KeyboardLayoutVisitor visitor) {
         Bundle metaData = receiver.metaData;
         if (metaData == null) {
             return;
@@ -909,6 +912,12 @@
 
         CharSequence receiverLabel = receiver.loadLabel(pm);
         String collection = receiverLabel != null ? receiverLabel.toString() : "";
+        int priority;
+        if ((receiver.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            priority = requestedPriority;
+        } else {
+            priority = 0;
+        }
 
         try {
             Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
@@ -943,7 +952,7 @@
                                         receiver.packageName, receiver.name, name);
                                 if (keyboardName == null || name.equals(keyboardName)) {
                                     visitor.visitKeyboardLayout(resources, descriptor,
-                                            label, collection, keyboardLayoutResId);
+                                            label, collection, keyboardLayoutResId, priority);
                                 }
                             }
                         } finally {
@@ -1550,8 +1559,8 @@
         final String[] result = new String[2];
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources,
-                    String descriptor, String label, String collection, int keyboardLayoutResId) {
+            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
+                    String collection, int keyboardLayoutResId, int priority) {
                 try {
                     result[0] = descriptor;
                     result[1] = Streams.readFully(new InputStreamReader(
@@ -1699,8 +1708,8 @@
     }
 
     private interface KeyboardLayoutVisitor {
-        void visitKeyboardLayout(Resources resources,
-                String descriptor, String label, String collection, int keyboardLayoutResId);
+        void visitKeyboardLayout(Resources resources, String descriptor, String label,
+                String collection, int keyboardLayoutResId, int priority);
     }
 
     private final class InputDevicesChangedListenerRecord implements DeathRecipient {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index b0b6fb9..a71161a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -161,7 +161,7 @@
 
         /**
          * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
-         * by {@link android.telecomm.DisconnectCause}.
+         * by {@link android.telecom.DisconnectCause}.
          */
         public DisconnectCause getDisconnectCause() {
             return mDisconnectCause;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index cae115d..9be0138 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -85,6 +85,17 @@
 
     /**
      * Creates a new DisconnectCause.
+     * @param label The localized label to show to the user to explain the disconnect.
+     * @param code The code for the disconnect cause.
+     * @param description The localized description to show to the user to explain the disconnect.
+     * @param reason The reason for the disconnect.
+     */
+    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason) {
+        this(code, label, description, reason, ToneGenerator.TONE_UNKNOWN);
+    }
+
+    /**
+     * Creates a new DisconnectCause.
      *
      * @param code The code for the disconnect cause.
      * @param label The localized label to show to the user to explain the disconnect.
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index afec5ed..d605202 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -781,12 +781,18 @@
     if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
             bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) {
         return UNKNOWN_ERROR;
+    } else {
+        const XMLNode::attribute_entry* attr = root->getAttribute(
+                String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName"));
+        if (attr != NULL) {
+            bundle->setVersionName(strdup(String8(attr->string).string()));
+        }
     }
     
+    sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
     if (bundle->getMinSdkVersion() != NULL
             || bundle->getTargetSdkVersion() != NULL
             || bundle->getMaxSdkVersion() != NULL) {
-        sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
         if (vers == NULL) {
             vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
             root->insertChildAt(vers, 0);
@@ -806,6 +812,14 @@
         }
     }
 
+    if (vers != NULL) {
+        const XMLNode::attribute_entry* attr = vers->getAttribute(
+                String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion"));
+        if (attr != NULL) {
+            bundle->setMinSdkVersion(strdup(String8(attr->string).string()));
+        }
+    }
+
     if (bundle->getPlatformBuildVersionCode() != "") {
         if (!addTagAttribute(root, "", "platformBuildVersionCode",
                     bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
@@ -973,8 +987,8 @@
 static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) {
     int32_t cookie = getPlatformAssetCookie(assets);
     if (cookie == 0) {
-        fprintf(stderr, "ERROR: Platform package not found\n");
-        return UNKNOWN_ERROR;
+        // No platform was loaded.
+        return NO_ERROR;
     }
 
     ResXMLTree tree;
@@ -1500,6 +1514,10 @@
         return err;
     }
 
+    if (table.modifyForCompat(bundle) != NO_ERROR) {
+        return UNKNOWN_ERROR;
+    }
+
     //block.restart();
     //printXMLBlock(&block);
 
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 8341de6..8c9efc9 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -12,6 +12,7 @@
 
 #include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
+#include <utils/TypeHelpers.h>
 #include <stdarg.h>
 
 #define NOISY(x) //x
@@ -3287,6 +3288,18 @@
     }
 }
 
+ResourceTable::Entry::Entry(const Entry& entry)
+    : RefBase()
+    , mName(entry.mName)
+    , mParent(entry.mParent)
+    , mType(entry.mType)
+    , mItem(entry.mItem)
+    , mItemFormat(entry.mItemFormat)
+    , mBag(entry.mBag)
+    , mNameIndex(entry.mNameIndex)
+    , mParentId(entry.mParentId)
+    , mPos(entry.mPos) {}
+
 status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
 {
     if (mType == TYPE_BAG) {
@@ -3372,6 +3385,17 @@
     return NO_ERROR;
 }
 
+status_t ResourceTable::Entry::removeFromBag(const String16& key) {
+    if (mType != Entry::TYPE_BAG) {
+        return NO_ERROR;
+    }
+
+    if (mBag.removeItem(key) >= 0) {
+        return NO_ERROR;
+    }
+    return UNKNOWN_ERROR;
+}
+
 status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
 {
     status_t err = makeItABag(sourcePos);
@@ -4113,3 +4137,175 @@
     }
     return res;
 }
+
+/**
+ * Returns true if the given attribute ID comes from
+ * a platform version from or after L.
+ */
+bool ResourceTable::isAttributeFromL(uint32_t attrId) {
+    const uint32_t baseAttrId = 0x010103f7;
+    if ((attrId & 0xffff0000) != (baseAttrId & 0xffff0000)) {
+        return false;
+    }
+
+    uint32_t specFlags;
+    if (!mAssets->getIncludedResources().getResourceFlags(attrId, &specFlags)) {
+        return false;
+    }
+
+    return (specFlags & ResTable_typeSpec::SPEC_PUBLIC) != 0 &&
+        (attrId & 0x0000ffff) >= (baseAttrId & 0x0000ffff);
+}
+
+/**
+ * Modifies the entries in the resource table to account for compatibility
+ * issues with older versions of Android.
+ *
+ * This primarily handles the issue of private/public attribute clashes
+ * in framework resources.
+ *
+ * AAPT has traditionally assigned resource IDs to public attributes,
+ * and then followed those public definitions with private attributes.
+ *
+ * --- PUBLIC ---
+ * | 0x01010234 | attr/color
+ * | 0x01010235 | attr/background
+ *
+ * --- PRIVATE ---
+ * | 0x01010236 | attr/secret
+ * | 0x01010237 | attr/shhh
+ *
+ * Each release, when attributes are added, they take the place of the private
+ * attributes and the private attributes are shifted down again.
+ *
+ * --- PUBLIC ---
+ * | 0x01010234 | attr/color
+ * | 0x01010235 | attr/background
+ * | 0x01010236 | attr/shinyNewAttr
+ * | 0x01010237 | attr/highlyValuedFeature
+ *
+ * --- PRIVATE ---
+ * | 0x01010238 | attr/secret
+ * | 0x01010239 | attr/shhh
+ *
+ * Platform code may look for private attributes set in a theme. If an app
+ * compiled against a newer version of the platform uses a new public
+ * attribute that happens to have the same ID as the private attribute
+ * the older platform is expecting, then the behavior is undefined.
+ *
+ * We get around this by detecting any newly defined attributes (in L),
+ * copy the resource into a -v21 qualified resource, and delete the
+ * attribute from the original resource. This ensures that older platforms
+ * don't see the new attribute, but when running on L+ platforms, the
+ * attribute will be respected.
+ */
+status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
+    if (bundle->getMinSdkVersion() != NULL) {
+        // If this app will only ever run on L+ devices,
+        // we don't need to do any compatibility work.
+
+        if (String8("L") == bundle->getMinSdkVersion()) {
+            // Code-name for the v21 release.
+            return NO_ERROR;
+        }
+
+        const int minSdk = atoi(bundle->getMinSdkVersion());
+        if (minSdk >= SDK_L) {
+            return NO_ERROR;
+        }
+    }
+
+    const String16 attr16("attr");
+
+    const size_t packageCount = mOrderedPackages.size();
+    for (size_t pi = 0; pi < packageCount; pi++) {
+        sp<Package> p = mOrderedPackages.itemAt(pi);
+        if (p == NULL || p->getTypes().size() == 0) {
+            // Empty, skip!
+            continue;
+        }
+
+        const size_t typeCount = p->getOrderedTypes().size();
+        for (size_t ti = 0; ti < typeCount; ti++) {
+            sp<Type> t = p->getOrderedTypes().itemAt(ti);
+            if (t == NULL) {
+                continue;
+            }
+
+            const size_t configCount = t->getOrderedConfigs().size();
+            for (size_t ci = 0; ci < configCount; ci++) {
+                sp<ConfigList> c = t->getOrderedConfigs().itemAt(ci);
+                if (c == NULL) {
+                    continue;
+                }
+
+                Vector<key_value_pair_t<ConfigDescription, sp<Entry> > > entriesToAdd;
+                const DefaultKeyedVector<ConfigDescription, sp<Entry> >& entries =
+                        c->getEntries();
+                const size_t entryCount = entries.size();
+                for (size_t ei = 0; ei < entryCount; ei++) {
+                    sp<Entry> e = entries.valueAt(ei);
+                    if (e == NULL || e->getType() != Entry::TYPE_BAG) {
+                        continue;
+                    }
+
+                    const ConfigDescription& config = entries.keyAt(ei);
+                    if (config.sdkVersion >= SDK_L) {
+                        // We don't need to do anything if the resource is
+                        // already qualified for version 21 or higher.
+                        continue;
+                    }
+
+                    Vector<String16> attributesToRemove;
+                    const KeyedVector<String16, Item>& bag = e->getBag();
+                    const size_t bagCount = bag.size();
+                    for (size_t bi = 0; bi < bagCount; bi++) {
+                        const Item& item = bag.valueAt(bi);
+                        const uint32_t attrId = getResId(bag.keyAt(bi), &attr16);
+                        if (isAttributeFromL(attrId)) {
+                            attributesToRemove.add(bag.keyAt(bi));
+                        }
+                    }
+
+                    if (attributesToRemove.isEmpty()) {
+                        continue;
+                    }
+
+                    // Duplicate the entry under the same configuration
+                    // but with sdkVersion == SDK_L.
+                    ConfigDescription newConfig(config);
+                    newConfig.sdkVersion = SDK_L;
+                    entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >(
+                            newConfig, new Entry(*e)));
+
+                    // Remove the attribute from the original.
+                    for (size_t i = 0; i < attributesToRemove.size(); i++) {
+                        e->removeFromBag(attributesToRemove[i]);
+                    }
+                }
+
+                const size_t entriesToAddCount = entriesToAdd.size();
+                for (size_t i = 0; i < entriesToAddCount; i++) {
+                    if (entries.indexOfKey(entriesToAdd[i].key) >= 0) {
+                        // An entry already exists for this config.
+                        // That means that any attributes that were
+                        // defined in L in the original bag will be overriden
+                        // anyways on L devices, so we do nothing.
+                        continue;
+                    }
+
+                    entriesToAdd[i].value->getPos()
+                            .printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
+                                    SDK_L,
+                                    String8(p->getName()).string(),
+                                    String8(t->getName()).string(),
+                                    String8(entriesToAdd[i].value->getName()).string(),
+                                    entriesToAdd[i].key.toString().string());
+
+                    c->addEntry(entriesToAdd[i].key, entriesToAdd[i].value);
+                }
+            }
+        }
+    }
+    return NO_ERROR;
+}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 3721de4..025a868 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -165,6 +165,8 @@
     size_t numLocalResources() const;
     bool hasResources() const;
 
+    status_t modifyForCompat(const Bundle* bundle);
+
     sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
             const bool isBase);
 
@@ -281,6 +283,9 @@
             : mName(name), mType(TYPE_UNKNOWN),
               mItemFormat(ResTable_map::TYPE_ANY), mNameIndex(-1), mPos(pos)
         { }
+
+        Entry(const Entry& entry);
+
         virtual ~Entry() { }
 
         enum type {
@@ -311,6 +316,8 @@
                           bool replace=false, bool isId = false,
                           int32_t format = ResTable_map::TYPE_ANY);
 
+        status_t removeFromBag(const String16& key);
+
         // Index of the entry's name string in the key pool.
         int32_t getNameIndex() const { return mNameIndex; }
         void setNameIndex(int32_t index) { mNameIndex = index; }
@@ -523,6 +530,7 @@
     const Item* getItem(uint32_t resID, uint32_t attrID) const;
     bool getItemValue(uint32_t resID, uint32_t attrID,
                       Res_value* outValue);
+    bool isAttributeFromL(uint32_t attrId);
 
 
     String16 mAssetsPackage;