Merge "Separate power warnings UI from state calculation."
diff --git a/api/current.txt b/api/current.txt
index f1aa8bf..e15c1d7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9926,6 +9926,7 @@
     method public void cubicTo(float, float, float, float, float, float);
     method public android.graphics.Path.FillType getFillType();
     method public void incReserve(int);
+    method public boolean isConvex();
     method public boolean isEmpty();
     method public boolean isInverseFillType();
     method public boolean isRect(android.graphics.RectF);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index da6ae56..5c27072 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1625,7 +1625,7 @@
 
             String locale = null;
             if (mConfiguration.locale != null) {
-                locale = localeToLanguageTag(mConfiguration.locale);
+                locale = adjustLanguageTag(localeToLanguageTag(mConfiguration.locale));
             }
             int width, height;
             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
@@ -1713,6 +1713,41 @@
     }
 
     /**
+     * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
+     * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
+     *
+     * All released versions of android prior to "L" used the deprecated language
+     * tags, so we will need to support them for backwards compatibility.
+     *
+     * Note that this conversion needs to take place *after* the call to
+     * {@code toLanguageTag} because that will convert all the deprecated codes to
+     * the new ones, even if they're set manually.
+     */
+    private static String adjustLanguageTag(String languageTag) {
+        final int separator = languageTag.indexOf('-');
+        final String language;
+        final String remainder;
+
+        if (separator == -1) {
+            language = languageTag;
+            remainder = "";
+        } else {
+            language = languageTag.substring(0, separator);
+            remainder = languageTag.substring(separator);
+        }
+
+        if ("id".equals(language)) {
+            return "in" + remainder;
+        } else if ("yi".equals(language)) {
+            return "ji" + remainder;
+        } else if ("he".equals(language)) {
+            return "iw" + remainder;
+        } else {
+            return languageTag;
+        }
+    }
+
+    /**
      * Update the system resources configuration if they have previously
      * been initialized.
      *
diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java
index 5b6f154..89c17c7 100644
--- a/core/java/android/net/CaptivePortalTracker.java
+++ b/core/java/android/net/CaptivePortalTracker.java
@@ -421,6 +421,13 @@
             case ConnectivityManager.TYPE_WIFI:
                 WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
                 if (currentWifiInfo != null) {
+                    // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
+                    // surrounded by double quotation marks (thus violating the Javadoc), but this
+                    // was changed to match the Javadoc in API 17. Since clients may have started
+                    // sanitizing the output of this method since API 17 was released, we should
+                    // not change it here as it would become impossible to tell whether the SSID is
+                    // simply being surrounded by quotes due to the API, or whether those quotes
+                    // are actually part of the SSID.
                     latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID());
                     latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID());
                 } else {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9b23b35..1f211c2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -255,8 +255,9 @@
         updateWindow(false, false);
     }
 
+    /** @hide */
     @Override
-    protected void onDetachedFromWindow() {
+    protected void onDetachedFromWindowInternal() {
         if (mGlobalListenersAdded) {
             ViewTreeObserver observer = getViewTreeObserver();
             observer.removeOnScrollChangedListener(mScrollChangedListener);
@@ -278,7 +279,7 @@
         mSession = null;
         mLayout.token = null;
 
-        super.onDetachedFromWindow();
+        super.onDetachedFromWindowInternal();
     }
 
     @Override
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index ef0d80d..3cfe5e9 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -228,10 +228,11 @@
         }
     }
 
+    /** @hide */
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
+    protected void onDetachedFromWindowInternal() {
         destroySurface();
+        super.onDetachedFromWindowInternal();
     }
 
     private void destroySurface() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a57b311..827c4cc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10838,11 +10838,14 @@
      * Sets the outline of the view, which defines the shape of the shadow it
      * casts, and can used for clipping.
      * <p>
+     * The outline path of a View must be {@link android.graphics.Path#isConvex() convex}.
+     * <p>
      * If the outline is not set, or {@link Path#isEmpty()}, shadows will be
      * cast from the bounds of the View, and clipToOutline will be ignored.
      *
-     * @param outline The new outline of the view. Must be non-null.
+     * @param outline The new outline of the view. Must be non-null, and convex.
      *
+     * @see #setCastsShadow(boolean)
      * @see #getOutline(Path)
      * @see #getClipToOutline()
      * @see #setClipToOutline(boolean)
@@ -10851,6 +10854,9 @@
         if (outline == null) {
             throw new IllegalArgumentException("Path must be non-null");
         }
+        if (!outline.isConvex()) {
+            throw new IllegalArgumentException("Path must be convex");
+        }
         // always copy the path since caller may reuse
         if (mOutline == null) {
             mOutline = new Path(outline);
@@ -13110,6 +13116,19 @@
      * @see #onAttachedToWindow()
      */
     protected void onDetachedFromWindow() {
+    }
+
+    /**
+     * This is a framework-internal mirror of onDetachedFromWindow() that's called
+     * after onDetachedFromWindow().
+     *
+     * If you override this you *MUST* call super.onDetachedFromWindowInternal()!
+     * The super method should be called at the end of the overriden method to ensure
+     * subclasses are destroyed first
+     *
+     * @hide
+     */
+    protected void onDetachedFromWindowInternal() {
         mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
         mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
@@ -13297,6 +13316,7 @@
         }
 
         onDetachedFromWindow();
+        onDetachedFromWindowInternal();
 
         ListenerInfo li = mListenerInfo;
         final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 9f2bf33..f8160c8 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -37,6 +37,7 @@
 import android.util.Slog;
 import android.util.Xml;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import android.view.inputmethod.InputMethodSubtypeArray;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -86,9 +87,9 @@
     final int mIsDefaultResId;
 
     /**
-     * The array of the subtypes.
+     * An array-like container of the subtypes.
      */
-    private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>();
+    private final InputMethodSubtypeArray mSubtypes;
 
     private final boolean mIsAuxIme;
 
@@ -138,6 +139,7 @@
         int isDefaultResId = 0;
 
         XmlResourceParser parser = null;
+        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
         try {
             parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA);
             if (parser == null) {
@@ -206,7 +208,7 @@
                     if (!subtype.isAuxiliary()) {
                         isAuxIme = false;
                     }
-                    mSubtypes.add(subtype);
+                    subtypes.add(subtype);
                 }
             }
         } catch (NameNotFoundException e) {
@@ -216,7 +218,7 @@
             if (parser != null) parser.close();
         }
 
-        if (mSubtypes.size() == 0) {
+        if (subtypes.size() == 0) {
             isAuxIme = false;
         }
 
@@ -225,14 +227,15 @@
             final int N = additionalSubtypes.size();
             for (int i = 0; i < N; ++i) {
                 final InputMethodSubtype subtype = additionalSubtypes.get(i);
-                if (!mSubtypes.contains(subtype)) {
-                    mSubtypes.add(subtype);
+                if (!subtypes.contains(subtype)) {
+                    subtypes.add(subtype);
                 } else {
                     Slog.w(TAG, "Duplicated subtype definition found: "
                             + subtype.getLocale() + ", " + subtype.getMode());
                 }
             }
         }
+        mSubtypes = new InputMethodSubtypeArray(subtypes);
         mSettingsActivityName = settingsActivityComponent;
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
@@ -246,7 +249,7 @@
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
         mService = ResolveInfo.CREATOR.createFromParcel(source);
-        source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR);
+        mSubtypes = new InputMethodSubtypeArray(source);
         mForceDefault = false;
     }
 
@@ -272,9 +275,7 @@
         mSettingsActivityName = settingsActivity;
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
-        if (subtypes != null) {
-            mSubtypes.addAll(subtypes);
-        }
+        mSubtypes = new InputMethodSubtypeArray(subtypes);
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = true;
     }
@@ -364,7 +365,7 @@
      * composed of {@link #getPackageName} and the class name returned here.
      *
      * <p>A null will be returned if there is no settings activity associated
-     * with the input method.
+     * with the input method.</p>
      */
     public String getSettingsActivity() {
         return mSettingsActivityName;
@@ -374,7 +375,7 @@
      * Return the count of the subtypes of Input Method.
      */
     public int getSubtypeCount() {
-        return mSubtypes.size();
+        return mSubtypes.getCount();
     }
 
     /**
@@ -479,7 +480,7 @@
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
         mService.writeToParcel(dest, flags);
-        dest.writeTypedList(mSubtypes);
+        mSubtypes.writeToParcel(dest);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
new file mode 100644
index 0000000..5bef71f
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2007-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.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AndroidRuntimeException;
+import android.util.Slog;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * An array-like container that stores multiple instances of {@link InputMethodSubtype}.
+ *
+ * <p>This container is designed to reduce the risk of {@link TransactionTooLargeException}
+ * when one or more instancess of {@link InputMethodInfo} are transferred through IPC.
+ * Basically this class does following three tasks.</p>
+ * <ul>
+ * <li>Applying compression for the marshalled data</li>
+ * <li>Lazily unmarshalling objects</li>
+ * <li>Caching the marshalled data when appropriate</li>
+ * </ul>
+ *
+ * @hide
+ */
+public class InputMethodSubtypeArray {
+    private final static String TAG = "InputMethodSubtypeArray";
+
+    /**
+     * Create a new instance of {@link InputMethodSubtypeArray} from an existing list of
+     * {@link InputMethodSubtype}.
+     *
+     * @param subtypes A list of {@link InputMethodSubtype} from which
+     * {@link InputMethodSubtypeArray} will be created.
+     */
+    public InputMethodSubtypeArray(final List<InputMethodSubtype> subtypes) {
+        if (subtypes == null) {
+            mCount = 0;
+            return;
+        }
+        mCount = subtypes.size();
+        mInstance = subtypes.toArray(new InputMethodSubtype[mCount]);
+    }
+
+    /**
+     * Unmarshall an instance of {@link InputMethodSubtypeArray} from a given {@link Parcel}
+     * object.
+     *
+     * @param source A {@link Parcel} object from which {@link InputMethodSubtypeArray} will be
+     * unmarshalled.
+     */
+    public InputMethodSubtypeArray(final Parcel source) {
+        mCount = source.readInt();
+        if (mCount > 0) {
+            mDecompressedSize = source.readInt();
+            mCompressedData = source.createByteArray();
+        }
+    }
+
+    /**
+     * Marshall the instance into a given {@link Parcel} object.
+     *
+     * <p>This methods may take a bit additional time to compress data lazily when called
+     * first time.</p>
+     *
+     * @param source A {@link Parcel} object to which {@link InputMethodSubtypeArray} will be
+     * marshalled.
+     */
+    public void writeToParcel(final Parcel dest) {
+        if (mCount == 0) {
+            dest.writeInt(mCount);
+            return;
+        }
+
+        byte[] compressedData = mCompressedData;
+        int decompressedSize = mDecompressedSize;
+        if (compressedData == null && decompressedSize == 0) {
+            synchronized (mLockObject) {
+                compressedData = mCompressedData;
+                decompressedSize = mDecompressedSize;
+                if (compressedData == null && decompressedSize == 0) {
+                    final byte[] decompressedData = marshall(mInstance);
+                    compressedData = compress(decompressedData);
+                    if (compressedData == null) {
+                        decompressedSize = -1;
+                        Slog.i(TAG, "Failed to compress data.");
+                    } else {
+                        decompressedSize = decompressedData.length;
+                    }
+                    mDecompressedSize = decompressedSize;
+                    mCompressedData = compressedData;
+                }
+            }
+        }
+
+        if (compressedData != null && decompressedSize > 0) {
+            dest.writeInt(mCount);
+            dest.writeInt(decompressedSize);
+            dest.writeByteArray(compressedData);
+        } else {
+            Slog.i(TAG, "Unexpected state. Behaving as an empty array.");
+            dest.writeInt(0);
+        }
+    }
+
+    /**
+     * Return {@link InputMethodSubtype} specified with the given index.
+     *
+     * <p>This methods may take a bit additional time to decompress data lazily when called
+     * first time.</p>
+     *
+     * @param index The index of {@link InputMethodSubtype}.
+     */
+    public InputMethodSubtype get(final int index) {
+        if (index < 0 || mCount <= index) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        InputMethodSubtype[] instance = mInstance;
+        if (instance == null) {
+            synchronized (mLockObject) {
+                instance = mInstance;
+                if (instance == null) {
+                    final byte[] decompressedData =
+                          decompress(mCompressedData, mDecompressedSize);
+                    // Clear the compressed data until {@link #getMarshalled()} is called.
+                    mCompressedData = null;
+                    mDecompressedSize = 0;
+                    if (decompressedData != null) {
+                        instance = unmarshall(decompressedData);
+                    } else {
+                        Slog.e(TAG, "Failed to decompress data. Returns null as fallback.");
+                        instance = new InputMethodSubtype[mCount];
+                    }
+                    mInstance = instance;
+                }
+            }
+        }
+        return instance[index];
+    }
+
+    /**
+     * Return the number of {@link InputMethodSubtype} objects.
+     */
+    public int getCount() {
+        return mCount;
+    }
+
+    private final Object mLockObject = new Object();
+    private final int mCount;
+
+    private volatile InputMethodSubtype[] mInstance;
+    private volatile byte[] mCompressedData;
+    private volatile int mDecompressedSize;
+
+    private static byte[] marshall(final InputMethodSubtype[] array) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            parcel.writeTypedArray(array, 0);
+            return parcel.marshall();
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+                parcel = null;
+            }
+        }
+    }
+
+    private static InputMethodSubtype[] unmarshall(final byte[] data) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            parcel.unmarshall(data, 0, data.length);
+            parcel.setDataPosition(0);
+            return parcel.createTypedArray(InputMethodSubtype.CREATOR);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+                parcel = null;
+            }
+        }
+    }
+
+    private static byte[] compress(final byte[] data) {
+        ByteArrayOutputStream resultStream = null;
+        GZIPOutputStream zipper = null;
+        try {
+            resultStream = new ByteArrayOutputStream();
+            zipper = new GZIPOutputStream(resultStream);
+            zipper.write(data);
+        } catch(IOException e) {
+            return null;
+        } finally {
+            try {
+                if (zipper != null) {
+                    zipper.close();
+                }
+            } catch (IOException e) {
+                zipper = null;
+                Slog.e(TAG, "Failed to close the stream.", e);
+                // swallowed, not propagated back to the caller
+            }
+            try {
+                if (resultStream != null) {
+                    resultStream.close();
+                }
+            } catch (IOException e) {
+                resultStream = null;
+                Slog.e(TAG, "Failed to close the stream.", e);
+                // swallowed, not propagated back to the caller
+            }
+        }
+        return resultStream != null ? resultStream.toByteArray() : null;
+    }
+
+    private static byte[] decompress(final byte[] data, final int expectedSize) {
+        ByteArrayInputStream inputStream = null;
+        GZIPInputStream unzipper = null;
+        try {
+            inputStream = new ByteArrayInputStream(data);
+            unzipper = new GZIPInputStream(inputStream);
+            final byte [] result = new byte[expectedSize];
+            int totalReadBytes = 0;
+            while (totalReadBytes < result.length) {
+                final int restBytes = result.length - totalReadBytes;
+                final int readBytes = unzipper.read(result, totalReadBytes, restBytes);
+                if (readBytes < 0) {
+                    break;
+                }
+                totalReadBytes += readBytes;
+            }
+            if (expectedSize != totalReadBytes) {
+                return null;
+            }
+            return result;
+        } catch(IOException e) {
+            return null;
+        } finally {
+            try {
+                if (unzipper != null) {
+                    unzipper.close();
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to close the stream.", e);
+                // swallowed, not propagated back to the caller
+            }
+            try {
+                if (inputStream != null) {
+                  inputStream.close();
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to close the stream.", e);
+                // swallowed, not propagated back to the caller
+            }
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 826bcec..81d36a4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2139,10 +2139,11 @@
         mProvider.getViewDelegate().onAttachedToWindow();
     }
 
+    /** @hide */
     @Override
-    protected void onDetachedFromWindow() {
+    protected void onDetachedFromWindowInternal() {
         mProvider.getViewDelegate().onDetachedFromWindow();
-        super.onDetachedFromWindow();
+        super.onDetachedFromWindowInternal();
     }
 
     @Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e5cb16f..687036c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4729,10 +4729,9 @@
         if (mEditor != null) mEditor.onAttachedToWindow();
     }
 
+    /** @hide */
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
+    protected void onDetachedFromWindowInternal() {
         if (mPreDrawRegistered) {
             getViewTreeObserver().removeOnPreDrawListener(this);
             mPreDrawRegistered = false;
@@ -4741,6 +4740,8 @@
         resetResolvedDrawables();
 
         if (mEditor != null) mEditor.onDetachedFromWindow();
+
+        super.onDetachedFromWindowInternal();
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 325a27d..e51345c 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -32,7 +32,9 @@
  * this file.
  */
 interface IInputMethodManager {
+    // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getInputMethodList();
+    // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getEnabledInputMethodList();
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
             boolean allowsImplicitlySelectedSubtypes);
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 429f177..e580d36 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -72,11 +72,16 @@
         *dst = *src;
     }
 
+    static jboolean isConvex(JNIEnv* env, jobject clazz, jlong objHandle) {
+        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
+        return obj->isConvex();
+    }
+
     static jint getFillType(JNIEnv* env, jobject clazz, jlong objHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         return obj->getFillType();
     }
- 
+
     static void setFillType(JNIEnv* env, jobject clazz, jlong pathHandle, jint ftHandle) {
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPath::FillType ft = static_cast<SkPath::FillType>(ftHandle);
@@ -524,6 +529,7 @@
     {"native_reset","(J)V", (void*) SkPathGlue::reset},
     {"native_rewind","(J)V", (void*) SkPathGlue::rewind},
     {"native_set","(JJ)V", (void*) SkPathGlue::assign},
+    {"native_isConvex","(J)Z", (void*) SkPathGlue::isConvex},
     {"native_getFillType","(J)I", (void*) SkPathGlue::getFillType},
     {"native_setFillType","(JI)V", (void*) SkPathGlue::setFillType},
     {"native_isEmpty","(J)Z", (void*) SkPathGlue::isEmpty},
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index c35bd48..52d90bc 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -25,7 +25,7 @@
         <item name="android:solidColor">@android:color/transparent</item>
         <item name="android:selectionDivider">@android:drawable/numberpicker_selection_divider</item>
         <item name="android:selectionDividerHeight">0dip</item>
-        <item name="android:selectionDividersDistance">0dip</item>
+        <item name="android:selectionDividersDistance">104dip</item>
         <item name="android:internalMinWidth">64dip</item>
         <item name="android:internalMaxHeight">180dip</item>
         <item name="virtualButtonPressedDrawable">?android:attr/selectableItemBackground</item>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index e429f96..7c0b7bc 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <resources>
-    <style name="Theme.Micro" parent="Theme.Holo">
+    <style name="Theme.Micro" parent="Theme.Holo.NoActionBar">
+        <item name="textViewStyle">@android:style/Widget.Micro.TextView</item>
         <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item>
         <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item>
         <item name="windowIsFloating">false</item>
diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
index 5e123ec..b0b119b 100755
--- a/core/tests/inputmethodtests/run_core_inputmethod_test.sh
+++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
@@ -21,4 +21,4 @@
   $COMMAND
 fi
 
-adb shell am instrument -w -e class android.os.InputMethodTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
+adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeArrayTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeArrayTest.java
new file mode 100644
index 0000000..1e0a919
--- /dev/null
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeArrayTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.os;
+
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtypeArray;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import java.util.ArrayList;
+
+public class InputMethodSubtypeArrayTest extends InstrumentationTestCase {
+    @SmallTest
+    public void testInstanciate() throws Exception {
+        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+        subtypes.add(createDummySubtype(0, "en_US"));
+        subtypes.add(createDummySubtype(1, "en_US"));
+        subtypes.add(createDummySubtype(2, "ja_JP"));
+
+        final InputMethodSubtypeArray array = new InputMethodSubtypeArray(subtypes);
+        assertEquals(subtypes.size(), array.getCount());
+        assertEquals(subtypes.get(0), array.get(0));
+        assertEquals(subtypes.get(1), array.get(1));
+        assertEquals(subtypes.get(2), array.get(2));
+
+        final InputMethodSubtypeArray clonedArray = cloneViaParcel(array);
+        assertEquals(subtypes.size(), clonedArray.getCount());
+        assertEquals(subtypes.get(0), clonedArray.get(0));
+        assertEquals(subtypes.get(1), clonedArray.get(1));
+        assertEquals(subtypes.get(2), clonedArray.get(2));
+
+        final InputMethodSubtypeArray clonedClonedArray = cloneViaParcel(clonedArray);
+        assertEquals(clonedArray.getCount(), clonedClonedArray.getCount());
+        assertEquals(clonedArray.get(0), clonedClonedArray.get(0));
+        assertEquals(clonedArray.get(1), clonedClonedArray.get(1));
+        assertEquals(clonedArray.get(2), clonedClonedArray.get(2));
+    }
+
+    InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            original.writeToParcel(parcel);
+            parcel.setDataPosition(0);
+            return new InputMethodSubtypeArray(parcel);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+
+    private static InputMethodSubtype createDummySubtype(final int id, final String locale) {
+        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
+        return builder.setSubtypeNameResId(0)
+                .setSubtypeIconResId(0)
+                .setSubtypeId(id)
+                .setSubtypeLocale(locale)
+                .setIsAsciiCapable(true)
+                .build();
+    }
+}
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 2ce73ac..c07a6da 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -168,6 +168,21 @@
     }
 
     /**
+     * Returns the path's convexity, as defined by the content of the path.
+     * <p>
+     * A path is convex if it has a single contour, and only ever curves in a
+     * single direction.
+     * <p>
+     * This function will calculate the convexity of the path from its control
+     * points, and cache the result.
+     *
+     * @return True if the path is convex.
+     */
+    public boolean isConvex() {
+        return native_isConvex(mNativePath);
+    }
+
+    /**
      * Enum for the ways a path may be filled.
      */
     public enum FillType {
@@ -224,7 +239,7 @@
     public void setFillType(FillType ft) {
         native_setFillType(mNativePath, ft.nativeInt);
     }
-    
+
     /**
      * Returns true if the filltype is one of the INVERSE variants
      *
@@ -232,18 +247,18 @@
      */
     public boolean isInverseFillType() {
         final int ft = native_getFillType(mNativePath);
-        return (ft & 2) != 0;
+        return (ft & FillType.INVERSE_WINDING.nativeInt) != 0;
     }
-    
+
     /**
      * Toggles the INVERSE state of the filltype
      */
     public void toggleInverseFillType() {
         int ft = native_getFillType(mNativePath);
-        ft ^= 2;
+        ft ^= FillType.INVERSE_WINDING.nativeInt;
         native_setFillType(mNativePath, ft);
     }
-    
+
     /**
      * Returns true if the path is empty (contains no lines or curves)
      *
@@ -719,6 +734,7 @@
     private static native void native_reset(long nPath);
     private static native void native_rewind(long nPath);
     private static native void native_set(long native_dst, long native_src);
+    private static native boolean native_isConvex(long nPath);
     private static native int native_getFillType(long nPath);
     private static native void native_setFillType(long nPath, int ft);
     private static native boolean native_isEmpty(long nPath);
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index bdb2f8d..0f76486 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -109,6 +109,9 @@
 void DisplayList::setData(DisplayListData* data) {
     delete mDisplayListData;
     mDisplayListData = data;
+    if (mDisplayListData) {
+        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
+    }
 }
 
 /**
@@ -488,6 +491,8 @@
             replayStruct.mDrawGlStatus);
 }
 
+#define SHADOW_DELTA 2.0f
+
 template <class T>
 void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
         T& handler, const int level) {
@@ -501,34 +506,66 @@
     int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     LinearAllocator& alloc = handler.allocator();
     ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight,
-            SkRegion::kIntersect_Op); // clip to 3d root bounds for now
+            SkRegion::kIntersect_Op); // clip to 3d root bounds
     handler(clipOp, PROPERTY_SAVECOUNT, mClipToBounds);
 
-    for (size_t i = 0; i < m3dNodes.size(); i++) {
-        const float zValue = m3dNodes[i].key;
-        DrawDisplayListOp* childOp = m3dNodes[i].value;
+    /**
+     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+     * with very similar Z heights to draw together.
+     *
+     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+     * underneath both, and neither's shadow is drawn on top of the other.
+     */
+    const size_t nonNegativeIndex = findNonNegativeIndex(m3dNodes);
+    size_t drawIndex, shadowIndex, endIndex;
+    if (mode == kNegativeZChildren) {
+        drawIndex = 0;
+        endIndex = nonNegativeIndex;
+        shadowIndex = endIndex; // draw no shadows
+    } else {
+        drawIndex = nonNegativeIndex;
+        endIndex = m3dNodes.size();
+        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+    }
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            DrawDisplayListOp* casterOp = m3dNodes[shadowIndex].value;
+            DisplayList* caster = casterOp->mDisplayList;
+            const float casterZ = m3dNodes[shadowIndex].key;
+            // attempt to render the shadow if the caster about to be drawn is its caster,
+            // OR if its caster's Z value is similar to the previous potential caster
+            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
 
-        if (mode == kPositiveZChildren && zValue < 0.0f) continue;
-        if (mode == kNegativeZChildren && zValue > 0.0f) break;
+                if (caster->mCastsShadow && caster->mAlpha > 0.0f) {
+                    mat4 shadowMatrix(casterOp->mTransformFromCompositingAncestor);
+                    caster->applyViewPropertyTransforms(shadowMatrix);
 
-        DisplayList* child = childOp->mDisplayList;
-        if (mode == kPositiveZChildren && zValue > 0.0f
-                && child->mCastsShadow && child->mAlpha > 0.0f) {
-            /* draw shadow with parent matrix applied, passing in the child's total matrix
-             * TODO: consider depth in more complex scenarios (neg z, added shadow depth)
-             */
-            mat4 shadowMatrix(childOp->mTransformFromCompositingAncestor);
-            child->applyViewPropertyTransforms(shadowMatrix);
+                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix,
+                            caster->mAlpha, &(caster->mOutline), caster->mWidth, caster->mHeight);
+                    handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
+                }
 
-            DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(shadowMatrix,
-                    child->mAlpha, &(child->mOutline), child->mWidth, child->mHeight);
-            handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
         }
 
+        // only the actual child DL draw needs to be in save/restore,
+        // since it modifies the renderer's matrix
+        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+        DrawDisplayListOp* childOp = m3dNodes[drawIndex].value;
+        DisplayList* child = childOp->mDisplayList;
+
         renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
         childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
         handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
         childOp->mSkipInOrderDraw = true;
+
+        renderer.restoreToCount(restoreTo);
+        drawIndex++;
     }
     handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
 }
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index aba40b6..a3577d4 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -550,6 +550,13 @@
 private:
     typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
 
+    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+        for (size_t i = 0; i < nodes.size(); i++) {
+            if (nodes[i].key >= 0.0f) return i;
+        }
+        return nodes.size();
+    }
+
     enum ChildrenSelectMode {
         kNegativeZChildren,
         kPositiveZChildren
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 35327c0..ed98b96 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -38,6 +38,7 @@
     libcamera_client \
     libmtp \
     libusbhost \
+    libjhead \
     libexif \
     libstagefright_amrnb_common \
 
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 5a2e261..a9322b9 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -595,13 +595,9 @@
         mDetached = false;
     }
 
-    /**
-     * This method is used as part of the View class and is not normally
-     * called or subclassed by clients of GLSurfaceView.
-     * Must not be called before a renderer has been set.
-     */
+    /** @hide */
     @Override
-    protected void onDetachedFromWindow() {
+    protected void onDetachedFromWindowInternal() {
         if (LOG_ATTACH_DETACH) {
             Log.d(TAG, "onDetachedFromWindow");
         }
@@ -609,7 +605,7 @@
             mGLThread.requestExitAndWait();
         }
         mDetached = true;
-        super.onDetachedFromWindow();
+        super.onDetachedFromWindowInternal();
     }
 
     // ----------------------------------------------------------------------
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_color_space_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_color_space_alpha.png
new file mode 100644
index 0000000..fe6dc52
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_color_space_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
new file mode 100644
index 0000000..0f9dfc7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_inversion_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_inversion_alpha.png
new file mode 100644
index 0000000..aea75c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_qs_inversion_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldpi/ic_qs_color_space_alpha.png b/packages/SystemUI/res/drawable-ldpi/ic_qs_color_space_alpha.png
new file mode 100644
index 0000000..46d2a16
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldpi/ic_qs_color_space_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-ldpi/ic_qs_contrast_alpha.png
new file mode 100644
index 0000000..704b4ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldpi/ic_qs_contrast_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldpi/ic_qs_inversion_alpha.png b/packages/SystemUI/res/drawable-ldpi/ic_qs_inversion_alpha.png
new file mode 100644
index 0000000..d56efb5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldpi/ic_qs_inversion_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_color_space_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_color_space_alpha.png
new file mode 100644
index 0000000..18b6029
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_color_space_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
new file mode 100644
index 0000000..a4dd087
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_inversion_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_inversion_alpha.png
new file mode 100644
index 0000000..b6ea14e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_qs_inversion_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_color_space_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_color_space_alpha.png
new file mode 100644
index 0000000..95cf67f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_color_space_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
new file mode 100644
index 0000000..9331e52
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_inversion_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_inversion_alpha.png
new file mode 100644
index 0000000..efd8b9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_qs_inversion_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_color_space_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_color_space_alpha.png
new file mode 100644
index 0000000..7f441c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_color_space_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
new file mode 100644
index 0000000..82c3842
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_inversion_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_inversion_alpha.png
new file mode 100644
index 0000000..ce9bae2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_inversion_alpha.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_space_off.xml b/packages/SystemUI/res/drawable/ic_qs_color_space_off.xml
new file mode 100644
index 0000000..cf34ba6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_color_space_off.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_color_space_alpha"
+    android:tint="@color/ic_qs_off" />
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_space_on.xml b/packages/SystemUI/res/drawable/ic_qs_color_space_on.xml
new file mode 100644
index 0000000..1806688
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_color_space_on.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_color_space_alpha"
+    android:tint="@color/ic_qs_on" />
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml b/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
new file mode 100644
index 0000000..5f65d8a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_contrast_alpha"
+    android:tint="@color/ic_qs_off" />
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
new file mode 100644
index 0000000..a018929
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_contrast_alpha"
+    android:tint="@color/ic_qs_on" />
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
new file mode 100644
index 0000000..9018a90
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_inversion_alpha"
+    android:tint="@color/ic_qs_off" />
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
new file mode 100644
index 0000000..9110201
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_qs_inversion_alpha"
+    android:tint="@color/ic_qs_on" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e525fbb..5cf0453 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -39,4 +39,10 @@
     <color name="status_bar_clock_color">#FFFFFFFF</color>
     <drawable name="notification_item_background_color">#ff111111</drawable>
     <drawable name="notification_item_background_color_pressed">#ff454545</drawable>
+
+    <!-- Tint color for inactive Quick Settings icons. -->
+    <color name="ic_qs_off">#ff404040</color>
+
+    <!-- Tint color for active Quick Settings icons. -->
+    <color name="ic_qs_on">#ffffffff</color>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 48ee1ce..174cad8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -996,8 +996,8 @@
         mInversionState.toggled = enabled;
         mInversionState.type = type;
         // TODO: Add real icon assets.
-        mInversionState.iconId = enabled ? R.drawable.ic_qs_bluetooth_on
-                : R.drawable.ic_qs_bluetooth_off;
+        mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on
+                : R.drawable.ic_qs_inversion_off;
         mInversionState.label = res.getString(R.string.quick_settings_inversion_label);
         mInversionCallback.refreshView(mInversionTile, mInversionState);
     }
@@ -1026,8 +1026,8 @@
         mContrastState.contrast = contrast;
         mContrastState.brightness = brightness;
         // TODO: Add real icon assets.
-        mContrastState.iconId = enabled ? R.drawable.ic_qs_bluetooth_on
-                : R.drawable.ic_qs_bluetooth_off;
+        mContrastState.iconId = enabled ? R.drawable.ic_qs_contrast_on
+                : R.drawable.ic_qs_contrast_off;
         mContrastState.label = res.getString(R.string.quick_settings_contrast_label);
         mContrastCallback.refreshView(mContrastTile, mContrastState);
     }
@@ -1053,8 +1053,8 @@
         mColorSpaceState.toggled = enabled;
         mColorSpaceState.type = type;
         // TODO: Add real icon assets.
-        mColorSpaceState.iconId = enabled ? R.drawable.ic_qs_bluetooth_on
-                : R.drawable.ic_qs_bluetooth_off;
+        mColorSpaceState.iconId = enabled ? R.drawable.ic_qs_color_space_on
+                : R.drawable.ic_qs_color_space_off;
         mColorSpaceState.label = res.getString(R.string.quick_settings_color_space_label);
         mColorSpaceCallback.refreshView(mColorSpaceTile, mColorSpaceState);
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index abc3fb1..8cd6e06 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -372,6 +372,7 @@
     static final Rect mTmpNavigationFrame = new Rect();
 
     WindowState mTopFullscreenOpaqueWindowState;
+    boolean mHideWindowBehindKeyguard;
     boolean mTopIsFullscreen;
     boolean mForceStatusBar;
     boolean mForceStatusBarFromKeyguard;
@@ -3355,6 +3356,7 @@
     @Override
     public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
         mTopFullscreenOpaqueWindowState = null;
+        mHideWindowBehindKeyguard = false;
         mForceStatusBar = false;
         mForceStatusBarFromKeyguard = false;
         mForcingShowNavBar = false;
@@ -3391,7 +3393,7 @@
             if (attrs.type == TYPE_KEYGUARD) {
                 mShowingLockscreen = true;
             }
-            boolean applyWindow = attrs.type >= FIRST_APPLICATION_WINDOW
+            boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
                     && attrs.type <= LAST_APPLICATION_WINDOW;
             if (attrs.type == TYPE_DREAM) {
                 // If the lockscreen was showing when the dream started then wait
@@ -3399,30 +3401,35 @@
                 if (!mDreamingLockscreen
                         || (win.isVisibleLw() && win.hasDrawnLw())) {
                     mShowingDream = true;
-                    applyWindow = true;
+                    appWindow = true;
                 }
             }
-            if (applyWindow
-                    && attrs.x == 0 && attrs.y == 0
-                    && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
-                    && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
-                mTopFullscreenOpaqueWindowState = win;
-                if ((fl & FLAG_SHOW_WHEN_LOCKED) != 0) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mHideLockScreen to true by win " + win);
-                    mHideLockScreen = true;
-                    mForceStatusBarFromKeyguard = false;
-                }
-                if ((fl & FLAG_DISMISS_KEYGUARD) != 0
-                        && mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win);
-                    mDismissKeyguard = mWinDismissingKeyguard == win ?
-                            DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
-                    mWinDismissingKeyguard = win;
-                    mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure();
-                }
-                if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
-                    mAllowLockscreenWhenOn = true;
+
+            final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
+            if (appWindow) {
+                if (attrs.x == 0 && attrs.y == 0
+                        && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+                        && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+                    if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
+                    mTopFullscreenOpaqueWindowState = win;
+                    if (showWhenLocked && !mHideWindowBehindKeyguard) {
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mHideLockScreen to true by win " + win);
+                        mHideLockScreen = true;
+                        mForceStatusBarFromKeyguard = false;
+                    }
+                    if ((fl & FLAG_DISMISS_KEYGUARD) != 0
+                            && mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
+                        if (DEBUG_LAYOUT) Slog.v(TAG, "Setting mDismissKeyguard true by win " + win);
+                        mDismissKeyguard = mWinDismissingKeyguard == win ?
+                                DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
+                        mWinDismissingKeyguard = win;
+                        mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure();
+                    }
+                    if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+                        mAllowLockscreenWhenOn = true;
+                    }
+                } else if (!showWhenLocked) {
+                    mHideWindowBehindKeyguard = true;
                 }
             }
         }
@@ -3509,7 +3516,7 @@
         if (mKeyguard != null) {
             if (localLOGV) Slog.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
                     + mHideLockScreen);
-            if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardDelegate.isSecure()) {
+            if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !isKeyguardSecure()) {
                 if (mKeyguard.hideLw(true)) {
                     changes |= FINISH_LAYOUT_REDO_LAYOUT
                             | FINISH_LAYOUT_REDO_CONFIG
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 782868e..b23ab25 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9212,8 +9212,13 @@
                         ActivityInfo ai = ris.get(i).activityInfo;
                         ComponentName comp = new ComponentName(ai.packageName, ai.name);
                         if (lastDoneReceivers.contains(comp)) {
+                            // We already did the pre boot receiver for this app with the current
+                            // platform version, so don't do it again...
                             ris.remove(i);
                             i--;
+                            // ...however, do keep it as one that has been done, so we don't
+                            // forget about it when rewriting the file of last done receivers.
+                            doneReceivers.add(comp);
                         }
                     }