Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index c80a9ab..99d73fa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -320,6 +320,7 @@
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/hotspot/IWifiHotspotManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
+	wifi/java/android/net/wifi/IWifiScanner.aidl \
 	packages/services/PacProcessor/com/android/net/IProxyService.aidl \
 	packages/services/Proxy/com/android/net/IProxyCallback.aidl \
 	packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
@@ -720,8 +721,9 @@
 $(full_target): $(framework_built) $(gen)
 
 # Run this for checkbuild
-.PHONY: checkbuild
 checkbuild: doc-comment-check-docs
+# Check comment when you are updating the API
+update-api: doc-comment-check-docs
 
 # ====  static html in the sdk ==================================
 include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index 1c9f871..302e77e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1061,6 +1061,7 @@
     field public static final int startDelay = 16843746; // 0x10103e2
     field public static final int startOffset = 16843198; // 0x10101be
     field public static final deprecated int startYear = 16843132; // 0x101017c
+    field public static final int stateListAnimator = 16843860; // 0x1010454
     field public static final int stateNotNeeded = 16842774; // 0x1010016
     field public static final int state_above_anchor = 16842922; // 0x10100aa
     field public static final int state_accelerated = 16843547; // 0x101031b
@@ -2788,6 +2789,7 @@
   public class AnimatorInflater {
     ctor public AnimatorInflater();
     method public static android.animation.Animator loadAnimator(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
+    method public static android.animation.StateListAnimator loadStateListAnimator(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
   }
 
   public abstract class AnimatorListenerAdapter implements android.animation.Animator.AnimatorListener android.animation.Animator.AnimatorPauseListener {
@@ -2984,6 +2986,12 @@
     method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect);
   }
 
+  public class StateListAnimator {
+    ctor public StateListAnimator();
+    method public void addState(int[], android.animation.Animator);
+    method public void jumpToCurrentState();
+  }
+
   public class TimeAnimator extends android.animation.ValueAnimator {
     ctor public TimeAnimator();
     method public void setTimeListener(android.animation.TimeAnimator.TimeListener);
@@ -3363,10 +3371,12 @@
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startLockTask();
     method public deprecated void startManagingCursor(android.database.Cursor);
     method public boolean startNextMatchingActivity(android.content.Intent);
     method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle);
     method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean);
+    method public void stopLockTask();
     method public deprecated void stopManagingCursor(android.database.Cursor);
     method public void takeKeyEvents(boolean);
     method public void triggerSearch(java.lang.String, android.os.Bundle);
@@ -4428,6 +4438,7 @@
     field public static final java.lang.String CATEGORY_STATUS = "status";
     field public static final java.lang.String CATEGORY_SYSTEM = "sys";
     field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+    field public static final int COLOR_DEFAULT = 0; // 0x0
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int DEFAULT_ALL = -1; // 0xffffffff
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
@@ -4473,6 +4484,7 @@
     field public int audioStreamType;
     field public android.widget.RemoteViews bigContentView;
     field public java.lang.String category;
+    field public int color;
     field public android.app.PendingIntent contentIntent;
     field public android.widget.RemoteViews contentView;
     field public int defaults;
@@ -4535,6 +4547,7 @@
     method public deprecated android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setCategory(java.lang.String);
+    method public android.app.Notification.Builder setColor(int);
     method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -5017,6 +5030,7 @@
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+    method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5046,6 +5060,7 @@
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean resetPassword(java.lang.String, int);
+    method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -8063,6 +8078,7 @@
     field public static final java.lang.String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
     field public static final java.lang.String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
     field public static final java.lang.String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
+    field public static final java.lang.String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
     field public static final java.lang.String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light";
     field public static final java.lang.String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
     field public static final java.lang.String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
@@ -11851,7 +11867,6 @@
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2;
     field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
@@ -12233,8 +12248,6 @@
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_TONE_CURVE;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
@@ -15712,6 +15725,7 @@
     method public android.net.NetworkInfo getActiveNetworkInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public deprecated boolean getBackgroundDataSetting();
+    method public android.net.ProxyInfo getGlobalProxy();
     method public android.net.NetworkInfo getNetworkInfo(int);
     method public int getNetworkPreference();
     method public boolean isActiveNetworkMetered();
@@ -15719,6 +15733,7 @@
     method public static boolean isNetworkTypeValid(int);
     method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean requestRouteToHost(int, int);
+    method public void setGlobalProxy(android.net.ProxyInfo);
     method public void setNetworkPreference(int);
     method public int startUsingNetworkFeature(int, java.lang.String);
     method public int stopUsingNetworkFeature(int, java.lang.String);
@@ -15894,9 +15909,22 @@
     method public static final deprecated int getDefaultPort();
     method public static final deprecated java.lang.String getHost(android.content.Context);
     method public static final deprecated int getPort(android.content.Context);
+    field public static final java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
     field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
   }
 
+  public class ProxyInfo implements android.os.Parcelable {
+    method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int);
+    method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int, java.util.List<java.lang.String>);
+    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
+    method public int describeContents();
+    method public java.lang.String[] getExclusionList();
+    method public java.lang.String getHost();
+    method public android.net.Uri getPacFileUrl();
+    method public int getPort();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
     ctor public deprecated SSLCertificateSocketFactory(int);
     method public java.net.Socket createSocket(java.net.Socket, java.lang.String, int, boolean) throws java.io.IOException;
@@ -24561,6 +24589,7 @@
     method public void contextDump();
     method public static android.renderscript.RenderScript create(android.content.Context);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
+    method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, long);
     method public void destroy();
     method public void finish();
     method public final android.content.Context getApplicationContext();
@@ -24570,6 +24599,9 @@
     method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
     method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
     method public void setPriority(android.renderscript.RenderScript.Priority);
+    field public static final long CREATE_FLAG_LOW_LATENCY = 1L; // 0x1L
+    field public static final long CREATE_FLAG_LOW_POWER = 2L; // 0x2L
+    field public static final long CREATE_FLAG_NONE = 0L; // 0x0L
   }
 
   public static final class RenderScript.ContextType extends java.lang.Enum {
@@ -30530,6 +30562,7 @@
     method public final int getScrollY();
     method public java.lang.String getSharedElementName();
     method public int getSolidColor();
+    method public android.animation.StateListAnimator getStateListAnimator();
     method protected int getSuggestedMinimumHeight();
     method protected int getSuggestedMinimumWidth();
     method public int getSystemUiVisibility();
@@ -30794,6 +30827,7 @@
     method public void setSelected(boolean);
     method public void setSharedElementName(java.lang.String);
     method public void setSoundEffectsEnabled(boolean);
+    method public void setStateListAnimator(android.animation.StateListAnimator);
     method public void setSystemUiVisibility(int);
     method public void setTag(java.lang.Object);
     method public void setTag(int, java.lang.Object);
@@ -39798,11 +39832,13 @@
     method public java.lang.String getValue();
     method public int getVersion();
     method public boolean hasExpired();
+    method public boolean isHttpOnly();
     method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
     method public void setComment(java.lang.String);
     method public void setCommentURL(java.lang.String);
     method public void setDiscard(boolean);
     method public void setDomain(java.lang.String);
+    method public void setHttpOnly(boolean);
     method public void setMaxAge(long);
     method public void setPath(java.lang.String);
     method public void setPortlist(java.lang.String);
@@ -40755,7 +40791,7 @@
     ctor public ConnectionPendingException();
   }
 
-  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
     ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
     method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -40763,6 +40799,8 @@
     method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
     method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
     method public abstract boolean isConnected();
+    method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
     method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
     method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
     method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -40810,6 +40848,7 @@
 
   public abstract class FileLock implements java.lang.AutoCloseable {
     ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
+    method public java.nio.channels.Channel acquiredBy();
     method public final java.nio.channels.FileChannel channel();
     method public final void close() throws java.io.IOException;
     method public final boolean isShared();
@@ -40842,6 +40881,24 @@
     method public abstract void close() throws java.io.IOException;
   }
 
+  public abstract class MembershipKey {
+    ctor protected MembershipKey();
+    method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.MulticastChannel channel();
+    method public abstract void drop();
+    method public abstract java.net.InetAddress group();
+    method public abstract boolean isValid();
+    method public abstract java.net.NetworkInterface networkInterface();
+    method public abstract java.net.InetAddress sourceAddress();
+    method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
+  }
+
+  public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
+    method public abstract void close() throws java.io.IOException;
+    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
+  }
+
   public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
     method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
     method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -44947,6 +45004,7 @@
     method public java.lang.String getDisplayName(java.util.Locale);
     method public static java.util.Currency getInstance(java.lang.String);
     method public static java.util.Currency getInstance(java.util.Locale);
+    method public int getNumericCode();
     method public java.lang.String getSymbol();
     method public java.lang.String getSymbol(java.util.Locale);
   }
@@ -48029,8 +48087,10 @@
 
   public class ZipFile implements java.io.Closeable {
     ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
+    ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException;
     ctor public ZipFile(java.lang.String) throws java.io.IOException;
     ctor public ZipFile(java.io.File, int) throws java.io.IOException;
+    ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
     method public void close() throws java.io.IOException;
     method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
     method public java.lang.String getComment();
@@ -48084,6 +48144,7 @@
 
   public class ZipInputStream extends java.util.zip.InflaterInputStream {
     ctor public ZipInputStream(java.io.InputStream);
+    ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
     method public void closeEntry() throws java.io.IOException;
     method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
     method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -48131,6 +48192,7 @@
 
   public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
     ctor public ZipOutputStream(java.io.OutputStream);
+    ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
     method public void closeEntry() throws java.io.IOException;
     method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
     method public void setComment(java.lang.String);
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 20236aa..933135d 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -21,6 +21,7 @@
 import android.content.res.XmlResourceParser;
 import android.content.res.Resources.NotFoundException;
 import android.util.AttributeSet;
+import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.animation.AnimationUtils;
@@ -87,9 +88,86 @@
         }
     }
 
+    public static StateListAnimator loadStateListAnimator(Context context, int id)
+            throws NotFoundException {
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
+        } catch (XmlPullParserException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException(
+                            "Can't load state list animator resource ID #0x" +
+                                    Integer.toHexString(id)
+                    );
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException(
+                            "Can't load state list animator resource ID #0x" +
+                                    Integer.toHexString(id)
+                    );
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+        }
+    }
+
+    private static StateListAnimator createStateListAnimatorFromXml(Context context,
+            XmlPullParser parser, AttributeSet attributeSet)
+            throws IOException, XmlPullParserException {
+        int type;
+        StateListAnimator stateListAnimator = new StateListAnimator();
+
+        while (true) {
+            type = parser.next();
+            switch (type) {
+                case XmlPullParser.END_DOCUMENT:
+                case XmlPullParser.END_TAG:
+                    return stateListAnimator;
+
+                case XmlPullParser.START_TAG:
+                    // parse item
+                    Animator animator = null;
+                    if ("item".equals(parser.getName())) {
+                        int attributeCount = parser.getAttributeCount();
+                        int[] states = new int[attributeCount];
+                        int stateIndex = 0;
+                        for (int i = 0; i < attributeCount; i++) {
+                            int attrName = attributeSet.getAttributeNameResource(i);
+                            if (attrName == com.android.internal.R.attr.animation) {
+                                animator = loadAnimator(context,
+                                        attributeSet.getAttributeResourceValue(i, 0));
+                            } else {
+                                states[stateIndex++] =
+                                        attributeSet.getAttributeBooleanValue(i, false) ?
+                                                attrName : -attrName;
+                            }
+
+                        }
+                        if (animator == null) {
+                            animator = createAnimatorFromXml(context, parser);
+                        }
+
+                        if (animator == null) {
+                            throw new Resources.NotFoundException(
+                                    "animation state item must have a valid animation");
+                        }
+                        stateListAnimator
+                                .addState(StateSet.trimStateSet(states, stateIndex), animator);
+
+                    }
+                    break;
+            }
+        }
+    }
+
     private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
             throws XmlPullParserException, IOException {
-
         return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
     }
 
diff --git a/core/java/android/animation/StateListAnimator.java b/core/java/android/animation/StateListAnimator.java
new file mode 100644
index 0000000..bc4843d
--- /dev/null
+++ b/core/java/android/animation/StateListAnimator.java
@@ -0,0 +1,209 @@
+/*
+ * 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.animation;
+
+import android.util.StateSet;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Lets you define a number of Animators that will run on the attached View depending on the View's
+ * drawable state.
+ * <p>
+ * It can be defined in an XML file with the <code>&lt;selector></code> element.
+ * Each State Animator is defined in a nested <code>&lt;item></code> element.
+ *
+ * @attr ref android.R.styleable#DrawableStates_state_focused
+ * @attr ref android.R.styleable#DrawableStates_state_window_focused
+ * @attr ref android.R.styleable#DrawableStates_state_enabled
+ * @attr ref android.R.styleable#DrawableStates_state_checkable
+ * @attr ref android.R.styleable#DrawableStates_state_checked
+ * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_activated
+ * @attr ref android.R.styleable#DrawableStates_state_active
+ * @attr ref android.R.styleable#DrawableStates_state_single
+ * @attr ref android.R.styleable#DrawableStates_state_first
+ * @attr ref android.R.styleable#DrawableStates_state_middle
+ * @attr ref android.R.styleable#DrawableStates_state_last
+ * @attr ref android.R.styleable#DrawableStates_state_pressed
+ * @attr ref android.R.styleable#StateListAnimatorItem_animation
+ */
+public class StateListAnimator {
+
+    private final ArrayList<Tuple> mTuples = new ArrayList<Tuple>();
+
+    private Tuple mLastMatch = null;
+
+    private Animator mRunningAnimator = null;
+
+    private WeakReference<View> mViewRef;
+
+    private AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mRunningAnimator == animation) {
+                mRunningAnimator = null;
+            }
+        }
+    };
+
+    /**
+     * Associates the given animator with the provided drawable state specs so that it will be run
+     * when the View's drawable state matches the specs.
+     *
+     * @param specs The drawable state specs to match against
+     * @param animator The animator to run when the specs match
+     */
+    public void addState(int[] specs, Animator animator) {
+        Tuple tuple = new Tuple(specs, animator);
+        tuple.mAnimator.addListener(mAnimatorListener);
+        mTuples.add(tuple);
+    }
+
+    /**
+     * Returns the current {@link android.animation.Animator} which is started because of a state
+     * change.
+     *
+     * @return The currently running Animator or null if no Animator is running
+     * @hide
+     */
+    public Animator getRunningAnimator() {
+        return mRunningAnimator;
+    }
+
+    /**
+     * @hide
+     */
+    public View getTarget() {
+        return mViewRef == null ? null : mViewRef.get();
+    }
+
+    /**
+     * Called by View
+     * @hide
+     */
+    public void setTarget(View view) {
+        final View current = getTarget();
+        if (current == view) {
+            return;
+        }
+        if (current != null) {
+            clearTarget();
+        }
+        if (view != null) {
+            mViewRef = new WeakReference<View>(view);
+        }
+
+    }
+
+    private void clearTarget() {
+        final int size = mTuples.size();
+        for (int i = 0; i < size; i++) {
+            mTuples.get(i).mAnimator.setTarget(null);
+        }
+
+        mViewRef = null;
+        mLastMatch = null;
+        mRunningAnimator = null;
+    }
+
+    /**
+     * Called by View
+     * @hide
+     */
+    public void setState(int[] state) {
+        Tuple match = null;
+        final int count = mTuples.size();
+        for (int i = 0; i < count; i++) {
+            final Tuple tuple = mTuples.get(i);
+            if (StateSet.stateSetMatches(tuple.mSpecs, state)) {
+                match = tuple;
+                break;
+            }
+        }
+        if (match == mLastMatch) {
+            return;
+        }
+        if (mLastMatch != null) {
+            cancel(mLastMatch);
+        }
+        mLastMatch = match;
+        if (match != null) {
+            start(match);
+        }
+    }
+
+    private void start(Tuple match) {
+        match.mAnimator.setTarget(getTarget());
+        mRunningAnimator = match.mAnimator;
+        match.mAnimator.start();
+    }
+
+    private void cancel(Tuple lastMatch) {
+        lastMatch.mAnimator.cancel();
+        lastMatch.mAnimator.setTarget(null);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<Tuple> getTuples() {
+        return mTuples;
+    }
+
+    /**
+     * If there is an animation running for a recent state change, ends it.
+     * <p>
+     * This causes the animation to assign the end value(s) to the View.
+     */
+    public void jumpToCurrentState() {
+        if (mRunningAnimator != null) {
+            mRunningAnimator.end();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class Tuple {
+
+        final int[] mSpecs;
+
+        final Animator mAnimator;
+
+        private Tuple(int[] specs, Animator animator) {
+            mSpecs = specs;
+            mAnimator = animator;
+        }
+
+        /**
+         * @hide
+         */
+        public int[] getSpecs() {
+            return mSpecs;
+        }
+
+        /**
+         * @hide
+         */
+        public Animator getAnimator() {
+            return mAnimator;
+        }
+    }
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index af3a92c..36f9f4b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -29,6 +29,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -5689,7 +5690,16 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Put this Activity in a mode where the user is locked to the
+     * current task.
+     *
+     * This will prevent the user from launching other apps, going to settings,
+     * or reaching the home screen.
+     *
+     * Lock task mode will only start if the activity has been whitelisted by the
+     * Device Owner through DevicePolicyManager#setLockTaskComponents.
+     */
     public void startLockTask() {
         try {
             ActivityManagerNative.getDefault().startLockTaskMode(mToken);
@@ -5697,7 +5707,15 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Allow the user to switch away from the current task.
+     *
+     * Called to end the mode started by {@link Activity#startLockTask}. This
+     * can only be called by activities that have successfully called
+     * startLockTask previously.
+     *
+     * This will allow the user to exit this app and move onto other activities.
+     */
     public void stopLockTask() {
         try {
             ActivityManagerNative.getDefault().stopLockTaskMode();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3b2ff7f..b606088 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -46,7 +46,7 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.IConnectivityManager;
 import android.net.Proxy;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -4294,8 +4294,8 @@
             // crash if we can't get it.
             IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
             try {
-                ProxyProperties proxyProperties = service.getProxy();
-                Proxy.setHttpProxySystemProperty(proxyProperties);
+                ProxyInfo proxyInfo = service.getProxy();
+                Proxy.setHttpProxySystemProperty(proxyInfo);
             } catch (RemoteException e) {}
         }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c621696..5ed5030 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.net.wifi.IWifiScanner;
+import android.net.wifi.WifiScanner;
 import android.os.Build;
 
 import com.android.internal.policy.PolicyManager;
@@ -589,6 +591,13 @@
                     return new WifiP2pManager(service);
                 }});
 
+        registerService(WIFI_SCANNING_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(WIFI_SCANNING_SERVICE);
+                IWifiScanner service = IWifiScanner.Stub.asInterface(b);
+                return new WifiScanner(ctx.getOuterContext(), service);
+            }});
+
         registerService(WINDOW_SERVICE, new ServiceFetcher() {
                 Display mDefaultDisplay;
                 public Object getService(ContextImpl ctx) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 25a1493..bba6caf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,7 +17,7 @@
 package android.app;
 
 import com.android.internal.R;
-import com.android.internal.util.LegacyNotificationUtil;
+import com.android.internal.util.NotificationColorUtil;
 
 import android.annotation.IntDef;
 import android.content.Context;
@@ -28,6 +28,7 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.BadParcelableException;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -420,6 +421,21 @@
     @Priority
     public int priority;
 
+    /**
+     * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
+     * to be applied by the standard Style templates when presenting this notification.
+     *
+     * The current template design constructs a colorful header image by overlaying the
+     * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
+     * ignored.
+     */
+    public int color = COLOR_DEFAULT;
+
+    /**
+     * Special value of {@link #color} telling the system not to decorate this notification with
+     * any special color but instead use default colors when presenting this notification.
+     */
+    public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
 
     /**
      * Sphere of visibility of this notification, which affects how and when the SystemUI reveals 
@@ -877,6 +893,8 @@
         if (parcel.readInt() != 0) {
             publicVersion = Notification.CREATOR.createFromParcel(parcel);
         }
+
+        color = parcel.readInt();
     }
 
     @Override
@@ -968,6 +986,8 @@
             this.publicVersion.cloneInto(that.publicVersion, heavy);
         }
 
+        that.color = this.color;
+
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
         }
@@ -1110,6 +1130,8 @@
         } else {
             parcel.writeInt(0);
         }
+
+        parcel.writeInt(color);
     }
 
     /**
@@ -1218,6 +1240,7 @@
         sb.append(Integer.toHexString(this.defaults));
         sb.append(" flags=0x");
         sb.append(Integer.toHexString(this.flags));
+        sb.append(String.format(" color=0x%08x", this.color));
         sb.append(" category="); sb.append(this.category);
         if (actions != null) {
             sb.append(" ");
@@ -1309,9 +1332,10 @@
         private boolean mShowWhen = true;
         private int mVisibility = VISIBILITY_PRIVATE;
         private Notification mPublicVersion = null;
-        private boolean mQuantumTheme;
-        private final LegacyNotificationUtil mLegacyNotificationUtil;
+        private final NotificationColorUtil mColorUtil;
         private ArrayList<String> mPeople;
+        private boolean mPreQuantum;
+        private int mColor = COLOR_DEFAULT;
 
         /**
          * Constructs a new Builder with the defaults:
@@ -1341,12 +1365,8 @@
             mPriority = PRIORITY_DEFAULT;
             mPeople = new ArrayList<String>();
 
-            // TODO: Decide on targetSdk from calling app whether to use quantum theme.
-            mQuantumTheme = true;
-
-            // TODO: Decide on targetSdk from calling app whether to instantiate the processor at
-            // all.
-            mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
+            mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L;
+            mColorUtil = NotificationColorUtil.getInstance();
         }
 
         /**
@@ -1853,29 +1873,38 @@
             }
         }
 
+        /**
+         * Sets {@link Notification#color}.
+         *
+         * @param argb The accent color to use
+         *
+         * @return The same Builder.
+         */
+        public Builder setColor(int argb) {
+            mColor = argb;
+            return this;
+        }
+
         private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
             RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
             boolean showLine3 = false;
             boolean showLine2 = false;
             int smallIconImageViewId = R.id.icon;
-            if (!mQuantumTheme && mPriority < PRIORITY_LOW) {
-                contentView.setInt(R.id.icon,
-                        "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
-                contentView.setInt(R.id.status_bar_latest_event_content,
-                        "setBackgroundResource", R.drawable.notification_bg_low);
+            if (mPriority < PRIORITY_LOW) {
+                // TODO: Low priority presentation
             }
             if (mLargeIcon != null) {
                 contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
-                processLegacyLargeIcon(mLargeIcon, contentView);
+                processLargeIcon(mLargeIcon, contentView);
                 smallIconImageViewId = R.id.right_icon;
             }
             if (mSmallIcon != 0) {
                 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
                 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
                 if (mLargeIcon != null) {
-                    processLegacySmallIcon(mSmallIcon, smallIconImageViewId, contentView);
+                    processSmallRightIcon(mSmallIcon, smallIconImageViewId, contentView);
                 } else {
-                    processLegacyLargeIcon(mSmallIcon, contentView);
+                    processSmallIconAsLarge(mSmallIcon, contentView);
                 }
 
             } else {
@@ -2035,12 +2064,12 @@
          *         doesn't create quantum notifications by itself) app.
          */
         private boolean isLegacy() {
-            return mLegacyNotificationUtil != null;
+            return mColorUtil != null;
         }
 
         private void processLegacyAction(Action action, RemoteViews button) {
             if (isLegacy()) {
-                if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) {
+                if (mColorUtil.isGrayscale(mContext, action.icon)) {
                     button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
                             mContext.getResources().getColor(
                                     R.color.notification_action_legacy_color_filter),
@@ -2051,47 +2080,70 @@
 
         private CharSequence processLegacyText(CharSequence charSequence) {
             if (isLegacy()) {
-                return mLegacyNotificationUtil.invertCharSequenceColors(charSequence);
+                return mColorUtil.invertCharSequenceColors(charSequence);
             } else {
                 return charSequence;
             }
         }
 
-        private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) {
-            if (isLegacy()) {
-                processLegacyLargeIcon(
-                        mLegacyNotificationUtil.isGrayscale(mContext, largeIconId),
-                        contentView);
+        /**
+         * Apply any necessary background to smallIcons being used in the largeIcon spot.
+         */
+        private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
+            if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) {
+                applyLargeIconBackground(contentView);
             }
         }
 
-        private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
-            if (isLegacy()) {
-                processLegacyLargeIcon(
-                        mLegacyNotificationUtil.isGrayscale(largeIcon),
-                        contentView);
+        /**
+         * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
+         * if it's grayscale).
+         */
+        // TODO: also check bounds, transparency, that sort of thing.
+        private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
+            if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) {
+                applyLargeIconBackground(contentView);
             }
         }
 
-        private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) {
-            if (isLegacy() && isGrayscale) {
-                contentView.setInt(R.id.icon, "setBackgroundResource",
-                        R.drawable.notification_icon_legacy_bg_inset);
-            }
+        /**
+         * Add a colored circle behind the largeIcon slot.
+         */
+        private void applyLargeIconBackground(RemoteViews contentView) {
+            contentView.setInt(R.id.icon, "setBackgroundResource",
+                    R.drawable.notification_icon_legacy_bg_inset);
+
+            contentView.setDrawableParameters(
+                    R.id.icon,
+                    true,
+                    -1,
+                    mColor,
+                    PorterDuff.Mode.SRC_ATOP,
+                    -1);
         }
 
-        private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId,
+        /**
+         * Recolor small icons when used in the R.id.right_icon slot.
+         */
+        private void processSmallRightIcon(int smallIconDrawableId, int smallIconImageViewId,
                 RemoteViews contentView) {
-            if (isLegacy()) {
-                if (mLegacyNotificationUtil.isGrayscale(mContext, smallIconDrawableId)) {
-                    contentView.setDrawableParameters(smallIconImageViewId, false, -1,
-                            mContext.getResources().getColor(
-                                    R.color.notification_action_legacy_color_filter),
-                            PorterDuff.Mode.MULTIPLY, -1);
-                }
+            if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) {
+                contentView.setDrawableParameters(smallIconImageViewId, false, -1,
+                        mContext.getResources().getColor(
+                                R.color.notification_action_legacy_color_filter),
+                        PorterDuff.Mode.MULTIPLY, -1);
             }
         }
 
+        private int resolveColor() {
+            if (mColor == COLOR_DEFAULT) {
+                mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color);
+            } else {
+                mColor |= 0xFF000000; // no alpha for custom colors
+            }
+            return mColor;
+        }
+
         /**
          * Apply the unstyled operations and return a new {@link Notification} object.
          * @hide
@@ -2102,6 +2154,9 @@
             n.icon = mSmallIcon;
             n.iconLevel = mSmallIconLevel;
             n.number = mNumber;
+
+            n.color = resolveColor();
+
             n.contentView = makeContentView();
             n.contentIntent = mContentIntent;
             n.deleteIntent = mDeleteIntent;
@@ -2207,45 +2262,31 @@
 
 
         private int getBaseLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_template_quantum_base
-                    : R.layout.notification_template_base;
+            return R.layout.notification_template_quantum_base;
         }
 
         private int getBigBaseLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_template_quantum_big_base
-                    : R.layout.notification_template_big_base;
+            return R.layout.notification_template_quantum_big_base;
         }
 
         private int getBigPictureLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_template_quantum_big_picture
-                    : R.layout.notification_template_big_picture;
+            return R.layout.notification_template_quantum_big_picture;
         }
 
         private int getBigTextLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_template_quantum_big_text
-                    : R.layout.notification_template_big_text;
+            return R.layout.notification_template_quantum_big_text;
         }
 
         private int getInboxLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_template_quantum_inbox
-                    : R.layout.notification_template_inbox;
+            return R.layout.notification_template_quantum_inbox;
         }
 
         private int getActionLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_quantum_action
-                    : R.layout.notification_action;
+            return R.layout.notification_quantum_action;
         }
 
         private int getActionTombstoneLayoutResource() {
-            return mQuantumTheme
-                    ? R.layout.notification_quantum_action_tombstone
-                    : R.layout.notification_action_tombstone;
+            return R.layout.notification_quantum_action_tombstone;
         }
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 209c536..61ff60a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2062,6 +2062,28 @@
     }
 
     /**
+     * Called by a profile owner to disable account management for a specific type of account.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param accountType For which account management is disabled or enabled.
+     * @param disabled The boolean indicating that account management will be disabled (true) or
+     * enabled (false).
+     */
+    public void setAccountManagementDisabled(ComponentName admin, String accountType,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setAccountManagementDisabled(admin, accountType, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Called by profile or device owner to re-enable system apps by intent that were disabled
      * by default when the managed profile was created. This should only be called from a profile
      * or device owner running within a managed profile.
@@ -2081,4 +2103,26 @@
         }
         return 0;
     }
+
+    /**
+     * Gets the array of accounts for which account management is disabled by the profile owner.
+     *
+     * <p> Account management can be disabled/enabled by calling
+     * {@link #setAccountManagementDisabled}.
+     *
+     * @return a list of account types for which account management has been disabled.
+     *
+     * @see #setAccountManagementDisabled
+     */
+    public String[] getAccountTypesWithManagementDisabled() {
+        if (mService != null) {
+            try {
+                return mService.getAccountTypesWithManagementDisabled();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b30f1b9..0096580 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -126,4 +126,7 @@
 
     void enableSystemApp(in ComponentName admin, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
+
+    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
+    String[] getAccountTypesWithManagementDisabled();
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7c625bd..042ee28 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1982,6 +1982,7 @@
             WIFI_SERVICE,
             WIFI_HOTSPOT_SERVICE,
             WIFI_P2P_SERVICE,
+            WIFI_SCANNING_SERVICE,
             NSD_SERVICE,
             AUDIO_SERVICE,
             MEDIA_ROUTER_SERVICE,
@@ -2054,6 +2055,9 @@
      *  <dt> {@link #WIFI_SERVICE} ("wifi")
      *  <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of
      * Wi-Fi connectivity.
+     *  <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
+     *  <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
+     * Wi-Fi Direct connectivity.
      * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
      * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
      * for management of input methods.
@@ -2357,6 +2361,16 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
+     * android.net.wifi.WifiScanner} for scanning the wifi universe
+     *
+     * @see #getSystemService
+     * @see android.net.wifi.WifiScanner
+     * @hide
+     */
+    public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.nsd.NsdManager} for handling management of network service
      * discovery
      *
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index a9a62a7..9ba45ca 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -473,7 +473,7 @@
          *   SyncRequest.Builder builder =
          *     new SyncRequest.Builder()
          *       .setSyncAdapter(dummyAccount, dummyProvider)
-         *       .syncOnce(5 * MINUTES_IN_SECS);
+         *       .syncOnce();
          *
          *   for (String syncData : syncItems) {
          *     Bundle extras = new Bundle();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1a003ff..52a169b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1083,6 +1083,13 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a heart rate monitor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
      * communication support.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d80ab7b..ff96c51 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3600,10 +3600,13 @@
         // For use by the package manager to keep track of the path to the
         // file an app came from.
         public String mScanPath;
-        
-        // For use by package manager to keep track of where it has done dexopt.
-        public boolean mDidDexOpt;
-        
+
+        // For use by package manager to keep track of where it needs to do dexopt.
+        public boolean mDexOptNeeded = true;
+
+        // For use by package manager to keep track of when a package was last used.
+        public long mLastPackageUsageTimeInMills;
+
         // // User set enabled state.
         // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         //
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 28309d7..5f2af8cf 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1277,20 +1277,6 @@
             new Key<Integer>("android.sensor.orientation", int.class);
 
     /**
-     * <p>The number of input samples for each dimension of
-     * {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}.</p>
-     * <p>The number of input samples for the hue, saturation, and value
-     * dimension of {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}. The order of the
-     * dimensions given is hue, saturation, value; where hue is the 0th
-     * element.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP
-     */
-    public static final Key<int[]> SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS =
-            new Key<int[]>("android.sensor.profileHueSatMapDimensions", int[].class);
-
-    /**
      * <p>Optional. Defaults to [OFF]. Lists the supported test
      * pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1d2d0e9..51ea447 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1906,36 +1906,6 @@
             new Key<Rational[]>("android.sensor.neutralColorPoint", Rational[].class);
 
     /**
-     * <p>A mapping containing a hue shift, saturation scale, and value scale
-     * for each pixel.</p>
-     * <p>hue_samples, saturation_samples, and value_samples are given in
-     * {@link CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS android.sensor.profileHueSatMapDimensions}.</p>
-     * <p>Each entry of this map contains three floats corresponding to the
-     * hue shift, saturation scale, and value scale, respectively; where the
-     * hue shift has the lowest index. The map entries are stored in the tag
-     * in nested loop order, with the value divisions in the outer loop, the
-     * hue divisions in the middle loop, and the saturation divisions in the
-     * inner loop. All zero input saturation entries are required to have a
-     * value scale factor of 1.0.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS
-     */
-    public static final Key<float[]> SENSOR_PROFILE_HUE_SAT_MAP =
-            new Key<float[]>("android.sensor.profileHueSatMap", float[].class);
-
-    /**
-     * <p>A list of x,y samples defining a tone-mapping curve for gamma adjustment.</p>
-     * <p>This tag contains a default tone curve that can be applied while
-     * processing the image as a starting point for user adjustments.
-     * The curve is specified as a list of value pairs in linear gamma.
-     * The curve is interpolated using a cubic spline.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     */
-    public static final Key<float[]> SENSOR_PROFILE_TONE_CURVE =
-            new Key<float[]>("android.sensor.profileToneCurve", float[].class);
-
-    /**
      * <p>The worst-case divergence between Bayer green channels.</p>
      * <p>This value is an estimate of the worst case split between the
      * Bayer green channels in the red and blue rows in the sensor color
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3da00b1..30d7043 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1307,14 +1307,13 @@
      * doing something unusual like general internal filtering this may be useful.  On
      * a private network where the proxy is not accessible, you may break HTTP using this.
      *
-     * @param p The a {@link ProxyProperties} object defining the new global
+     * @param p The a {@link ProxyInfo} object defining the new global
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      *
      * <p>This method requires the call to hold the permission
-     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
-     * {@hide}
+     * android.Manifest.permission#CONNECTIVITY_INTERNAL.
      */
-    public void setGlobalProxy(ProxyProperties p) {
+    public void setGlobalProxy(ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
         } catch (RemoteException e) {
@@ -1324,14 +1323,13 @@
     /**
      * Retrieve any network-independent global HTTP proxy.
      *
-     * @return {@link ProxyProperties} for the current global HTTP proxy or {@code null}
+     * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
      *        if no global HTTP proxy is set.
      *
      * <p>This method requires the call to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     * {@hide}
      */
-    public ProxyProperties getGlobalProxy() {
+    public ProxyInfo getGlobalProxy() {
         try {
             return mService.getGlobalProxy();
         } catch (RemoteException e) {
@@ -1343,14 +1341,14 @@
      * Get the HTTP proxy settings for the current default network.  Note that
      * if a global proxy is set, it will override any per-network setting.
      *
-     * @return the {@link ProxyProperties} for the current HTTP proxy, or {@code null} if no
+     * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      *
      * <p>This method requires the call to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
-    public ProxyProperties getProxy() {
+    public ProxyInfo getProxy() {
         try {
             return mService.getProxy();
         } catch (RemoteException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 381a817..d53a856 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -21,7 +21,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
@@ -107,11 +107,11 @@
 
     void reportInetCondition(int networkType, int percentage);
 
-    ProxyProperties getGlobalProxy();
+    ProxyInfo getGlobalProxy();
 
-    void setGlobalProxy(in ProxyProperties p);
+    void setGlobalProxy(in ProxyInfo p);
 
-    ProxyProperties getProxy();
+    ProxyInfo getProxy();
 
     void setDataDependency(int networkType, boolean met);
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4dfd3d9..2dcc544 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.text.TextUtils;
@@ -65,7 +65,7 @@
     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
     private String mDomains;
     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
-    private ProxyProperties mHttpProxy;
+    private ProxyInfo mHttpProxy;
     private int mMtu;
 
     // Stores the properties of links that are "stacked" above this link.
@@ -101,7 +101,7 @@
             mDomains = source.getDomains();
             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
             mHttpProxy = (source.getHttpProxy() == null)  ?
-                    null : new ProxyProperties(source.getHttpProxy());
+                    null : new ProxyInfo(source.getHttpProxy());
             for (LinkProperties l: source.mStackedLinks.values()) {
                 addStackedLink(l);
             }
@@ -295,10 +295,10 @@
         return routes;
     }
 
-    public void setHttpProxy(ProxyProperties proxy) {
+    public void setHttpProxy(ProxyInfo proxy) {
         mHttpProxy = proxy;
     }
-    public ProxyProperties getHttpProxy() {
+    public ProxyInfo getHttpProxy() {
         return mHttpProxy;
     }
 
@@ -720,7 +720,7 @@
                     netProp.addRoute((RouteInfo)in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
-                    netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+                    netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
                 }
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index bea8d1c..8f41e85 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -19,6 +19,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.net.ProxyInfo;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -63,8 +64,11 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
-    /** {@hide} **/
-    public static final String EXTRA_PROXY_INFO = "proxy";
+    /**
+     * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
+     * It describes the new proxy being used (as a {@link ProxyInfo} object).
+     */
+    public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
 
     /** @hide */
     public static final int PROXY_VALID             = 0;
@@ -114,24 +118,14 @@
      */
     public static final java.net.Proxy getProxy(Context ctx, String url) {
         String host = "";
-        if (url != null) {
+        if ((url != null) && !isLocalHost(host)) {
             URI uri = URI.create(url);
-            host = uri.getHost();
-        }
+            ProxySelector proxySelector = ProxySelector.getDefault();
 
-        if (!isLocalHost(host)) {
-            if (sConnectivityManager == null) {
-                sConnectivityManager = (ConnectivityManager)ctx.getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-            }
-            if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
+            List<java.net.Proxy> proxyList = proxySelector.select(uri);
 
-            ProxyProperties proxyProperties = sConnectivityManager.getProxy();
-
-            if (proxyProperties != null) {
-                if (!proxyProperties.isExcluded(host)) {
-                    return proxyProperties.makeProxy();
-                }
+            if (proxyList.size() > 0) {
+                return proxyList.get(0);
             }
         }
         return java.net.Proxy.NO_PROXY;
@@ -275,7 +269,7 @@
     }
 
     /** @hide */
-    public static final void setHttpProxySystemProperty(ProxyProperties p) {
+    public static final void setHttpProxySystemProperty(ProxyInfo p) {
         String host = null;
         String port = null;
         String exclList = null;
@@ -283,8 +277,8 @@
         if (p != null) {
             host = p.getHost();
             port = Integer.toString(p.getPort());
-            exclList = p.getExclusionList();
-            pacFileUrl = p.getPacFileUrl();
+            exclList = p.getExclusionListAsString();
+            pacFileUrl = p.getPacFileUrl().toString();
         }
         setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
     }
diff --git a/core/java/android/net/ProxyProperties.aidl b/core/java/android/net/ProxyInfo.aidl
similarity index 95%
rename from core/java/android/net/ProxyProperties.aidl
rename to core/java/android/net/ProxyInfo.aidl
index 02ea15d..2c91960 100644
--- a/core/java/android/net/ProxyProperties.aidl
+++ b/core/java/android/net/ProxyInfo.aidl
@@ -17,5 +17,5 @@
 
 package android.net;
 
-parcelable ProxyProperties;
+parcelable ProxyInfo;
 
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyInfo.java
similarity index 61%
rename from core/java/android/net/ProxyProperties.java
rename to core/java/android/net/ProxyInfo.java
index 50f45e8..b40941f 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -21,14 +21,23 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import org.apache.http.client.HttpClient;
+
 import java.net.InetSocketAddress;
+import java.net.URLConnection;
+import java.util.List;
 import java.util.Locale;
 
 /**
- * A container class for the http proxy info
- * @hide
+ * Describes a proxy configuration.
+ *
+ * Proxy configurations are already integrated within the Apache HTTP stack.
+ * So {@link URLConnection} and {@link HttpClient} will use them automatically.
+ *
+ * Other HTTP stacks will need to obtain the proxy info from
+ * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
  */
-public class ProxyProperties implements Parcelable {
+public class ProxyInfo implements Parcelable {
 
     private String mHost;
     private int mPort;
@@ -36,32 +45,82 @@
     private String[] mParsedExclusionList;
 
     private String mPacFileUrl;
+    /**
+     *@hide
+     */
     public static final String LOCAL_EXCL_LIST = "";
+    /**
+     *@hide
+     */
     public static final int LOCAL_PORT = -1;
+    /**
+     *@hide
+     */
     public static final String LOCAL_HOST = "localhost";
 
-    public ProxyProperties(String host, int port, String exclList) {
+    /**
+     * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+     * on the specified host and port.
+     */
+    public static ProxyInfo buildDirectProxy(String host, int port) {
+        return new ProxyInfo(host, port, null);
+    }
+
+    /**
+     * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+     * on the specified host and port.
+     *
+     * The proxy will not be used to access any host in exclusion list, exclList.
+     *
+     * @param exclList Hosts to exclude using the proxy on connections for.  These
+     *                 hosts can use wildcards such as *.example.com.
+     */
+    public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) {
+        String[] array = exclList.toArray(new String[exclList.size()]);
+        return new ProxyInfo(host, port, TextUtils.join(",", array), array);
+    }
+
+    /**
+     * Construct a {@link ProxyInfo} that will download and run the PAC script
+     * at the specified URL.
+     */
+    public static ProxyInfo buildPacProxy(Uri pacUri) {
+        return new ProxyInfo(pacUri.toString());
+    }
+
+    /**
+     * Create a ProxyProperties that points at a HTTP Proxy.
+     * @hide
+     */
+    public ProxyInfo(String host, int port, String exclList) {
         mHost = host;
         mPort = port;
         setExclusionList(exclList);
     }
 
-    public ProxyProperties(String pacFileUrl) {
+    /**
+     * Create a ProxyProperties that points at a PAC URL.
+     * @hide
+     */
+    public ProxyInfo(String pacFileUrl) {
         mHost = LOCAL_HOST;
         mPort = LOCAL_PORT;
         setExclusionList(LOCAL_EXCL_LIST);
         mPacFileUrl = pacFileUrl;
     }
 
-    // Only used in PacManager after Local Proxy is bound.
-    public ProxyProperties(String pacFileUrl, int localProxyPort) {
+    /**
+     * Only used in PacManager after Local Proxy is bound.
+     * @hide
+     */
+    public ProxyInfo(String pacFileUrl, int localProxyPort) {
         mHost = LOCAL_HOST;
         mPort = localProxyPort;
         setExclusionList(LOCAL_EXCL_LIST);
         mPacFileUrl = pacFileUrl;
     }
 
-    private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
+    private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
         mHost = host;
         mPort = port;
         mExclusionList = exclList;
@@ -70,16 +129,22 @@
     }
 
     // copy constructor instead of clone
-    public ProxyProperties(ProxyProperties source) {
+    /**
+     * @hide
+     */
+    public ProxyInfo(ProxyInfo source) {
         if (source != null) {
             mHost = source.getHost();
             mPort = source.getPort();
-            mPacFileUrl = source.getPacFileUrl();
-            mExclusionList = source.getExclusionList();
+            mPacFileUrl = source.mPacFileUrl;
+            mExclusionList = source.getExclusionListAsString();
             mParsedExclusionList = source.mParsedExclusionList;
         }
     }
 
+    /**
+     * @hide
+     */
     public InetSocketAddress getSocketAddress() {
         InetSocketAddress inetSocketAddress = null;
         try {
@@ -88,20 +153,46 @@
         return inetSocketAddress;
     }
 
-    public String getPacFileUrl() {
-        return mPacFileUrl;
+    /**
+     * Returns the URL of the current PAC script or null if there is
+     * no PAC script.
+     */
+    public Uri getPacFileUrl() {
+        if (TextUtils.isEmpty(mPacFileUrl)) {
+            return null;
+        }
+        return Uri.parse(mPacFileUrl);
     }
 
+    /**
+     * When configured to use a Direct Proxy this returns the host
+     * of the proxy.
+     */
     public String getHost() {
         return mHost;
     }
 
+    /**
+     * When configured to use a Direct Proxy this returns the port
+     * of the proxy
+     */
     public int getPort() {
         return mPort;
     }
 
-    // comma separated
-    public String getExclusionList() {
+    /**
+     * When configured to use a Direct Proxy this returns the list
+     * of hosts for which the proxy is ignored.
+     */
+    public String[] getExclusionList() {
+        return mParsedExclusionList;
+    }
+
+    /**
+     * comma separated
+     * @hide
+     */
+    public String getExclusionListAsString() {
         return mExclusionList;
     }
 
@@ -111,33 +202,13 @@
         if (mExclusionList == null) {
             mParsedExclusionList = new String[0];
         } else {
-            String splitExclusionList[] = exclusionList.toLowerCase(Locale.ROOT).split(",");
-            mParsedExclusionList = new String[splitExclusionList.length * 2];
-            for (int i = 0; i < splitExclusionList.length; i++) {
-                String s = splitExclusionList[i].trim();
-                if (s.startsWith(".")) s = s.substring(1);
-                mParsedExclusionList[i*2] = s;
-                mParsedExclusionList[(i*2)+1] = "." + s;
-            }
+            mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
         }
     }
 
-    public boolean isExcluded(String url) {
-        if (TextUtils.isEmpty(url) || mParsedExclusionList == null ||
-                mParsedExclusionList.length == 0) return false;
-
-        Uri u = Uri.parse(url);
-        String urlDomain = u.getHost();
-        if (urlDomain == null) return false;
-        for (int i = 0; i< mParsedExclusionList.length; i+=2) {
-            if (urlDomain.equals(mParsedExclusionList[i]) ||
-                    urlDomain.endsWith(mParsedExclusionList[i+1])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    /**
+     * @hide
+     */
     public boolean isValid() {
         if (!TextUtils.isEmpty(mPacFileUrl)) return true;
         return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
@@ -145,6 +216,9 @@
                                                 mExclusionList == null ? "" : mExclusionList);
     }
 
+    /**
+     * @hide
+     */
     public java.net.Proxy makeProxy() {
         java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
         if (mHost != null) {
@@ -179,17 +253,17 @@
 
     @Override
     public boolean equals(Object o) {
-        if (!(o instanceof ProxyProperties)) return false;
-        ProxyProperties p = (ProxyProperties)o;
+        if (!(o instanceof ProxyInfo)) return false;
+        ProxyInfo p = (ProxyInfo)o;
         // If PAC URL is present in either then they must be equal.
         // Other parameters will only be for fall back.
         if (!TextUtils.isEmpty(mPacFileUrl)) {
             return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
         }
-        if (!TextUtils.isEmpty(p.getPacFileUrl())) {
+        if (!TextUtils.isEmpty(p.mPacFileUrl)) {
             return false;
         }
-        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
+        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false;
         if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
             return false;
         }
@@ -245,15 +319,15 @@
      * Implement the Parcelable interface.
      * @hide
      */
-    public static final Creator<ProxyProperties> CREATOR =
-        new Creator<ProxyProperties>() {
-            public ProxyProperties createFromParcel(Parcel in) {
+    public static final Creator<ProxyInfo> CREATOR =
+        new Creator<ProxyInfo>() {
+            public ProxyInfo createFromParcel(Parcel in) {
                 String host = null;
                 int port = 0;
                 if (in.readByte() != 0) {
                     String url = in.readString();
                     int localPort = in.readInt();
-                    return new ProxyProperties(url, localPort);
+                    return new ProxyInfo(url, localPort);
                 }
                 if (in.readByte() != 0) {
                     host = in.readString();
@@ -261,13 +335,13 @@
                 }
                 String exclList = in.readString();
                 String[] parsedExclList = in.readStringArray();
-                ProxyProperties proxyProperties =
-                        new ProxyProperties(host, port, exclList, parsedExclList);
+                ProxyInfo proxyProperties =
+                        new ProxyInfo(host, port, exclList, parsedExclList);
                 return proxyProperties;
             }
 
-            public ProxyProperties[] newArray(int size) {
-                return new ProxyProperties[size];
+            public ProxyInfo[] newArray(int size) {
+                return new ProxyInfo[size];
             }
         };
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d5a3bcb..e0ac60b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2838,6 +2838,8 @@
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SAVED_STATE);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+            MOVED_TO_GLOBAL.add(Settings.Global.WIFI_ENHANCED_AUTO_JOIN);
+            MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORK_SHOW_RSSI);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
@@ -5469,7 +5471,21 @@
        public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
                "wifi_supplicant_scan_interval_ms";
 
-       /**
+        /**
+         * whether frameworks handles wifi auto-join
+         * @hide
+         */
+       public static final String WIFI_ENHANCED_AUTO_JOIN =
+                "wifi_enhanced_auto_join";
+
+        /**
+         * whether settings show RSSI
+         * @hide
+         */
+        public static final String WIFI_NETWORK_SHOW_RSSI =
+                "wifi_network_show_rssi";
+
+        /**
         * The interval in milliseconds to scan at supplicant when p2p is connected
         * @hide
         */
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 11948b2..f234baa 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -264,27 +264,6 @@
 
     private static native int nCallDrawGLFunction(long renderer, long drawGLFunction);
 
-    @Override
-    public int invokeFunctors(Rect dirty) {
-        return nInvokeFunctors(mRenderer, dirty);
-    }
-
-    private static native int nInvokeFunctors(long renderer, Rect dirty);
-
-    @Override
-    public void detachFunctor(long functor) {
-        nDetachFunctor(mRenderer, functor);
-    }
-
-    private static native void nDetachFunctor(long renderer, long functor);
-
-    @Override
-    public void attachFunctor(long functor) {
-        nAttachFunctor(mRenderer, functor);
-    }
-
-    private static native void nAttachFunctor(long renderer, long functor);
-
     ///////////////////////////////////////////////////////////////////////////
     // Memory
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 97339cc..7b49006 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -168,7 +168,6 @@
     private final Rect mRedrawClip = new Rect();
 
     private final int[] mSurfaceSize = new int[2];
-    private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
 
     private long mDrawDelta = Long.MAX_VALUE;
 
@@ -654,6 +653,11 @@
     }
 
     @Override
+    void setOpaque(boolean opaque) {
+        // Not supported
+    }
+
+    @Override
     boolean loadSystemProperties() {
         boolean value;
         boolean changed = false;
@@ -1116,22 +1120,6 @@
         mName = name;
     }
 
-    class FunctorsRunnable implements Runnable {
-        View.AttachInfo attachInfo;
-
-        @Override
-        public void run() {
-            final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
-            if (renderer == null || !renderer.isEnabled() || renderer != GLRenderer.this) {
-                return;
-            }
-
-            if (checkRenderContext() != SURFACE_STATE_ERROR) {
-                mCanvas.invokeFunctors(mRedrawClip);
-            }
-        }
-    }
-
     @Override
     void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
             Rect dirty) {
@@ -1366,23 +1354,6 @@
         }
     }
 
-    @Override
-    void detachFunctor(long functor) {
-        if (mCanvas != null) {
-            mCanvas.detachFunctor(functor);
-        }
-    }
-
-    @Override
-    void attachFunctor(View.AttachInfo attachInfo, long functor) {
-        if (mCanvas != null) {
-            mCanvas.attachFunctor(functor);
-            mFunctorsRunnable.attachInfo = attachInfo;
-            attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
-            attachInfo.mHandler.postDelayed(mFunctorsRunnable,  0);
-        }
-    }
-
     /**
      * Ensures the current EGL context and surface are the ones we expect.
      * This method throws an IllegalStateException if invoked from a thread
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 7ec2cc6..9568760 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -111,45 +111,6 @@
     }
 
     /**
-     * Invoke all the functors who requested to be invoked during the previous frame.
-     *
-     * @param dirty Ignored
-     *
-     * @return Ignored
-     *
-     * @hide
-     */
-    public int invokeFunctors(Rect dirty) {
-        return RenderNode.STATUS_DONE;
-    }
-
-    /**
-     * Detaches the specified functor from the current functor execution queue.
-     *
-     * @param functor The native functor to remove from the execution queue.
-     *
-     * @see #invokeFunctors(android.graphics.Rect)
-     * @see #callDrawGLFunction(long)
-     * @see #detachFunctor(long)
-     *
-     * @hide
-     */
-    abstract void detachFunctor(long functor);
-
-    /**
-     * Attaches the specified functor to the current functor execution queue.
-     *
-     * @param functor The native functor to add to the execution queue.
-     *
-     * @see #invokeFunctors(android.graphics.Rect)
-     * @see #callDrawGLFunction(long)
-     * @see #detachFunctor(long)
-     *
-     * @hide
-     */
-    abstract void attachFunctor(long functor);
-
-    /**
      * Indicates that the specified layer must be updated as soon as possible.
      *
      * @param layer The layer to update
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d31c79d..e366697 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -423,28 +423,6 @@
     abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
 
     /**
-     * Detaches the specified functor from the current functor execution queue.
-     *
-     * @param functor The native functor to remove from the execution queue.
-     *
-     * @see HardwareCanvas#callDrawGLFunction(int)
-     * @see #attachFunctor(android.view.View.AttachInfo, long)
-     */
-    abstract void detachFunctor(long functor);
-
-    /**
-     * Schedules the specified functor in the functors execution queue.
-     *
-     * @param attachInfo AttachInfo tied to this renderer.
-     * @param functor The native functor to insert in the execution queue.
-     *
-     * @see HardwareCanvas#callDrawGLFunction(int)
-     * @see #detachFunctor(long)
-     *
-     */
-    abstract void attachFunctor(View.AttachInfo attachInfo, long functor);
-
-    /**
      * Schedules the functor for execution in either kModeProcess or
      * kModeProcessNoContext, depending on whether or not there is an EGLContext.
      *
@@ -491,6 +469,11 @@
     abstract void setName(String name);
 
     /**
+     * Change the HardwareRenderer's opacity
+     */
+    abstract void setOpaque(boolean opaque);
+
+    /**
      * Creates a hardware renderer using OpenGL.
      *
      * @param translucent True if the surface is translucent, false otherwise
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0bf99d3..2587ba1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -148,6 +148,11 @@
     }
 
     @Override
+    void setOpaque(boolean opaque) {
+        nSetOpaque(mNativeProxy, opaque);
+    }
+
+    @Override
     int getWidth() {
         return mWidth;
     }
@@ -215,16 +220,6 @@
     }
 
     @Override
-    void detachFunctor(long functor) {
-        // no-op, we never attach functors to need to detach them
-    }
-
-    @Override
-    void attachFunctor(AttachInfo attachInfo, long functor) {
-        invokeFunctor(functor, true);
-    }
-
-    @Override
     void invokeFunctor(long functor, boolean waitForCompletion) {
         nInvokeFunctor(mNativeProxy, functor, waitForCompletion);
     }
@@ -312,6 +307,7 @@
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native void nPauseSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height);
+    private static native void nSetOpaque(long nativeProxy, boolean opaque);
     private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
     private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d8fcfc5..bef96b1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,7 +16,9 @@
 
 package android.view;
 
+import android.animation.AnimatorInflater;
 import android.animation.RevealAnimator;
+import android.animation.StateListAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -667,6 +669,7 @@
  * @attr ref android.R.styleable#View_scrollbarTrackVertical
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
+ * @attr ref android.R.styleable#View_stateListAnimator
  * @attr ref android.R.styleable#View_sharedElementName
  * @attr ref android.R.styleable#View_soundEffectsEnabled
  * @attr ref android.R.styleable#View_tag
@@ -3258,6 +3261,11 @@
     private Outline mOutline;
 
     /**
+     * Animator that automatically runs based on state changes.
+     */
+    private StateListAnimator mStateListAnimator;
+
+    /**
      * When this view has focus and the next focus is {@link #FOCUS_LEFT},
      * the user may specify which view to go to next.
      */
@@ -3995,6 +4003,10 @@
                 case R.styleable.View_nestedScrollingEnabled:
                     setNestedScrollingEnabled(a.getBoolean(attr, false));
                     break;
+                case R.styleable.View_stateListAnimator:
+                    setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
+                            a.getResourceId(attr, 0)));
+                    break;
             }
         }
 
@@ -10620,6 +10632,40 @@
     }
 
     /**
+     * Returns the current StateListAnimator if exists.
+     *
+     * @return StateListAnimator or null if it does not exists
+     * @see    #setStateListAnimator(android.animation.StateListAnimator)
+     */
+    public StateListAnimator getStateListAnimator() {
+        return mStateListAnimator;
+    }
+
+    /**
+     * Attaches the provided StateListAnimator to this View.
+     * <p>
+     * Any previously attached StateListAnimator will be detached.
+     *
+     * @param stateListAnimator The StateListAnimator to update the view
+     * @see {@link android.animation.StateListAnimator}
+     */
+    public void setStateListAnimator(StateListAnimator stateListAnimator) {
+        if (mStateListAnimator == stateListAnimator) {
+            return;
+        }
+        if (mStateListAnimator != null) {
+            mStateListAnimator.setTarget(null);
+        }
+        mStateListAnimator = stateListAnimator;
+        if (stateListAnimator != null) {
+            stateListAnimator.setTarget(this);
+            if (isAttachedToWindow()) {
+                stateListAnimator.setState(getDrawableState());
+            }
+        }
+    }
+
+    /**
      * Sets the outline of the view, which defines the shape of the shadow it
      * casts.
      * <p>
@@ -12835,7 +12881,6 @@
         destroyLayer(false);
 
         cleanupDraw();
-
         mCurrentAnimation = null;
     }
 
@@ -15489,9 +15534,11 @@
     /**
      * This function is called whenever the state of the view changes in such
      * a way that it impacts the state of drawables being shown.
-     *
-     * <p>Be sure to call through to the superclass when overriding this
-     * function.
+     * <p>
+     * If the View has a StateListAnimator, it will also be called to run necessary state
+     * change animations.
+     * <p>
+     * Be sure to call through to the superclass when overriding this function.
      *
      * @see Drawable#setState(int[])
      */
@@ -15500,6 +15547,10 @@
         if (d != null && d.isStateful()) {
             d.setState(getDrawableState());
         }
+
+        if (mStateListAnimator != null) {
+            mStateListAnimator.setState(getDrawableState());
+        }
     }
 
     /**
@@ -15644,11 +15695,17 @@
     /**
      * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}
      * on all Drawable objects associated with this view.
+     * <p>
+     * Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator
+     * attached to this view.
      */
     public void jumpDrawablesToCurrentState() {
         if (mBackground != null) {
             mBackground.jumpToCurrentState();
         }
+        if (mStateListAnimator != null) {
+            mStateListAnimator.jumpToCurrentState();
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index db87394..9b09d85 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -665,18 +665,9 @@
         mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
     }
 
-    public void attachFunctor(long functor) {
-        //noinspection SimplifiableIfStatement
-        if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
-            mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
-        }
-    }
-
     public void detachFunctor(long functor) {
+        // TODO: Make the resize buffer some other way to not need this block
         mBlockResizeBuffer = true;
-        if (mAttachInfo.mHardwareRenderer != null) {
-            mAttachInfo.mHardwareRenderer.detachFunctor(functor);
-        }
     }
 
     public boolean invokeFunctor(long functor, boolean waitForCompletion) {
@@ -719,12 +710,15 @@
 
             if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
                     && forceHwAccelerated)) {
-                // Don't enable hardware acceleration when we're not on the main thread
-                if (!HardwareRenderer.sSystemRendererDisabled &&
-                        Looper.getMainLooper() != Looper.myLooper()) {
-                    Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
-                            + "acceleration outside of the main thread, aborting");
-                    return;
+                if (!HardwareRenderer.sUseRenderThread) {
+                    // TODO: Delete
+                    // Don't enable hardware acceleration when we're not on the main thread
+                    if (!HardwareRenderer.sSystemRendererDisabled &&
+                            Looper.getMainLooper() != Looper.myLooper()) {
+                        Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware "
+                                + "acceleration outside of the main thread, aborting");
+                        return;
+                    }
                 }
 
                 if (mAttachInfo.mHardwareRenderer != null) {
@@ -6174,8 +6168,10 @@
     }
 
     void changeCanvasOpacity(boolean opaque) {
-        // TODO(romainguy): recreate Canvas (software or hardware) to reflect the opacity change.
         Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.setOpaque(opaque);
+        }
     }
 
     class TakenSurfaceHolder extends BaseSurfaceHolder {
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index cde8080..e4ad354 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -276,6 +276,8 @@
      * @see Intent#ACTION_SEND_MULTIPLE
      */
     public void setShareIntent(Intent shareIntent) {
+        shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+                        Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
             mShareHistoryFileName);
         dataModel.setIntent(shareIntent);
@@ -292,7 +294,8 @@
             final int itemId = item.getItemId();
             Intent launchIntent = dataModel.chooseActivity(itemId);
             if (launchIntent != null) {
-                launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+                launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+                        Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
                 mContext.startActivity(launchIntent);
             }
             return true;
@@ -308,7 +311,7 @@
             return;
         }
         if (mOnChooseActivityListener == null) {
-            mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy();
+            mOnChooseActivityListener = new ShareActivityChooserModelPolicy();
         }
         ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
         dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
@@ -317,7 +320,7 @@
     /**
      * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
      */
-    private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener {
+    private class ShareActivityChooserModelPolicy implements OnChooseActivityListener {
         @Override
         public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
             if (mOnShareTargetSelectedListener != null) {
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 8cdaf91..abd1791 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,156 +18,122 @@
 
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
+import android.graphics.Color;
 import android.graphics.Typeface;
-import android.provider.Settings;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
-import android.text.method.AllCapsTransformationMethod;
+import android.provider.Settings;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 public class PlatLogoActivity extends Activity {
-    FrameLayout mContent;
-    int mCount;
-    final Handler mHandler = new Handler();
-    static final int BGCOLOR = 0xffed1d24;
+    private static class Torso extends FrameLayout {
+        boolean mAnimate = false;
+        TextView mText;
+
+        public Torso(Context context) {
+            this(context, null);
+        }
+        public Torso(Context context, AttributeSet attrs) {
+            this(context, attrs, 0);
+        }
+        public Torso(Context context, AttributeSet attrs, int flags) {
+            super(context, attrs, flags);
+
+            for (int i=0; i<2; i++) {
+                final View v = new View(context);
+                v.setBackgroundColor(i % 2 == 0 ? Color.BLUE : Color.RED);
+                addView(v);
+            }
+
+            mText = new TextView(context);
+            mText.setTextColor(Color.BLACK);
+            mText.setTextSize(14 /* sp */);
+            mText.setTypeface(Typeface.create("monospace", Typeface.BOLD));
+
+            addView(mText, new FrameLayout.LayoutParams(
+                    FrameLayout.LayoutParams.MATCH_PARENT,
+                    FrameLayout.LayoutParams.WRAP_CONTENT,
+                    Gravity.BOTTOM | Gravity.LEFT
+            ));
+        }
+
+        private Runnable mRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mText.setText(String.format("android_%s.flv - build %s",
+                        Build.VERSION.CODENAME,
+                        Build.VERSION.INCREMENTAL));
+                final int N = getChildCount();
+                final float parentw = getMeasuredWidth();
+                final float parenth = getMeasuredHeight();
+                for (int i=0; i<N; i++) {
+                    final View v = getChildAt(i);
+                    if (v instanceof TextView) continue;
+
+                    final int w = (int) (Math.random() * parentw);
+                    final int h = (int) (Math.random() * parenth);
+                    v.setLayoutParams(new FrameLayout.LayoutParams(w, h));
+
+                    v.setX((float) Math.random() * (parentw - w));
+                    v.setY((float) Math.random() * (parenth - h));
+                }
+
+                if (mAnimate) postDelayed(this, 1000);
+            }
+        };
+        @Override
+        protected void onAttachedToWindow() {
+            mAnimate = true;
+            post(mRunnable);
+        }
+        @Override
+        protected void onDetachedFromWindow() {
+            mAnimate = false;
+            removeCallbacks(mRunnable);
+        }
+    }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        DisplayMetrics metrics = new DisplayMetrics();
-        getWindowManager().getDefaultDisplay().getMetrics(metrics);
+        final Torso t = new Torso(this);
+        t.setBackgroundColor(Color.WHITE);
 
-        Typeface bold = Typeface.create("sans-serif", Typeface.BOLD);
-        Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL);
+        t.getChildAt(0)
+                .setOnLongClickListener(new View.OnLongClickListener() {
+                    @Override
+                    public boolean onLongClick(View v) {
+                        final ContentResolver cr = getContentResolver();
+                        if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0)
+                                == 0) {
+                            // For posterity: the moment this user unlocked the easter egg
+                            Settings.System.putLong(cr,
+                                    Settings.System.EGG_MODE,
+                                    System.currentTimeMillis());
+                        }
+                        try {
+                            startActivity(new Intent(Intent.ACTION_MAIN)
+                                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                                    .addCategory("com.android.internal.category.PLATLOGO"));
+                        } catch (ActivityNotFoundException ex) {
+                            android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
+                        }
+                        finish();
+                        return true;
+                    }
+                });
 
-        mContent = new FrameLayout(this);
-        mContent.setBackgroundColor(0xC0000000);
-        
-        final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.WRAP_CONTENT,
-                FrameLayout.LayoutParams.WRAP_CONTENT);
-        lp.gravity = Gravity.CENTER;
-
-        final ImageView logo = new ImageView(this);
-        logo.setImageResource(com.android.internal.R.drawable.platlogo);
-        logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        logo.setVisibility(View.INVISIBLE);
-
-        final View bg = new View(this);
-        bg.setBackgroundColor(BGCOLOR);
-        bg.setAlpha(0f);
-
-        final TextView letter = new TextView(this);
-
-        letter.setTypeface(bold);
-        letter.setTextSize(300);
-        letter.setTextColor(0xFFFFFFFF);
-        letter.setGravity(Gravity.CENTER);
-        letter.setText(String.valueOf(Build.ID).substring(0, 1));
-
-        final int p = (int)(4 * metrics.density);
-
-        final TextView tv = new TextView(this);
-        if (light != null) tv.setTypeface(light);
-        tv.setTextSize(30);
-        tv.setPadding(p, p, p, p);
-        tv.setTextColor(0xFFFFFFFF);
-        tv.setGravity(Gravity.CENTER);
-        tv.setTransformationMethod(new AllCapsTransformationMethod(this));
-        tv.setText("Android " + Build.VERSION.RELEASE);
-        tv.setVisibility(View.INVISIBLE);
-
-        mContent.addView(bg);
-        mContent.addView(letter, lp);
-        mContent.addView(logo, lp);
-
-        final FrameLayout.LayoutParams lp2 = new FrameLayout.LayoutParams(lp);
-        lp2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
-        lp2.bottomMargin = 10*p;
-
-        mContent.addView(tv, lp2);
-
-        mContent.setOnClickListener(new View.OnClickListener() {
-            int clicks;
-            @Override
-            public void onClick(View v) {
-                clicks++;
-                if (clicks >= 6) {
-                    mContent.performLongClick();
-                    return;
-                }
-                letter.animate().cancel();
-                final float offset = (int)letter.getRotation() % 360;
-                letter.animate()
-                    .rotationBy((Math.random() > 0.5f ? 360 : -360) - offset)
-                    .setInterpolator(new DecelerateInterpolator())
-                    .setDuration(700).start();
-            }
-        });
-
-        mContent.setOnLongClickListener(new View.OnLongClickListener() {
-            @Override
-            public boolean onLongClick(View v) {
-                if (logo.getVisibility() != View.VISIBLE) {
-                    bg.setScaleX(0.01f);
-                    bg.animate().alpha(1f).scaleX(1f).setStartDelay(500).start();
-                    letter.animate().alpha(0f).scaleY(0.5f).scaleX(0.5f)
-                            .rotationBy(360)
-                            .setInterpolator(new AccelerateInterpolator())
-                            .setDuration(1000)
-                            .start();
-                    logo.setAlpha(0f);
-                    logo.setVisibility(View.VISIBLE);
-                    logo.setScaleX(0.5f);
-                    logo.setScaleY(0.5f);
-                    logo.animate().alpha(1f).scaleX(1f).scaleY(1f)
-                        .setDuration(1000).setStartDelay(500)
-                        .setInterpolator(new AnticipateOvershootInterpolator())
-                        .start();
-                    tv.setAlpha(0f);
-                    tv.setVisibility(View.VISIBLE);
-                    tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start();
-                    return true;
-                }
-                return false;
-            }
-        });
-
-        logo.setOnLongClickListener(new View.OnLongClickListener() {
-            @Override
-            public boolean onLongClick(View v) {
-                if (Settings.System.getLong(getContentResolver(), Settings.System.EGG_MODE, 0)
-                        == 0) {
-                    // For posterity: the moment this user unlocked the easter egg
-                    Settings.System.putLong(getContentResolver(),
-                            Settings.System.EGG_MODE,
-                            System.currentTimeMillis());
-                }
-                try {
-                    startActivity(new Intent(Intent.ACTION_MAIN)
-                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        .addCategory("com.android.internal.category.PLATLOGO"));
-                } catch (ActivityNotFoundException ex) {
-                    android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
-                }
-                finish();
-                return true;
-            }
-        });
-        
-        setContentView(mContent);
+        setContentView(t);
     }
 }
diff --git a/core/java/com/android/internal/util/LegacyNotificationUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
similarity index 93%
rename from core/java/com/android/internal/util/LegacyNotificationUtil.java
rename to core/java/com/android/internal/util/NotificationColorUtil.java
index 0394bbc..f38cbde 100644
--- a/core/java/com/android/internal/util/LegacyNotificationUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.TextAppearanceSpan;
@@ -38,21 +39,21 @@
  *
  * @hide
  */
-public class LegacyNotificationUtil {
+public class NotificationColorUtil {
 
-    private static final String TAG = "LegacyNotificationUtil";
+    private static final String TAG = "NotificationColorUtil";
 
     private static final Object sLock = new Object();
-    private static LegacyNotificationUtil sInstance;
+    private static NotificationColorUtil sInstance;
 
     private final ImageUtils mImageUtils = new ImageUtils();
     private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
             new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
 
-    public static LegacyNotificationUtil getInstance() {
+    public static NotificationColorUtil getInstance() {
         synchronized (sLock) {
             if (sInstance == null) {
-                sInstance = new LegacyNotificationUtil();
+                sInstance = new NotificationColorUtil();
             }
             return sInstance;
         }
@@ -107,6 +108,9 @@
             AnimationDrawable ad = (AnimationDrawable) d;
             int count = ad.getNumberOfFrames();
             return count > 0 && isGrayscale(ad.getFrame(0));
+        } else if (d instanceof VectorDrawable) {
+            // We just assume you're doing the right thing if using vectors
+            return true;
         } else {
             return false;
         }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index b380403..bc92c4a 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -46,6 +46,8 @@
     public static final int BASE_WIFI_MONITOR                                       = 0x00024000;
     public static final int BASE_WIFI_MANAGER                                       = 0x00025000;
     public static final int BASE_WIFI_CONTROLLER                                    = 0x00026000;
+    public static final int BASE_WIFI_SCANNER                                       = 0x00027000;
+    public static final int BASE_WIFI_SCANNER_SERVICE                               = 0x00027100;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3aa179d..7b2f829 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -206,32 +206,6 @@
     return renderer->callDrawGLFunction(functor, dirty);
 }
 
-static void android_view_GLES20Canvas_detachFunctor(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jlong functorPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    renderer->detachFunctor(functor);
-}
-
-static void android_view_GLES20Canvas_attachFunctor(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jlong functorPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    renderer->attachFunctor(functor);
-}
-
-static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jobject dirty) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    android::uirenderer::Rect bounds;
-    status_t status = renderer->invokeFunctors(bounds);
-    if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
-        env->CallVoidMethod(dirty, gRectClassInfo.set,
-                int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
-    }
-    return status;
-}
-
 // ----------------------------------------------------------------------------
 // Misc
 // ----------------------------------------------------------------------------
@@ -1007,10 +981,6 @@
     { "nGetStencilSize",    "()I",             (void*) android_view_GLES20Canvas_getStencilSize },
 
     { "nCallDrawGLFunction", "(JJ)I",          (void*) android_view_GLES20Canvas_callDrawGLFunction },
-    { "nDetachFunctor",      "(JJ)V",          (void*) android_view_GLES20Canvas_detachFunctor },
-    { "nAttachFunctor",      "(JJ)V",          (void*) android_view_GLES20Canvas_attachFunctor },
-    { "nInvokeFunctors",     "(JLandroid/graphics/Rect;)I",
-            (void*) android_view_GLES20Canvas_invokeFunctors },
 
     { "nSave",              "(JI)I",           (void*) android_view_GLES20Canvas_save },
     { "nRestore",           "(J)V",            (void*) android_view_GLES20Canvas_restore },
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 2e8dccf..8a426ac 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -74,15 +74,10 @@
 } gRectClassInfo;
 
 static struct {
-    jfieldID mFinalizer;
-    jfieldID mNativeCanvas;
     jfieldID mSurfaceFormat;
+    jmethodID safeCanvasSwap;
 } gCanvasClassInfo;
 
-static struct {
-    jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
 #define GET_INT(object, field) \
     env->GetIntField(object, field)
 
@@ -146,15 +141,6 @@
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
-    jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
-    SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
-            GET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas));
-    SET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas, (long) newCanvas);
-    SET_LONG(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (long) newCanvas);
-    SkSafeUnref(previousCanvas);
-}
-
 static inline SkBitmap::Config convertPixelFormat(int32_t format) {
     switch (format) {
         case PIXEL_FORMAT_RGBA_8888:
@@ -213,7 +199,7 @@
     SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
 
     SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     SkRect clipRect;
     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
@@ -233,7 +219,7 @@
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
     SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     if (wrapper) {
         status_t status = wrapper->buffer->unlock();
@@ -332,13 +318,8 @@
     GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
-            "Landroid/graphics/Canvas$CanvasFinalizer;");
-    GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
     GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
-
-    FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
-    GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
+    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
 
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index ab6c1e0..6c9d060 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -69,15 +69,10 @@
 } gRectClassInfo;
 
 static struct {
-    jfieldID mFinalizer;
-    jfieldID mNativeCanvas;
     jfieldID mSurfaceFormat;
+    jmethodID safeCanvasSwap;
 } gCanvasClassInfo;
 
-static struct {
-    jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
 // ----------------------------------------------------------------------------
 
 // this is just a pointer we use to pass to inc/decStrong
@@ -191,15 +186,6 @@
     }
 }
 
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
-  jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
-  SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
-          env->GetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas));
-  env->SetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas, (jlong)newCanvas);
-  env->SetLongField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (jlong)newCanvas);
-  SkSafeUnref(previousCanvas);
-}
-
 static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
         jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
     sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
@@ -247,7 +233,7 @@
     }
 
     SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    swapCanvasPtr(env, canvasObj, nativeCanvas);
+    env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     if (dirtyRectPtr) {
         nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) );
@@ -277,7 +263,7 @@
 
     // detach the canvas from the surface
     SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    swapCanvasPtr(env, canvasObj, nativeCanvas);
+    env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
@@ -388,12 +374,8 @@
     gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(J)V");
 
     clazz = env->FindClass("android/graphics/Canvas");
-    gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;");
-    gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "J");
     gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I");
-
-    clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer");
-    gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "J");
+    gCanvasClassInfo.safeCanvasSwap = env->GetMethodID(clazz, "safeCanvasSwap", "(JZ)V");
 
     clazz = env->FindClass("android/graphics/Rect");
     gRectClassInfo.left = env->GetFieldID(clazz, "left", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 77ede33..9258543 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -44,16 +44,11 @@
 } gRectClassInfo;
 
 static struct {
-    jfieldID mFinalizer;
-    jfieldID mNativeCanvas;
     jfieldID mSurfaceFormat;
+    jmethodID safeCanvasSwap;
 } gCanvasClassInfo;
 
 static struct {
-    jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
-static struct {
     jfieldID nativeWindow;
 } gTextureViewClassInfo;
 
@@ -125,15 +120,6 @@
     }
 }
 
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
-    jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
-    SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
-          env->GetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas));
-    env->SetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas, (jlong)newCanvas);
-    env->SetLongField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (jlong)newCanvas);
-    SkSafeUnref(previousCanvas);
-}
-
 static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
         jlong nativeWindow, jobject canvas, jobject dirtyRect) {
 
@@ -175,7 +161,7 @@
     SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format);
 
     SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     SkRect clipRect;
     clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
@@ -193,7 +179,7 @@
         jlong nativeWindow, jobject canvas) {
 
     SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
@@ -241,13 +227,8 @@
     GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
-            "Landroid/graphics/Canvas$CanvasFinalizer;");
-    GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
     GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
-
-    FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
-    GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
+    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
 
     FIND_CLASS(clazz, "android/view/TextureView");
     GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "J");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6ff28e3..cdd036e 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -197,6 +197,12 @@
     proxy->setup(width, height);
 }
 
+static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean opaque) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setOpaque(opaque);
+}
+
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
         jint dirtyRight, jint dirtyBottom) {
@@ -279,6 +285,7 @@
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
+    { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
     { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 241500a..cdb77f1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1295,10 +1295,9 @@
     <!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
          that removes restrictions on where broadcasts can be sent and allows other
          types of interactions. -->
-    <!-- TODO: Remove the system protection level.-->
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signature|system"
+        android:protectionLevel="signature"
         android:label="@string/permlab_interactAcrossUsersFull"
         android:description="@string/permdesc_interactAcrossUsersFull" />
 
diff --git a/core/res/res/anim/button_state_list_anim_quantum.xml b/core/res/res/anim/button_state_list_anim_quantum.xml
new file mode 100644
index 0000000..01989a4
--- /dev/null
+++ b/core/res/res/anim/button_state_list_anim_quantum.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:state_enabled="true">
+        <set>
+            <objectAnimator android:propertyName="translationZ"
+                            android:duration="@integer/button_pressed_animation_duration"
+                            android:valueTo="@dimen/button_pressed_z"
+                            android:valueType="floatType"/>
+        </set>
+    </item>
+    <!-- base state -->
+    <item>
+        <set>
+            <objectAnimator android:propertyName="translationZ"
+                            android:duration="@integer/button_pressed_animation_duration"
+                            android:valueTo="0"
+                            android:valueType="floatType"/>
+        </set>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb_am.png b/core/res/res/drawable-hdpi/stat_sys_adb_am.png
deleted file mode 100644
index dad614c..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_adb_am.png b/core/res/res/drawable-ldpi/stat_sys_adb_am.png
deleted file mode 100644
index 0171adb..0000000
--- a/core/res/res/drawable-ldpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb_am.png b/core/res/res/drawable-mdpi/stat_sys_adb_am.png
deleted file mode 100644
index 5482f34..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
deleted file mode 100644
index 6351c2d..0000000
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
new file mode 100644
index 0000000..8eb00fa
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -0,0 +1,39 @@
+<!--
+    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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size android:width="400dp" android:height="400dp"/>
+
+    <viewport android:viewportHeight="25" android:viewportWidth="25" />
+
+    <group>
+        <path
+            android:name="shadow"
+            android:pathData="m12,2.5 a11,11 0 1,0 1,0
+            M6.5,7.5
+            l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+            android:fill="#40000000"
+            />
+        <path
+            android:name="circle-L-ranch"
+            android:pathData="m12,1.5 a11,11 0 1,0 1,0
+            M6.5,6.5
+            l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+            android:fill="#FFFFFF40"
+            />
+    </group>
+</vector>
+
+
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
new file mode 100644
index 0000000..37df348
--- /dev/null
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -0,0 +1,30 @@
+<!--
+    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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size android:width="25dp" android:height="25dp"/>
+
+    <viewport android:viewportHeight="25" android:viewportWidth="25" />
+
+    <group>
+        <path
+            android:name="adb"
+            android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z"
+            android:fill="#FFFFFFFF"
+            />
+    </group>
+</vector>
+
+
diff --git a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xhdpi/stat_sys_adb_am.png
deleted file mode 100644
index e53f498..0000000
--- a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
deleted file mode 100644
index d6018dd..0000000
--- a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/drawable/notification_icon_legacy_bg.xml
index 4ac67c3..cc5755d 100644
--- a/core/res/res/drawable/notification_icon_legacy_bg.xml
+++ b/core/res/res/drawable/notification_icon_legacy_bg.xml
@@ -18,5 +18,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
     <solid
-            android:color="@color/notification_icon_legacy_bg_color"/>
+            android:color="@color/notification_icon_bg_color"/>
 </shape>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0326e18..172877f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2364,6 +2364,9 @@
         <!-- Specifies that this view should permit nested scrolling within a compatible
              ancestor view. -->
         <attr name="nestedScrollingEnabled" format="boolean" />
+
+        <!-- Sets the state-based animator for the View. -->
+        <attr name="stateListAnimator" format="reference"/>
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -4255,6 +4258,11 @@
         <attr name="drawable" format="reference" />
     </declare-styleable>
 
+    <!-- Attributes that can be assigned to a StateListAnimator item. -->
+    <declare-styleable name="StateListAnimatorItem">
+        <attr name="animation"/>
+    </declare-styleable>
+
     <!-- Drawable used to render a geometric shape, with a gradient or a solid color. -->
     <declare-styleable name="GradientDrawable">
         <!-- Indicates whether the drawable should intially be visible. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 761170d..7d3fb44 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -123,7 +123,7 @@
     <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
 
-    <color name="notification_icon_legacy_bg_color">#ff4285F4</color>
+    <color name="notification_icon_bg_color">#ffa3a3a3</color>
     <color name="notification_action_legacy_color_filter">#ff555555</color>
 
     <!-- Keyguard colors -->
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index cebee12..53e97fd 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -49,4 +49,7 @@
 
     <dimen name="floating_window_z">16dp</dimen>
     <dimen name="floating_window_margin">32dp</dimen>
+
+    <!-- the amount of elevation for pressed button state-->
+    <dimen name="button_pressed_z">2dp</dimen>
 </resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index dc90bbf..e6748c4 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -19,4 +19,5 @@
 <resources>
     <integer name="kg_carousel_angle">75</integer>
     <integer name="kg_glowpad_rotation_offset">0</integer>
+    <integer name="button_pressed_animation_duration">100</integer>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8874c30..d0cdefe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2171,6 +2171,7 @@
   <public type="attr" name="actionOverflowMenuStyle" />
   <public type="attr" name="documentLaunchMode" />
   <public type="attr" name="autoRemoveFromRecents" />
+  <public type="attr" name="stateListAnimator" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 01c3017..88a2a9f 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -364,6 +364,7 @@
         <item name="textColor">?attr/textColorPrimary</item>
         <item name="minHeight">48dip</item>
         <item name="minWidth">96dip</item>
+        <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item>
     </style>
 
     <!-- Small bordered ink button -->
@@ -375,6 +376,7 @@
     <!-- Borderless ink button -->
     <style name="Widget.Quantum.Button.Borderless">
         <item name="background">@drawable/btn_borderless_quantum</item>
+        <item name="stateListAnimator">@null</item>
     </style>
 
     <!-- Small borderless ink button -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8a4b51..2bf72e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1664,6 +1664,7 @@
   <java-symbol type="layout" name="notification_template_quantum_big_text" />
   <java-symbol type="layout" name="notification_template_quantum_inbox" />
   <java-symbol type="color" name="notification_action_legacy_color_filter" />
+  <java-symbol type="color" name="notification_icon_bg_color" />
   <java-symbol type="drawable" name="notification_icon_legacy_bg_inset" />
   <java-symbol type="drawable" name="notification_quantum_bg_dim" />
   <java-symbol type="drawable" name="notification_quantum_bg" />
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 39c8beb..768fd9a 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -123,7 +123,7 @@
         <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item>
 
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
-        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
@@ -467,7 +467,7 @@
         <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.Light.TextView.ListSeparator</item>
 
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
-        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
diff --git a/core/tests/coretests/res/anim/reset_state_anim.xml b/core/tests/coretests/res/anim/reset_state_anim.xml
new file mode 100644
index 0000000..918d0a3
--- /dev/null
+++ b/core/tests/coretests/res/anim/reset_state_anim.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+    <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/core/tests/coretests/res/anim/test_state_anim.xml b/core/tests/coretests/res/anim/test_state_anim.xml
new file mode 100644
index 0000000..9e08f68
--- /dev/null
+++ b/core/tests/coretests/res/anim/test_state_anim.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+        </set>
+    </item>
+    <item android:state_enabled="true" android:state_pressed="false">
+        <set>
+            <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+            <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+        </set>
+    </item>
+    <!-- base state-->
+    <item android:animation="@anim/reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
new file mode 100644
index 0000000..38df78d
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -0,0 +1,102 @@
+/*
+* 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.animation;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.util.StateSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+
+    public StateListAnimatorTest() {
+        super(BasicAnimatorActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testInflateFromAnimator() throws Exception {
+        StateListAnimator stateListAnimator = AnimatorInflater
+                .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+        assertNotNull("A state list animator should be returned", stateListAnimator);
+        assertEquals("State list animator should have three items", 3,
+                stateListAnimator.getTuples().size());
+    }
+
+    @UiThreadTest
+    public void testAttachDetach() throws Exception {
+        View view = new View(getActivity());
+        final AtomicInteger setStateCount = new AtomicInteger(0);
+        StateListAnimator stateListAnimator = new StateListAnimator() {
+            @Override
+            public void setState(int[] state) {
+                setStateCount.incrementAndGet();
+                super.setState(state);
+            }
+        };
+        view.setStateListAnimator(stateListAnimator);
+        assertNotNull("State list animator should have a reference to view even if it is detached",
+                stateListAnimator.getTarget());
+        ViewGroup viewGroup = (ViewGroup) getActivity().findViewById(android.R.id.content);
+        int preSetStateCount = setStateCount.get();
+        viewGroup.addView(view);
+        assertTrue("When view is attached, state list drawable's setState should be called",
+                preSetStateCount < setStateCount.get());
+
+        StateListAnimator stateListAnimator2 = new StateListAnimator();
+        view.setStateListAnimator(stateListAnimator2);
+        assertNull("When a new state list animator is assigned, previous one should be detached",
+                stateListAnimator.getTarget());
+        assertNull("Any running animator should be removed on detach",
+                stateListAnimator.getRunningAnimator());
+        assertEquals("The new state list animator should be attached to the view",
+                view, stateListAnimator2.getTarget());
+        viewGroup.removeView(view);
+        assertNotNull("When view is detached from window, state list animator should still keep the"
+                        + " reference",
+                stateListAnimator2.getTarget());
+    }
+
+    public void testStateListLoading() throws InterruptedException {
+        StateListAnimator stateListAnimator = AnimatorInflater
+                .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+        assertNotNull("A state list animator should be returned", stateListAnimator);
+        assertEquals("Steate list animator should have two items", 3,
+                stateListAnimator.getTuples().size());
+        StateListAnimator.Tuple tuple1 = stateListAnimator.getTuples().get(0);
+        assertEquals("first tuple should have one state", 1, tuple1.getSpecs().length);
+        assertEquals("first spec in tuple 1 should be pressed",
+                com.android.internal.R.attr.state_pressed, tuple1.getSpecs()[0]);
+
+        StateListAnimator.Tuple tuple2 = stateListAnimator.getTuples().get(1);
+        assertEquals("Second tuple should have two specs", 2, tuple2.getSpecs().length);
+        assertTrue("Tuple two should match the expected state",
+                StateSet.stateSetMatches(tuple2.getSpecs(),
+                new int[]{-com.android.internal.R.attr.state_pressed,
+                com.android.internal.R.attr.state_enabled}));
+    }
+}
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index c263a84..a40085b 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -154,7 +154,7 @@
             getMatrix(mMatrix);
             canvas.concat(mMatrix);
         } else {
-            nativeApplyToCanvas(canvas.mNativeCanvas);
+            nativeApplyToCanvas(canvas.getNativeCanvas());
         }
     }
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index ae3eae1..5b18623 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -39,8 +39,12 @@
 public class Canvas {
 
     // assigned in constructors or setBitmap, freed in finalizer
+    private long mNativeCanvas;
+
     /** @hide */
-    public long mNativeCanvas;
+    public long getNativeCanvas() {
+        return mNativeCanvas;
+    }
 
     // may be null
     private Bitmap mBitmap;
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 69089b1..6ff5f4f 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -164,12 +164,12 @@
     }
 
     void drawSoftware(Canvas canvas, RectF location, Paint paint) {
-        nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+        nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
                 paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
     }
 
     void drawSoftware(Canvas canvas, Rect location, Paint paint) {
-        nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk,
+        nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk,
                 paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity);
     }
 
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 25188e0..a16c099 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -107,7 +107,7 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
-        nativeDraw(canvas.mNativeCanvas, mNativePicture);
+        nativeDraw(canvas.getNativeCanvas(), mNativePicture);
     }
 
     /**
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 95fdb04..6de369c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -379,49 +379,9 @@
     dirtyClip();
 }
 
-void OpenGLRenderer::detachFunctor(Functor* functor) {
-    mFunctors.remove(functor);
-}
-
-void OpenGLRenderer::attachFunctor(Functor* functor) {
-    mFunctors.add(functor);
-}
-
-status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
-    status_t result = DrawGlInfo::kStatusDone;
-    size_t count = mFunctors.size();
-
-    if (count > 0) {
-        interrupt();
-        SortedVector<Functor*> functors(mFunctors);
-        mFunctors.clear();
-
-        DrawGlInfo info;
-        info.clipLeft = 0;
-        info.clipTop = 0;
-        info.clipRight = 0;
-        info.clipBottom = 0;
-        info.isLayer = false;
-        info.width = 0;
-        info.height = 0;
-        memset(info.transform, 0, sizeof(float) * 16);
-
-        for (size_t i = 0; i < count; i++) {
-            Functor* f = functors.itemAt(i);
-            result |= (*f)(DrawGlInfo::kModeProcess, &info);
-        }
-        resume();
-    }
-
-    return result;
-}
-
 status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
-    detachFunctor(functor);
-
-
     Rect clip(*currentClipRect());
     clip.snapToPixelBoundaries();
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7794abc..4de52ac 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -151,9 +151,6 @@
         return mCountOverdraw ? mOverdraw : 0.0f;
     }
 
-    ANDROID_API status_t invokeFunctors(Rect& dirty);
-    ANDROID_API void detachFunctor(Functor* functor);
-    ANDROID_API void attachFunctor(Functor* functor);
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
 
     ANDROID_API void pushLayerUpdate(Layer* layer);
@@ -959,8 +956,6 @@
 
     // List of rectangles to clear after saveLayer() is invoked
     Vector<Rect*> mLayers;
-    // List of functors to invoke after a frame is drawn
-    SortedVector<Functor*> mFunctors;
     // List of layers to update at the beginning of a frame
     Vector<Layer*> mLayerUpdates;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fc3548c..5a23158 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -349,6 +349,8 @@
         mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
         mHaveNewSurface = true;
         makeCurrent();
+    } else {
+        mRenderThread.removeFrameCallback(this);
     }
 }
 
@@ -385,6 +387,10 @@
     mCanvas->setViewport(width, height);
 }
 
+void CanvasContext::setOpaque(bool opaque) {
+    mOpaque = opaque;
+}
+
 void CanvasContext::makeCurrent() {
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
@@ -468,6 +474,10 @@
 
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
+    if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
+        return;
+    }
+
     ATRACE_CALL();
 
     TreeInfo info;
@@ -486,10 +496,7 @@
         requireGlContext();
         mode = DrawGlInfo::kModeProcess;
     }
-    // TODO: Remove the dummy info in the future
-    DrawGlInfo dummyInfo;
-    memset(&dummyInfo, 0, sizeof(DrawGlInfo));
-    (*functor)(mode, &dummyInfo);
+    (*functor)(mode, NULL);
 
     if (mCanvas) {
         mCanvas->resume();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a95e27a..dcb9858 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -52,6 +52,7 @@
     void updateSurface(EGLNativeWindowType window);
     void pauseSurface(EGLNativeWindowType window);
     void setup(int width, int height);
+    void setOpaque(bool opaque);
     void makeCurrent();
     void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
     void draw(Rect* dirty);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c2806fa..82a2dbc 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -159,6 +159,18 @@
     post(task);
 }
 
+CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
+    args->context->setOpaque(args->opaque);
+    return NULL;
+}
+
+void RenderProxy::setOpaque(bool opaque) {
+    SETUP_TASK(setOpaque);
+    args->context = mContext;
+    args->opaque = opaque;
+    post(task);
+}
+
 int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
         int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 013c3bd..4a7e70a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -67,6 +67,7 @@
     ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height);
+    ANDROID_API void setOpaque(bool opaque);
     ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index abebd48..6f42057 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -648,7 +648,7 @@
         return;
     }
 
-    clazz = env->FindClass("android/net/ProxyProperties");
+    clazz = env->FindClass("android/net/ProxyInfo");
     if (clazz == NULL) {
         return;
     }
@@ -660,7 +660,7 @@
         env->GetMethodID(clazz, "getPort", "()I");
 
     fields.proxyConfigGetExclusionList =
-        env->GetMethodID(clazz, "getExclusionList", "()Ljava/lang/String;");
+        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
 }
 
 static void
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..c779437
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..98ba690
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..61947ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..0b563b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..3600ee6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-sw600dp/heads_up.xml b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
index 71f7c21..f7035fe 100644
--- a/packages/SystemUI/res/layout-sw600dp/heads_up.xml
+++ b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
@@ -25,7 +25,6 @@
         android:id="@+id/content_holder"
         android:layout_height="wrap_content"
         android:layout_width="@dimen/notification_panel_width"
-        android:layout_marginStart="@dimen/notification_panel_margin_left"
         android:background="@drawable/heads_up_window_bg"
         />
 </com.android.systemui.statusbar.policy.HeadsUpNotificationView>
diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml
index f3c1b90..28d9625 100644
--- a/packages/SystemUI/res/layout/flip_settings.xml
+++ b/packages/SystemUI/res/layout/flip_settings.xml
@@ -22,5 +22,4 @@
     android:layout_height="wrap_content"
     android:background="#5f000000"
     android:animateLayoutChanges="true"
-    android:visibility="gone"
     android:columnCount="@integer/quick_settings_num_columns" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index 3a58b84..e4954e7 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -22,6 +22,5 @@
         android:layout_height="wrap_content"
         android:layout_width="@dimen/notification_panel_width"
         android:id="@+id/content_holder"
-        android:layout_marginStart="@dimen/notification_panel_margin_left"
         android:background="@drawable/notification_panel_bg"
         />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index ec5acba..194829d 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -55,4 +55,14 @@
         android:textStyle="italic"
         android:textAppearance="?android:attr/textAppearanceMedium"/>
 
+    <ImageView
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="bottom|center_horizontal"
+        android:src="@drawable/ic_lock_24dp"
+        android:scaleType="center"
+        android:alpha="0.7"
+        android:layerType="hardware"
+        android:tint="#ffffffff"/>
+
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 761ad42..3267c36 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -23,9 +23,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
     android:layout_width="0dp"
-    android:layout_height="wrap_content"
-    android:paddingTop="@dimen/notification_panel_padding_top"
-    android:layout_marginStart="@dimen/notification_panel_margin_left"
+    android:layout_height="match_parent"
     >
 
     <include
@@ -36,15 +34,6 @@
         android:layout_gravity="bottom"
         />
 
-    <include
-        layout="@layout/status_bar_flip_button"
-        android:id="@+id/keyguard_flipper"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_gravity="right|top"
-        android:layout_marginTop="@dimen/status_bar_height"
-        android:visibility="gone" />
-
     <com.android.keyguard.CarrierText
         android:id="@+id/keyguard_carrier_text"
         android:layout_width="wrap_content"
@@ -54,11 +43,6 @@
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <include layout="@layout/status_bar_expanded_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_panel_header_height"
-        />
-
     <include
         layout="@layout/keyguard_status_view"
         android:layout_height="wrap_content"
@@ -74,27 +58,54 @@
         android:visibility="gone"
         />
 
-    <FrameLayout
+    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:id="@+id/notification_container_parent"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/close_handle_underlap"
-        >
-        <include
-            layout="@layout/flip_settings"
-            android:layout_marginTop="@dimen/notification_panel_header_height"
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <com.android.systemui.statusbar.phone.ObservableScrollView
+            android:id="@+id/scroll_view"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
+            android:layout_height="match_parent"
+            android:visibility="invisible"
+            android:scrollbars="none"
+            android:fillViewport="true">
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+                <include
+                    layout="@layout/flip_settings"
+                    android:layout_marginTop="@dimen/status_bar_header_height_expanded"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"/>
+
+                <!-- A view to reserve space for the collapsed stack -->
+                <View
+                    android:layout_height="@dimen/collapsed_stack_height"
+                    android:layout_width="match_parent"/>
+            </LinearLayout>
+        </com.android.systemui.statusbar.phone.ObservableScrollView>
+
 
         <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
-    </FrameLayout>
+            android:layout_height="match_parent"
+            android:layout_marginBottom="@dimen/close_handle_underlap"/>
+
+    </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
+
+
+    <include layout="@layout/status_bar_expanded_header"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/status_bar_header_height"
+        />
 
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
+
 </com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 8975728..460dd4b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -15,21 +15,30 @@
 ** limitations under the License.
 -->
 
-<LinearLayout
+<!-- Extends RelativeLayout -->
+<com.android.systemui.statusbar.phone.StatusBarHeaderView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/header"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/notification_panel_header_height"
-    android:background="@drawable/notification_header_bg"
+    android:layout_height="@dimen/status_bar_header_height"
     android:orientation="horizontal"
     android:gravity="center_vertical"
     android:baselineAligned="false"
     >
+
+    <View
+        android:id="@+id/background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/notification_header_bg"
+        android:clickable="true"
+        />
     <RelativeLayout
         android:id="@+id/datetime"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:layout_gravity="start"
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:background="@drawable/ic_notify_button_bg"
@@ -55,12 +64,6 @@
             />
     </RelativeLayout>
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        />
-
     <TextView
         android:id="@+id/header_debug_info"
         android:visibility="invisible"
@@ -74,18 +77,22 @@
         android:padding="2dp"
         />
 
+    <include layout="@layout/status_bar_flip_button"
+        android:id="@+id/header_flipper"
+        android:layout_width="50dp"
+        android:layout_height="50dp"
+        android:layout_alignParentEnd="true"/>
+
     <ImageView android:id="@+id/clear_all_button"
         android:layout_width="50dp"
         android:layout_height="50dp"
+        android:layout_toStartOf="@id/header_flipper"
         android:scaleType="center"
         android:src="@drawable/ic_notify_clear"
         android:background="@drawable/ic_notify_button_bg"
         android:contentDescription="@string/accessibility_clear_all"
         />
 
-    <include layout="@layout/status_bar_flip_button"
-        android:id="@+id/header_flipper"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_marginStart="12dp" />
-</LinearLayout>
+
+
+</com.android.systemui.statusbar.phone.StatusBarHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml
index b7dff8c..f4d7033 100644
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ b/packages/SystemUI/res/layout/status_bar_flip_button.xml
@@ -15,22 +15,11 @@
   ~ limitations under the License
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="50dp"
-        android:layout_height="50dp">
-    <ImageView android:id="@+id/settings_button"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:scaleType="center"
-        android:src="@drawable/ic_notify_settings"
-        android:background="@drawable/ic_notify_button_bg"
-        android:contentDescription="@string/accessibility_desc_quick_settings" />
-    <ImageView android:id="@+id/notification_button"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:scaleType="center"
-        android:src="@drawable/ic_notifications"
-        android:background="@drawable/ic_notify_button_bg"
-        android:visibility="gone"
-        android:contentDescription="@string/accessibility_notifications_button" />
-</FrameLayout>
\ No newline at end of file
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/settings_button"
+    android:layout_width="50dp"
+    android:layout_height="50dp"
+    android:scaleType="center"
+    android:src="@drawable/ic_notify_quicksettings"
+    android:background="@drawable/ic_notify_button_bg"
+    android:contentDescription="@string/accessibility_desc_quick_settings"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index 2e08bff..30eedee 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -18,7 +18,7 @@
 <com.android.systemui.statusbar.NotificationOverflowContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="32dp"
+    android:layout_height="40dp"
     android:focusable="true"
     android:clickable="true"
     >
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 61d43d7..f9b022c 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -33,11 +33,10 @@
     <com.android.systemui.statusbar.phone.PanelHolder
         android:id="@+id/panel_holder"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/panel_holder_padding_top">
+        android:layout_height="match_parent" >
         <include layout="@layout/status_bar_expanded"
             android:layout_width="@dimen/notification_panel_width"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="start|top" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
deleted file mode 100644
index c6c0719..0000000
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, 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.
-*/
--->
-<resources>
-    <!-- Layout parameters for the notification panel -->
-	<dimen name="notification_panel_margin_bottom">0dp</dimen>
-    <dimen name="notification_panel_margin_left">32dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 92e3885..7372181 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -19,10 +19,6 @@
     <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) -->
     <dimen name="notification_panel_width">478dp</dimen>
 
-    <!-- Layout parameters for the notification panel -->
-    <dimen name="notification_panel_margin_bottom">192dp</dimen>
-    <dimen name="notification_panel_margin_left">16dp</dimen>
-
     <!-- Gravity for the notification panel -->
     <!-- 0x31 = top|center_horizontal -->
     <integer name="notification_panel_layout_gravity">0x31</integer>
@@ -43,9 +39,6 @@
     <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
     <dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
 
-    <!-- On tablets, panels drop from the statusbar instead of overlapping it. -->
-    <dimen name="panel_holder_padding_top">@*android:dimen/status_bar_height</dimen>
-
     <!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
     <item type="dimen" name="notification_panel_min_height_frac">40%</item>
 
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index b1fc00a..a1c5b66 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -24,8 +24,6 @@
 
     <!-- The width of the ticker, including the icon -->
     <dimen name="notification_ticker_width">360dp</dimen>
-    <!-- Status bar panel bottom offset (height of status bar - overlap) -->
-    <dimen name="status_bar_panel_bottom_offset">36dp</dimen>
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">1dp</dimen>
     <!-- The width of the notification panel window -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c3ccb59..93a75bc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -99,9 +99,6 @@
 
     <integer name="blinds_pop_duration_ms">10</integer>
 
-    <!-- The device supports quick settings. -->
-    <bool name="config_hasQuickSettings">true</bool>
-
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d763bd6..8d3a565 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -152,21 +152,11 @@
     <!-- Amount of close_handle that will NOT overlap the notification list -->
     <dimen name="close_handle_underlap">32dp</dimen>
 
-    <!-- Height of the notification panel header bar -->
-    <dimen name="notification_panel_header_height">48dp</dimen>
+    <!-- Height of the status bar header bar -->
+    <dimen name="status_bar_header_height">48dp</dimen>
 
-    <!-- Extra space above the panel -->
-    <dimen name="notification_panel_padding_top">0dp</dimen>
-
-    <!-- Extra space above the clock in the panel -->
-    <dimen name="notification_panel_header_padding_top">0dp</dimen>
-
-    <!-- Extra space above the panel holder -->
-    <dimen name="panel_holder_padding_top">0dp</dimen>
-
-    <!-- Layout parameters for the notification panel -->
-    <dimen name="notification_panel_margin_bottom">0dp</dimen>
-    <dimen name="notification_panel_margin_left">0dp</dimen>
+    <!-- Height of the status bar header bar when expanded -->
+    <dimen name="status_bar_header_height_expanded">144dp</dimen>
 
     <!-- Gravity for the notification panel -->
     <!-- 0x37 = fill_horizontal|top -->
@@ -261,6 +251,9 @@
     <!-- The padding between the individual notification cards. -->
     <dimen name="notification_padding">3dp</dimen>
 
+    <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) -->
+    <dimen name="collapsed_stack_height">94dp</dimen>
+
     <!-- Width of the zen mode interstitial dialog. -->
     <dimen name="zen_mode_dialog_width">320dp</dimen>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e5168c4..6418930 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -18,13 +18,21 @@
 <resources>
     <item type="id" name="translation_y_animator_tag"/>
     <item type="id" name="translation_z_animator_tag"/>
+    <item type="id" name="scale_animator_tag"/>
     <item type="id" name="alpha_animator_tag"/>
     <item type="id" name="top_inset_animator_tag"/>
     <item type="id" name="height_animator_tag"/>
     <item type="id" name="translation_y_animator_end_value_tag"/>
     <item type="id" name="translation_z_animator_end_value_tag"/>
+    <item type="id" name="scale_animator_end_value_tag"/>
     <item type="id" name="alpha_animator_end_value_tag"/>
     <item type="id" name="top_inset_animator_end_value_tag"/>
     <item type="id" name="height_animator_end_value_tag"/>
+    <item type="id" name="translation_y_animator_start_value_tag"/>
+    <item type="id" name="translation_z_animator_start_value_tag"/>
+    <item type="id" name="scale_animator_start_value_tag"/>
+    <item type="id" name="alpha_animator_start_value_tag"/>
+    <item type="id" name="top_inset_animator_start_value_tag"/>
+    <item type="id" name="height_animator_start_value_tag"/>
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 396cb14..3ef8316 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -64,7 +64,7 @@
                 mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
                 mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
                 mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
-                Console.log(Constants.DebugFlags.App.RecentsComponent,
+                Console.log(Constants.Log.App.RecentsComponent,
                         "[RecentsComponent|RecentsMessageHandler|handleMessage]",
                         "singleTaskRect: " + mSingleCountFirstTaskRect +
                         " multipleTaskRect: " + mMultipleCountFirstTaskRect);
@@ -83,7 +83,7 @@
     class RecentsServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
-            Console.log(Constants.DebugFlags.App.RecentsComponent,
+            Console.log(Constants.Log.App.RecentsComponent,
                     "[RecentsComponent|ServiceConnection|onServiceConnected]",
                     "toggleRecents: " + mToggleRecentsUponServiceBound);
             mService = new Messenger(service);
@@ -103,7 +103,7 @@
 
         @Override
         public void onServiceDisconnected(ComponentName className) {
-            Console.log(Constants.DebugFlags.App.RecentsComponent,
+            Console.log(Constants.Log.App.RecentsComponent,
                     "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
             mService = null;
             mServiceIsBound = false;
@@ -154,7 +154,7 @@
     }
 
     public void onStart() {
-        Console.log(Constants.DebugFlags.App.RecentsComponent, "[RecentsComponent|start]");
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
 
         // Try to create a long-running connection to the recents service
         bindToRecentsService(false);
@@ -162,11 +162,11 @@
 
     /** Toggles the alternate recents activity */
     public void onToggleRecents(Display display, int layoutDirection, View statusBarView) {
-        Console.logStartTracingTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey);
-        Console.logStartTracingTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
-                Constants.DebugFlags.App.TimeRecentsLaunchKey);
-        Console.log(Constants.DebugFlags.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
+        Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey);
+        Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
+                Constants.Log.App.TimeRecentsLaunchKey);
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
                 "serviceIsBound: " + mServiceIsBound);
         mStatusBarView = statusBarView;
         if (!mServiceIsBound) {
@@ -192,7 +192,7 @@
     }
 
     public void onCloseRecents() {
-        Console.log(Constants.DebugFlags.App.RecentsComponent, "[RecentsComponent|closeRecents]");
+        Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|closeRecents]");
         if (mServiceIsBound) {
             // Try and update the recents configuration
             try {
@@ -400,10 +400,10 @@
                     mService.send(msg);
 
                     // Time this path
-                    Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                            Constants.DebugFlags.App.TimeRecentsStartupKey, "sendToggleRecents");
-                    Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
-                            Constants.DebugFlags.App.TimeRecentsLaunchKey, "sendToggleRecents");
+                    Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                            Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
+                    Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                            Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
                 } catch (RemoteException re) {
                     re.printStackTrace();
                 }
@@ -450,8 +450,8 @@
             startAlternateRecentsActivity(opts, false);
         }
 
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey, "startRecentsActivity");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
         mLastToggleTime = System.currentTimeMillis();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 90ea873..1d6a76c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -18,7 +18,6 @@
 
 /**
  * Constants
- * XXX: We are going to move almost all of these into a resource.
  */
 public class Constants {
     public static class DebugFlags {
@@ -26,12 +25,18 @@
         public static final boolean Verbose = false;
 
         public static class App {
+            // Enables the filtering of tasks according to their grouping
             public static final boolean EnableTaskFiltering = false;
+            // Enables clipping of tasks against each other
             public static final boolean EnableTaskStackClipping = false;
+            // Enables the use of theme colors as the task bar background
             public static final boolean EnableTaskBarThemeColors = true;
+            // Enables the info pane on long-press
             public static final boolean EnableInfoPane = true;
-            public static final boolean EnableSearchButton = true;
-
+            // Enables the search bar layout
+            public static final boolean EnableSearchLayout = true;
+            // Enables the dynamic shadows behind each task
+            public static final boolean EnableShadows = false;
             // This disables the bitmap and icon caches
             public static final boolean DisableBackgroundCache = false;
             // For debugging, this enables us to create mock recents tasks
@@ -40,8 +45,11 @@
             public static final int SystemServicesProxyMockPackageCount = 3;
             // For debugging, this defines the number of mock recents tasks to create
             public static final int SystemServicesProxyMockTaskCount = 75;
+        }
+    }
 
-            // Timing certain paths
+    public static class Log {
+        public static class App {
             public static final String TimeRecentsStartupKey = "startup";
             public static final String TimeRecentsLaunchKey = "launchTask";
             public static final boolean TimeRecentsStartup = false;
@@ -72,6 +80,7 @@
         }
     }
 
+    /** XXX: We are going to move almost all of these into a resource once they are nailed down. */
     public static class Values {
         public static class App {
             public static int AppWidgetHostId = 1024;
@@ -102,10 +111,5 @@
             // The number of cards we see in the peek space
             public static final int StackPeekNumCards = 3;
         }
-
-        public static class TaskView {
-            public static final boolean AnimateFrontTaskBarOnEnterRecents = true;
-            public static final boolean AnimateFrontTaskBarOnLeavingRecents = true;
-        }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 110130b..3be6932 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -78,7 +77,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+            Console.log(Constants.Log.App.SystemUIHandshake,
                     "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
             if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // Try and unfilter and filtered stacks
@@ -134,7 +133,7 @@
 
     /** Attempts to allocate and bind the search bar app widget */
     void bindSearchBarAppWidget() {
-        if (Constants.DebugFlags.App.EnableSearchButton) {
+        if (Constants.DebugFlags.App.EnableSearchLayout) {
             RecentsConfiguration config = RecentsConfiguration.getInstance();
             SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
 
@@ -152,7 +151,7 @@
                     ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
                     appWidgetId = -1;
                 }
-                Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                Console.log(Constants.Log.App.SystemUIHandshake,
                         "[RecentsActivity|onCreate|settings|appWidgetId]",
                         "Id: " + appWidgetId,
                         Console.AnsiBlue);
@@ -163,7 +162,7 @@
                 Pair<Integer, AppWidgetProviderInfo> widgetInfo =
                         ssp.bindSearchAppWidget(mAppWidgetHost);
                 if (widgetInfo != null) {
-                    Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                    Console.log(Constants.Log.App.SystemUIHandshake,
                             "[RecentsActivity|onCreate|searchWidget]",
                             "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
                             Console.AnsiBlue);
@@ -178,11 +177,11 @@
 
     /** Creates the search bar app widget view */
     void addSearchBarAppWidgetView() {
-        if (Constants.DebugFlags.App.EnableSearchButton) {
+        if (Constants.DebugFlags.App.EnableSearchLayout) {
             RecentsConfiguration config = RecentsConfiguration.getInstance();
             int appWidgetId = config.searchBarAppWidgetId;
             if (appWidgetId >= 0) {
-                Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                Console.log(Constants.Log.App.SystemUIHandshake,
                         "[RecentsActivity|onCreate|addSearchAppWidgetView]",
                         "Id: " + appWidgetId,
                         Console.AnsiBlue);
@@ -216,11 +215,11 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Console.logDivider(Constants.DebugFlags.App.SystemUIHandshake);
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
+        Console.logDivider(Constants.Log.App.SystemUIHandshake);
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
                 getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey, "onCreate");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey, "onCreate");
 
         // Initialize the loader and the configuration
         RecentsTaskLoader.initialize(this);
@@ -260,11 +259,11 @@
         // Reset the task launched flag if we encounter an onNewIntent() before onStop()
         mTaskLaunched = false;
 
-        Console.logDivider(Constants.DebugFlags.App.SystemUIHandshake);
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
+        Console.logDivider(Constants.Log.App.SystemUIHandshake);
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
                 intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey, "onNewIntent");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey, "onNewIntent");
 
         // Initialize the loader and the configuration
         RecentsTaskLoader.initialize(this);
@@ -279,7 +278,7 @@
 
     @Override
     protected void onStart() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
                 Console.AnsiRed);
         super.onStart();
         mAppWidgetHost.startListening();
@@ -288,14 +287,14 @@
 
     @Override
     protected void onResume() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
                 Console.AnsiRed);
         super.onResume();
     }
 
     @Override
     public void onAttachedToWindow() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+        Console.log(Constants.Log.App.SystemUIHandshake,
                 "[RecentsActivity|onAttachedToWindow]", "",
                 Console.AnsiRed);
         super.onAttachedToWindow();
@@ -313,7 +312,7 @@
 
     @Override
     public void onDetachedFromWindow() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+        Console.log(Constants.Log.App.SystemUIHandshake,
                 "[RecentsActivity|onDetachedFromWindow]", "",
                 Console.AnsiRed);
         super.onDetachedFromWindow();
@@ -325,14 +324,14 @@
 
     @Override
     protected void onPause() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
                 Console.AnsiRed);
         super.onPause();
     }
 
     @Override
     protected void onStop() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
                 Console.AnsiRed);
         super.onStop();
 
@@ -343,7 +342,7 @@
 
     @Override
     protected void onDestroy() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
                 Console.AnsiRed);
         super.onDestroy();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index d1a3954..cb7e42a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -88,7 +88,7 @@
                 Configuration.ORIENTATION_LANDSCAPE;
         transposeSearchLayoutWithOrientation =
                 res.getBoolean(R.bool.recents_transpose_search_layout_with_orientation);
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
+        Console.log(Constants.Log.UI.MeasureAndLayout,
                 "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
                 Console.AnsiGreen);
 
@@ -174,7 +174,7 @@
      */
     public void getSearchBarBounds(int width, int height, Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
-        if (!Constants.DebugFlags.App.EnableSearchButton) {
+        if (!Constants.DebugFlags.App.EnableSearchLayout) {
             searchBarSpaceBounds.set(0, 0, 0, 0);
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 0c6ed84..837cb34 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -45,7 +45,7 @@
 
     @Override
     public void handleMessage(Message msg) {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+        Console.log(Constants.Log.App.SystemUIHandshake,
                 "[RecentsService|handleMessage]", msg);
 
         Context context = mContext.get();
@@ -115,10 +115,10 @@
             context.sendBroadcast(intent);
 
             // Time this path
-            Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                    Constants.DebugFlags.App.TimeRecentsStartupKey, "receivedToggleRecents");
-            Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
-                    Constants.DebugFlags.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                    Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+            Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                    Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
         }
     }
 }
@@ -131,31 +131,31 @@
 
     @Override
     public void onCreate() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onCreate]");
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onCreate]");
         super.onCreate();
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onBind]");
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onBind]");
         return mSystemUIMessenger.getBinder();
     }
 
     @Override
     public boolean onUnbind(Intent intent) {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onUnbind]");
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onUnbind]");
         return super.onUnbind(intent);
     }
 
     @Override
     public void onRebind(Intent intent) {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onRebind]");
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onRebind]");
         super.onRebind(intent);
     }
 
     @Override
     public void onDestroy() {
-        Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onDestroy]");
+        Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onDestroy]");
         super.onDestroy();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index 4a19027..c9038ee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -51,7 +51,7 @@
 
     /** Adds a new task to the load queue */
     void addTask(Task t, boolean forceLoad) {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
+        Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
         if (!mQueue.contains(t)) {
             mQueue.add(t);
         }
@@ -68,7 +68,7 @@
      * force reloaded.
      */
     Pair<Task, Boolean> nextTask() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
+        Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
         Task task = mQueue.poll();
         Boolean forceLoadTask = null;
         if (task != null) {
@@ -82,14 +82,14 @@
 
     /** Removes a task from the load queue */
     void removeTask(Task t) {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
+        Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
         mQueue.remove(t);
         mForceLoadSet.remove(t.key);
     }
 
     /** Clears all the tasks from the load queue */
     void clearTasks() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
+        Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
         mQueue.clear();
         mForceLoadSet.clear();
     }
@@ -132,7 +132,7 @@
 
     /** Restarts the loader thread */
     void start(Context context) {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|start]");
+        Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|start]");
         mContext = context;
         mCancelled = false;
         mSystemServicesProxy = new SystemServicesProxy(context);
@@ -144,7 +144,7 @@
 
     /** Requests the loader thread to stop after the current iteration */
     void stop() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
+        Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|stop]");
         // Mark as cancelled for the thread to pick up
         mCancelled = true;
         mSystemServicesProxy = null;
@@ -158,10 +158,10 @@
     @Override
     public void run() {
         while (true) {
-            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+            Console.log(Constants.Log.App.TaskDataLoader,
                     "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
             if (mCancelled) {
-                Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                Console.log(Constants.Log.App.TaskDataLoader,
                         "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
                 // We have to unset the context here, since the background thread may be using it
                 // when we call stop()
@@ -169,7 +169,7 @@
                 // If we are cancelled, then wait until we are started again
                 synchronized(mLoadThread) {
                     try {
-                        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                        Console.log(Constants.Log.App.TaskDataLoader,
                                 "[TaskResourceLoader|waitOnLoadThreadCancelled]");
                         mLoadThread.wait();
                     } catch (InterruptedException ie) {
@@ -186,7 +186,7 @@
                 if (t != null) {
                     Drawable loadIcon = mApplicationIconCache.get(t.key);
                     Bitmap loadThumbnail = mThumbnailCache.get(t.key);
-                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                    Console.log(Constants.Log.App.TaskDataLoader,
                             "  [TaskResourceLoader|load]",
                             t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
                                     " forceLoad: " + forceLoadTask);
@@ -197,7 +197,7 @@
                         Drawable icon = ssp.getActivityIcon(info, t.userId);
                         if (!mCancelled) {
                             if (icon != null) {
-                                Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                Console.log(Constants.Log.App.TaskDataLoader,
                                         "    [TaskResourceLoader|loadIcon]",
                                         icon);
                                 loadIcon = icon;
@@ -210,7 +210,7 @@
                         Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
                         if (!mCancelled) {
                             if (thumbnail != null) {
-                                Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                                Console.log(Constants.Log.App.TaskDataLoader,
                                         "    [TaskResourceLoader|loadThumbnail]",
                                         thumbnail);
                                 thumbnail.setHasAlpha(false);
@@ -240,7 +240,7 @@
                 if (!mCancelled && mLoadQueue.isEmpty()) {
                     synchronized(mLoadQueue) {
                         try {
-                            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                            Console.log(Constants.Log.App.TaskDataLoader,
                                     "[TaskResourceLoader|waitOnLoadQueue]");
                             mWaitingOnLoadQueue = true;
                             mLoadQueue.wait();
@@ -319,7 +319,7 @@
         int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                 mMaxThumbnailCacheSize;
 
-        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+        Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
                 " iconCache: " + iconCacheSize);
 
@@ -336,7 +336,7 @@
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         mDefaultThumbnail.eraseColor(0x00000000);
         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
-        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+        Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|defaultBitmaps]",
                 "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
     }
@@ -366,10 +366,10 @@
         List<ActivityManager.RecentTaskInfo> tasks =
                 ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
-        Console.log(Constants.DebugFlags.App.TimeSystemCalls,
+        Console.log(Constants.Log.App.TimeSystemCalls,
                 "[RecentsTaskLoader|getRecentTasks]",
                 "" + (System.currentTimeMillis() - t1) + "ms");
-        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+        Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|tasks]", "" + tasks.size());
 
         // Remove home/recents tasks
@@ -396,7 +396,7 @@
     SpaceNode reload(Context context, int preloadCount) {
         long t1 = System.currentTimeMillis();
 
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
+        Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
         Resources res = context.getResources();
         ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
         TaskStack stack = new TaskStack(context);
@@ -435,7 +435,7 @@
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
-                Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                Console.log(Constants.Log.App.TaskDataLoader,
                         "[RecentsTaskLoader|preloadTask]",
                         "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
 
@@ -467,7 +467,7 @@
                     }
                 }
                 if (task.thumbnail == null) {
-                    Console.log(Constants.DebugFlags.App.TaskDataLoader,
+                    Console.log(Constants.Log.App.TaskDataLoader,
                             "[RecentsTaskLoader|loadingTaskThumbnail]");
                     task.thumbnail = ssp.getTaskThumbnail(task.key.id);
                     if (task.thumbnail != null) {
@@ -480,11 +480,11 @@
             }
 
             // Add the task to the stack
-            Console.log(Constants.DebugFlags.App.TaskDataLoader,
+            Console.log(Constants.Log.App.TaskDataLoader,
                 "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
             stack.addTask(task);
         }
-        Console.log(Constants.DebugFlags.App.TimeSystemCalls,
+        Console.log(Constants.Log.App.TimeSystemCalls,
                 "[RecentsTaskLoader|getAllTaskTopThumbnail]",
                 "" + (System.currentTimeMillis() - t1) + "ms");
 
@@ -492,10 +492,10 @@
         // Get all the stacks
         t1 = System.currentTimeMillis();
         List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
-        Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
+        Console.log(Constants.Log.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
+        Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
         for (ActivityManager.StackInfo s : stackInfos) {
-            Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [RecentsTaskLoader|stack]", s.toString());
+            Console.log(Constants.Log.App.TaskDataLoader, "  [RecentsTaskLoader|stack]", s.toString());
             if (stacks.containsKey(s.stackId)) {
                 stacks.get(s.stackId).setRect(s.bounds);
             }
@@ -518,7 +518,7 @@
         Drawable applicationIcon = mApplicationIconCache.get(t.key);
         Bitmap thumbnail = mThumbnailCache.get(t.key);
 
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
+        Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
                 t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail +
                         " thumbnailCacheSize: " + mThumbnailCache.size());
 
@@ -539,7 +539,7 @@
 
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+        Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|unloadTask]", t +
                 " thumbnailCacheSize: " + mThumbnailCache.size());
 
@@ -549,7 +549,7 @@
 
     /** Completely removes the resource data from the pool. */
     public void deleteTaskData(Task t) {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader,
+        Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|deleteTask]", t);
 
         mLoadQueue.removeTask(t);
@@ -560,13 +560,13 @@
 
     /** Stops the task loader and clears all pending tasks */
     void stopLoader() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
+        Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
         mLoader.stop();
         mLoadQueue.clearTasks();
     }
 
     void onTrimMemory(int level) {
-        Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
+        Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
                 Console.trimMemoryLevelToString(level));
 
         switch (level) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index c3b8a20..b41555f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -180,7 +180,7 @@
      */
     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
         if (mIpm == null) return null;
-        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
 
         try {
             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
@@ -197,7 +197,7 @@
      */
     public ActivityInfo getActivityInfo(ComponentName cn) {
         if (mPm == null) return null;
-        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
+        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
 
         try {
             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c2e8275..eb7b5c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -124,7 +124,7 @@
     /** Adds the search bar */
     public void setSearchBar(View searchBar) {
         // Create the search bar (and hide it if we have no recent tasks)
-        if (Constants.DebugFlags.App.EnableSearchButton) {
+        if (Constants.DebugFlags.App.EnableSearchLayout) {
             // Remove the previous search bar if one exists
             if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
                 removeView(mSearchBar);
@@ -135,7 +135,7 @@
                 mSearchBar.setVisibility(mHasTasks ? View.VISIBLE : View.GONE);
                 addView(mSearchBar);
 
-                Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
+                Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
                         "" + (mSearchBar.getVisibility() == View.VISIBLE),
                         Console.AnsiBlue);
             }
@@ -152,10 +152,10 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|measure]",
+        Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]",
                 "width: " + width + " height: " + height, Console.AnsiGreen);
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
 
         // Get the search bar bounds and measure the search bar layout
         RecentsConfiguration config = RecentsConfiguration.getInstance();
@@ -194,10 +194,10 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[RecentsView|layout]",
+        Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]",
                 new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsStartup,
-                Constants.DebugFlags.App.TimeRecentsStartupKey, "RecentsView.onLayout");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+                Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout");
 
         // Get the search bar bounds so that we lay it out
         RecentsConfiguration config = RecentsConfiguration.getInstance();
@@ -232,14 +232,14 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        Console.log(Constants.DebugFlags.UI.Draw, "[RecentsView|dispatchDraw]", "",
+        Console.log(Constants.Log.UI.Draw, "[RecentsView|dispatchDraw]", "",
                 Console.AnsiPurple);
         super.dispatchDraw(canvas);
     }
 
     @Override
     protected boolean fitSystemWindows(Rect insets) {
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
+        Console.log(Constants.Log.UI.MeasureAndLayout,
                 "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
 
         // Update the configuration with the latest system insets and trigger a relayout
@@ -359,16 +359,16 @@
                     }
                 }
 
-                Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
-                        Constants.DebugFlags.App.TimeRecentsLaunchKey, "startActivity");
+                Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                        Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
             }
         };
 
-        Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
-                Constants.DebugFlags.App.TimeRecentsLaunchKey, "onTaskLaunched");
+        Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+                Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
 
         // Launch the app right away if there is no task view, otherwise, animate the icon out first
-        if (tv == null || !Constants.Values.TaskView.AnimateFrontTaskBarOnLeavingRecents) {
+        if (tv == null) {
             post(launchRunnable);
         } else {
             tv.animateOnLeavingRecents(launchRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 21ef9ff..c34300c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -178,7 +178,7 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        Console.log(Constants.DebugFlags.UI.TouchEvents,
+        Console.log(Constants.Log.UI.TouchEvents,
                 "[SwipeHelper|interceptTouchEvent]",
                 Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
         final int action = ev.getAction();
@@ -291,7 +291,7 @@
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
-        Console.log(Constants.DebugFlags.UI.TouchEvents,
+        Console.log(Constants.Log.UI.TouchEvents,
                 "[SwipeHelper|touchEvent]",
                 Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index ce43b5a..cfacd18 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -110,7 +109,7 @@
         requestSynchronizeStackViewsWithModel(0);
     }
     void requestSynchronizeStackViewsWithModel(int duration) {
-        Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+        Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
                 "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
         if (!mStackViewsDirty) {
             invalidate();
@@ -215,7 +214,7 @@
 
     /** Synchronizes the views with the model */
     void synchronizeStackViewsWithModel() {
-        Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+        Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
                 "[TaskStackView|synchronizeViewsWithModel]",
                 "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
         if (mStackViewsDirty) {
@@ -271,7 +270,7 @@
                 }
             }
 
-            Console.log(Constants.DebugFlags.TaskStack.SynchronizeViewsWithModel,
+            Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
                     "  [TaskStackView|viewChildren]", "" + getChildCount());
 
             mStackViewsAnimationDuration = 0;
@@ -429,7 +428,7 @@
         }
 
         // Debug logging
-        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+        if (Constants.Log.UI.MeasureAndLayout) {
             Console.log("  [TaskStack|minScroll] " + mMinScroll);
             Console.log("  [TaskStack|maxScroll] " + mMaxScroll);
         }
@@ -456,7 +455,7 @@
 
     /** Enables the hw layers and increments the hw layer requirement ref count */
     void addHwLayersRefCount(String reason) {
-        Console.log(Constants.DebugFlags.UI.HwLayers,
+        Console.log(Constants.Log.UI.HwLayers,
                 "[TaskStackView|addHwLayersRefCount] refCount: " +
                         mHwLayersRefCount + "->" + (mHwLayersRefCount + 1) + " " + reason);
         if (mHwLayersRefCount == 0) {
@@ -473,7 +472,7 @@
     /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
         need them anymore. */
     void decHwLayersRefCount(String reason) {
-        Console.log(Constants.DebugFlags.UI.HwLayers,
+        Console.log(Constants.Log.UI.HwLayers,
                 "[TaskStackView|decHwLayersRefCount] refCount: " +
                         mHwLayersRefCount + "->" + (mHwLayersRefCount - 1) + " " + reason);
         mHwLayersRefCount--;
@@ -515,7 +514,7 @@
 
     @Override
     public void dispatchDraw(Canvas canvas) {
-        Console.log(Constants.DebugFlags.UI.Draw, "[TaskStackView|dispatchDraw]", "",
+        Console.log(Constants.Log.UI.Draw, "[TaskStackView|dispatchDraw]", "",
                 Console.AnsiPurple);
         synchronizeStackViewsWithModel();
         super.dispatchDraw(canvas);
@@ -568,7 +567,7 @@
 
         int smallestDimension = Math.min(width, height);
         int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f);
-        if (Constants.DebugFlags.App.EnableSearchButton) {
+        if (Constants.DebugFlags.App.EnableSearchLayout) {
             mStackRect.top += padding;
             mStackRect.left += padding;
             mStackRect.right -= padding;
@@ -600,7 +599,7 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[TaskStackView|measure]",
+        Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|measure]",
                 "width: " + width + " height: " + height +
                 " awaitingFirstLayout: " + mAwaitingFirstLayout, Console.AnsiGreen);
 
@@ -611,7 +610,7 @@
         computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom);
 
         // Debug logging
-        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+        if (Constants.Log.UI.MeasureAndLayout) {
             Console.log("  [TaskStack|fullRect] " + mRect);
             Console.log("  [TaskStack|stackRect] " + mStackRect);
             Console.log("  [TaskStack|stackRectSansPeek] " + mStackRectSansPeek);
@@ -626,8 +625,7 @@
             synchronizeStackViewsWithModel();
 
             // Animate the task bar of the first task view
-            if (config.launchedWithThumbnailAnimation &&
-                    Constants.Values.TaskView.AnimateFrontTaskBarOnEnterRecents) {
+            if (config.launchedWithThumbnailAnimation) {
                 TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
                 if (tv != null) {
                     tv.animateOnEnterRecents();
@@ -653,11 +651,11 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        Console.log(Constants.DebugFlags.UI.MeasureAndLayout, "[TaskStackView|layout]",
+        Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|layout]",
                 "" + new Rect(left, top, right, bottom), Console.AnsiGreen);
 
         // Debug logging
-        if (Constants.DebugFlags.UI.MeasureAndLayout) {
+        if (Constants.Log.UI.MeasureAndLayout) {
             Console.log("  [TaskStack|fullRect] " + mRect);
             Console.log("  [TaskStack|stackRect] " + mStackRect);
             Console.log("  [TaskStack|stackRectSansPeek] " + mStackRectSansPeek);
@@ -911,7 +909,7 @@
 
     @Override
     public TaskView createView(Context context) {
-        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|createPoolView]");
+        Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|createPoolView]");
         return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
     }
 
@@ -919,7 +917,7 @@
     public void prepareViewToEnterPool(TaskView tv) {
         Task task = tv.getTask();
         tv.resetViewProperties();
-        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]",
+        Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]",
                 tv.getTask() + " tv: " + tv);
 
         // Report that this tasks's data is no longer being used
@@ -935,7 +933,7 @@
 
     @Override
     public void prepareViewToLeavePool(TaskView tv, Task prepareData, boolean isNewView) {
-        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]",
+        Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]",
                 "isNewView: " + isNewView);
 
         // Setup and attach the view to the window
@@ -958,7 +956,7 @@
         }
 
         // Add/attach the view to the hierarchy
-        Console.log(Constants.DebugFlags.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
+        Console.log(Constants.Log.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
                 "" + insertIndex);
         if (isNewView) {
             addView(tv, insertIndex);
@@ -988,7 +986,7 @@
 
     @Override
     public void onTaskIconClicked(TaskView tv) {
-        Console.log(Constants.DebugFlags.UI.ClickEvents, "[TaskStack|Clicked|Icon]",
+        Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Icon]",
                 tv.getTask() + " is currently filtered: " + mStack.hasFilteredTasks(),
                 Console.AnsiCyan);
         if (Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -1024,7 +1022,7 @@
     public void onClick(View v) {
         TaskView tv = (TaskView) v;
         Task task = tv.getTask();
-        Console.log(Constants.DebugFlags.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
+        Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
                 task + " cb: " + mCb);
 
         // Close any open info panes if the user taps on another task
@@ -1149,7 +1147,7 @@
 
     /** Touch preprocessing for handling below */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        Console.log(Constants.DebugFlags.UI.TouchEvents,
+        Console.log(Constants.Log.UI.TouchEvents,
                 "[TaskStackViewTouchHandler|interceptTouchEvent]",
                 Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
 
@@ -1233,7 +1231,7 @@
 
     /** Handles touch events once we have intercepted them */
     public boolean onTouchEvent(MotionEvent ev) {
-        Console.log(Constants.DebugFlags.UI.TouchEvents,
+        Console.log(Constants.Log.UI.TouchEvents,
                 "[TaskStackViewTouchHandler|touchEvent]",
                 Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
 
@@ -1336,7 +1334,7 @@
                             Math.abs((float) velocity / mMaximumVelocity)) *
                             Constants.Values.TaskStackView.TaskStackOverscrollRange);
 
-                    Console.log(Constants.DebugFlags.UI.TouchEvents,
+                    Console.log(Constants.Log.UI.TouchEvents,
                             "[TaskStackViewTouchHandler|fling]",
                             "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
                                     " maxVelocity: " + mMaximumVelocity +
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 801de24..45c2fae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -146,13 +146,17 @@
         if (duration > 0) {
             if (animateFromTransform != null) {
                 setTranslationY(animateFromTransform.translationY);
-                setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
+                if (Constants.DebugFlags.App.EnableShadows) {
+                    setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
+                }
                 setScaleX(animateFromTransform.scale);
                 setScaleY(animateFromTransform.scale);
                 setAlpha(animateFromTransform.alpha);
             }
+            if (Constants.DebugFlags.App.EnableShadows) {
+                animate().translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
+            }
             animate().translationY(toTransform.translationY)
-                    .translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)))
                     .scaleX(toTransform.scale)
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
@@ -168,7 +172,9 @@
                     .start();
         } else {
             setTranslationY(toTransform.translationY);
-            setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
+            if (Constants.DebugFlags.App.EnableShadows) {
+                setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
+            }
             setScaleX(toTransform.scale);
             setScaleY(toTransform.scale);
             setAlpha(toTransform.alpha);
@@ -181,7 +187,9 @@
     void resetViewProperties() {
         setTranslationX(0f);
         setTranslationY(0f);
-        setTranslationZ(0f);
+        if (Constants.DebugFlags.App.EnableShadows) {
+            setTranslationZ(0f);
+        }
         setScaleX(1f);
         setScaleY(1f);
         setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 1c88ea7..91df9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -38,6 +38,7 @@
 public abstract class ActivatableNotificationView extends ExpandableOutlineView {
 
     private static final long DOUBLETAP_TIMEOUT_MS = 1000;
+    private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
 
     private boolean mDimmed;
 
@@ -174,12 +175,14 @@
     private void makeInactive() {
         if (mActivated) {
             // Make sure that we clear the hotspot from the center.
-            mBackgroundDimmed.setHotspot(0, getWidth() / 2, getActualHeight() / 2);
-            mBackgroundDimmed.removeHotspot(0);
+            if (mBackgroundDimmed != null) {
+                mBackgroundDimmed.setHotspot(0, getWidth() / 2, getActualHeight() / 2);
+                mBackgroundDimmed.removeHotspot(0);
+            }
             mActivated = false;
         }
         if (mOnActivatedListener != null) {
-            mOnActivatedListener.onReset(this);
+            mOnActivatedListener.onActivationReset(this);
         }
         removeCallbacks(mTapTimeoutRunnable);
     }
@@ -189,12 +192,6 @@
                 && Math.abs(event.getY() - mDownY) < mTouchSlop;
     }
 
-    /**
-     * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
-     *
-     * @param dimmed Whether the notification should be dimmed.
-     * @param fade Whether an animation should be played to change the state.
-     */
     public void setDimmed(boolean dimmed, boolean fade) {
         if (mDimmed != dimmed) {
             mDimmed = dimmed;
@@ -226,7 +223,7 @@
         }
         int startAlpha = mDimmed ? 255 : 0;
         int endAlpha = mDimmed ? 0 : 255;
-        int duration = NotificationActivator.ANIMATION_LENGTH_MS;
+        int duration = BACKGROUND_ANIMATION_LENGTH_MS;
         // Check whether there is already a background animation running.
         if (mBackgroundAnimator != null) {
             startAlpha = (Integer) mBackgroundAnimator.getAnimatedValue();
@@ -313,8 +310,8 @@
     }
 
     @Override
-    public void setActualHeight(int actualHeight) {
-        super.setActualHeight(actualHeight);
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        super.setActualHeight(actualHeight, notifyListeners);
         invalidate();
         setPivotY(actualHeight / 2);
     }
@@ -331,6 +328,6 @@
 
     public interface OnActivatedListener {
         void onActivated(View view);
-        void onReset(View view);
+        void onActivationReset(View view);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index d224975..ecefc39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -70,7 +70,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.util.LegacyNotificationUtil;
+import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
@@ -143,7 +143,7 @@
     // public mode, private notifications, etc
     private boolean mLockscreenPublicMode = false;
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
-    private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
+    private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance();
 
     private UserManager mUserManager;
 
@@ -852,7 +852,7 @@
 
             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
             icon.setImageDrawable(iconDrawable);
-            if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) {
+            if (mNotificationColorUtil.isGrayscale(iconDrawable)) {
                 icon.setBackgroundResource(
                         com.android.internal.R.drawable.notification_icon_legacy_bg_inset);
             }
@@ -1067,7 +1067,6 @@
                     entry.row.setSystemExpanded(top);
                 }
             }
-            entry.row.setDimmed(onKeyguard, false /* fade */);
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
             if (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
                     || !showOnKeyguard)) {
@@ -1087,48 +1086,11 @@
 
         if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
             mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
-            mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, false /* fade */);
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
     }
 
-    @Override
-    public void onActivated(View view) {
-        int n = mNotificationData.size();
-        for (int i = 0; i < n; i++) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE) {
-                if (view == entry.row) {
-                    entry.row.getActivator().activate();
-                } else {
-                    entry.row.getActivator().activateInverse();
-                }
-            }
-        }
-        if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
-            if (view == mKeyguardIconOverflowContainer) {
-                mKeyguardIconOverflowContainer.getActivator().activate();
-            } else {
-                mKeyguardIconOverflowContainer.getActivator().activateInverse();
-            }
-        }
-    }
-
-    @Override
-    public void onReset(View view) {
-        int n = mNotificationData.size();
-        for (int i = 0; i < n; i++) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE) {
-                entry.row.getActivator().reset();
-            }
-        }
-        if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
-            mKeyguardIconOverflowContainer.getActivator().reset();
-        }
-    }
-
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
         return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
     }
@@ -1144,7 +1106,6 @@
     protected abstract void updateNotificationIcons();
     protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
-    protected abstract int getExpandedViewMaxHeight();
     protected abstract boolean shouldDisableNavbarGestures();
 
     protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index e471754..5b2ea0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -117,7 +117,7 @@
                 } else {
                     if (mDraggedFarEnough) {
                         mDraggedFarEnough = false;
-                        mOnDragDownListener.onReset();
+                        mOnDragDownListener.onDragDownReset();
                     }
                 }
                 return true;
@@ -188,7 +188,7 @@
             cancelExpansion(mStartingChild);
         }
         mDraggingDown = false;
-        mOnDragDownListener.onReset();
+        mOnDragDownListener.onDragDownReset();
     }
 
     private ExpandableView findView(float x, float y) {
@@ -200,7 +200,7 @@
 
     public interface OnDragDownListener {
         void onDraggedDown(View startingChild);
-        void onReset();
+        void onDragDownReset();
         void onThresholdReached();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e5512a3..39f2bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -52,7 +52,6 @@
     private NotificationContentView mPublicLayout;
     private NotificationContentView mPrivateLayout;
     private int mMaxExpandHeight;
-    private NotificationActivator mActivator;
 
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -63,8 +62,6 @@
         super.onFinishInflate();
         mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
-
-        mActivator = new NotificationActivator(this, this);
     }
 
     @Override
@@ -208,23 +205,10 @@
         mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE);
     }
 
-    /**
-     * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
-     */
-    @Override
-    public void setDimmed(boolean dimmed, boolean fade) {
-        super.setDimmed(dimmed, fade);
-        mActivator.setDimmed(dimmed, fade);
-    }
-
     public int getMaxExpandHeight() {
         return mMaxExpandHeight;
     }
 
-    public NotificationActivator getActivator() {
-        return mActivator;
-    }
-
     /**
      * @return the potential height this view could expand in addition.
      */
@@ -238,10 +222,10 @@
     }
 
     @Override
-    public void setActualHeight(int height) {
-        mPrivateLayout.setActualHeight(height);
+    public void setActualHeight(int height, boolean notifyListeners) {
+        mPrivateLayout.setActualHeight(height, notifyListeners);
         invalidate();
-        super.setActualHeight(height);
+        super.setActualHeight(height, notifyListeners);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 43eb5b5..a42c194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -33,8 +33,8 @@
     }
 
     @Override
-    public void setActualHeight(int actualHeight) {
-        super.setActualHeight(actualHeight);
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        super.setActualHeight(actualHeight, notifyListeners);
         updateOutline();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 169521d..281bd2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -61,10 +61,19 @@
     /**
      * Sets the actual height of this notification. This is different than the laid out
      * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
+     *
+     * @param actualHeight The height of this notification.
+     * @param notifyListeners Whether the listener should be informed about the change.
      */
-    public void setActualHeight(int actualHeight) {
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
         mActualHeight = actualHeight;
-        notifyHeightChanged();
+        if (notifyListeners) {
+            notifyHeightChanged();
+        }
+    }
+
+    public void setActualHeight(int actualHeight) {
+        setActualHeight(actualHeight, true);
     }
 
     /**
@@ -91,6 +100,15 @@
     }
 
     /**
+     * Sets the notification as dimmed. The default implementation does nothing.
+     *
+     * @param dimmed Whether the notification should be dimmed.
+     * @param fade Whether an animation should be played to change the state.
+     */
+    public void setDimmed(boolean dimmed, boolean fade) {
+    }
+
+    /**
      * @return The desired notification height.
      */
     public int getIntrinsicHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
deleted file mode 100644
index a03aeec..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.R;
-
-/**
- * A helper class used by both {@link com.android.systemui.statusbar.ExpandableNotificationRow} and
- * {@link com.android.systemui.statusbar.NotificationOverflowIconsView} to make a notification look
- * active after tapping it once on the Keyguard.
- */
-public class NotificationActivator {
-
-    public static final int ANIMATION_LENGTH_MS = 220;
-    private static final float INVERSE_ALPHA = 0.9f;
-    private static final float DIMMED_SCALE = 0.95f;
-
-    /**
-     * Normal state. Notification is fully interactable.
-     */
-    private static final int STATE_NORMAL = 0;
-
-    /**
-     * Dimmed state. Neutral state when on the lockscreen, with slight transparency and scaled down
-     * a bit.
-     */
-    private static final int STATE_DIMMED = 1;
-
-    /**
-     * Activated state. Used after tapping a notification on the lockscreen. Normal transparency and
-     * normal scale.
-     */
-    private static final int STATE_ACTIVATED = 2;
-
-    /**
-     * Inverse activated state. Used for the other notifications on the lockscreen when tapping on
-     * one.
-     */
-    private static final int STATE_ACTIVATED_INVERSE = 3;
-
-    private final View mTargetView;
-    private final View mHotspotView;
-    private final Interpolator mFastOutSlowInInterpolator;
-    private final Interpolator mLinearOutSlowInInterpolator;
-    private final int mTranslationZ;
-
-    private int mState;
-
-    public NotificationActivator(View targetView, View hotspotView) {
-        mTargetView = targetView;
-        mHotspotView = hotspotView;
-        Context ctx = targetView.getContext();
-        mFastOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
-        mLinearOutSlowInInterpolator =
-                AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in);
-        mTranslationZ =
-                ctx.getResources().getDimensionPixelSize(R.dimen.z_distance_between_notifications);
-        mTargetView.animate().setDuration(ANIMATION_LENGTH_MS);
-    }
-
-    public void activateInverse() {
-        if (mState == STATE_ACTIVATED_INVERSE) {
-            return;
-        }
-        mTargetView.animate().cancel();
-        mTargetView.animate().withLayer().alpha(INVERSE_ALPHA);
-        mState = STATE_ACTIVATED_INVERSE;
-    }
-
-    public void addHotspot() {
-        mHotspotView.getBackground().setHotspot(
-                0, mHotspotView.getWidth()/2, mHotspotView.getHeight()/2);
-    }
-
-    public void activate() {
-        if (mState == STATE_ACTIVATED) {
-            return;
-        }
-        mTargetView.animate().cancel();
-        mTargetView.animate()
-                .setInterpolator(mLinearOutSlowInInterpolator)
-                .scaleX(1)
-                .scaleY(1)
-                .translationZBy(mTranslationZ);
-        mState = STATE_ACTIVATED;
-    }
-
-    public void reset() {
-        if (mState == STATE_DIMMED) {
-            return;
-        }
-        mTargetView.animate().cancel();
-        mTargetView.animate()
-                .setInterpolator(mFastOutSlowInInterpolator)
-                .scaleX(DIMMED_SCALE)
-                .scaleY(DIMMED_SCALE)
-                .translationZBy(-mTranslationZ);
-        if (mTargetView.getAlpha() != 1.0f) {
-            mTargetView.animate().withLayer().alpha(1);
-        }
-        mState = STATE_DIMMED;
-    }
-
-    public void setDimmed(boolean dimmed, boolean fade) {
-        if (dimmed) {
-            mTargetView.animate().cancel();
-            if (fade) {
-                mTargetView.animate()
-                        .setInterpolator(mFastOutSlowInInterpolator)
-                        .scaleX(DIMMED_SCALE)
-                        .scaleY(DIMMED_SCALE);
-            } else {
-                mTargetView.setScaleX(DIMMED_SCALE);
-                mTargetView.setScaleY(DIMMED_SCALE);
-            }
-            mState = STATE_DIMMED;
-        } else {
-            mTargetView.animate().cancel();
-            if (fade) {
-                mTargetView.animate()
-                        .setInterpolator(mFastOutSlowInInterpolator)
-                        .scaleX(1)
-                        .scaleY(1);
-            } else {
-                mTargetView.animate().cancel();
-                mTargetView.setScaleX(1);
-                mTargetView.setScaleY(1);
-            }
-            mState = STATE_NORMAL;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 379bd05..9df2701 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -70,8 +70,8 @@
     }
 
     @Override
-    public void setActualHeight(int actualHeight) {
-        super.setActualHeight(actualHeight);
+    public void setActualHeight(int actualHeight, boolean notifyListeners) {
+        super.setActualHeight(actualHeight, notifyListeners);
         selectLayout();
         updateClipping();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index e6b5600..864c597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -28,14 +28,13 @@
 public class NotificationOverflowContainer extends ActivatableNotificationView {
 
     private NotificationOverflowIconsView mIconsView;
-    private NotificationActivator mActivator;
 
     public NotificationOverflowContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     @Override
-    public void setActualHeight(int currentHeight) {
+    public void setActualHeight(int currentHeight, boolean notifyListeners) {
         // noop
     }
 
@@ -54,22 +53,9 @@
         super.onFinishInflate();
         mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
         mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
-
-        mActivator = new NotificationActivator(this, this);
-        setDimmed(true, false);
-    }
-
-    @Override
-    public void setDimmed(boolean dimmed, boolean fade) {
-        super.setDimmed(dimmed, fade);
-        mActivator.setDimmed(dimmed, fade);
     }
 
     public NotificationOverflowIconsView getIconsView() {
         return mIconsView;
     }
-
-    public NotificationActivator getActivator() {
-        return mActivator;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 627b80f..f63ba9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,11 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
@@ -29,18 +35,40 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 public class NotificationPanelView extends PanelView implements
-        ExpandableView.OnHeightChangedListener {
+        ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
+        View.OnClickListener {
     public static final boolean DEBUG_GESTURES = true;
+    private static final int EXPANSION_ANIMATION_LENGTH = 375;
 
     PhoneStatusBar mStatusBar;
-    private View mHeader;
+    private StatusBarHeaderView mHeader;
+    private QuickSettingsContainerView mQsContainer;
     private View mKeyguardStatusView;
+    private ObservableScrollView mScrollView;
+    private View mStackScrollerContainer;
 
     private NotificationStackScrollLayout mNotificationStackScroller;
-    private boolean mTrackingSettings;
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
 
+    private Interpolator mExpansionInterpolator;
+
+    private int mTrackingPointer;
+    private VelocityTracker mVelocityTracker;
+    private boolean mTracking;
+    private boolean mQsExpanded;
+    private float mInitialHeightOnTouch;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mQsExpansionHeight;
+    private int mQsMinExpansionHeight;
+    private int mQsMaxExpansionHeight;
+    private int mMinStackHeight;
+    private float mNotificationTranslation;
+    private int mStackScrollerIntrinsicPadding;
+    private boolean mQsExpansionEnabled = true;
+    private ValueAnimator mQsExpansionAnimator;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,14 +91,21 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        mHeader = findViewById(R.id.header);
+        mHeader = (StatusBarHeaderView) findViewById(R.id.header);
+        mHeader.getBackgroundView().setOnClickListener(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
+        mStackScrollerContainer = findViewById(R.id.notification_container_parent);
+        mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
+        mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
+        mScrollView.setListener(this);
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
+        mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
+        mExpansionInterpolator = AnimationUtils.loadInterpolator(
+                getContext(), android.R.interpolator.fast_out_slow_in);
     }
 
     @Override
@@ -78,11 +113,21 @@
         super.onLayout(changed, left, top, right, bottom);
         int keyguardBottomMargin =
                 ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin;
-        mNotificationStackScroller.setTopPadding(mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
-                : mHeader.getBottom() + mNotificationTopPadding,
-                mAnimateNextTopPaddingChange);
-        mAnimateNextTopPaddingChange = false;
+        if (!mQsExpanded) {
+            mStackScrollerIntrinsicPadding = mStatusBar.getBarState() == StatusBarState.KEYGUARD
+                    ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
+                    : mHeader.getBottom() + mNotificationTopPadding;
+            mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
+                    mAnimateNextTopPaddingChange);
+            mAnimateNextTopPaddingChange = false;
+        }
+
+        // Calculate quick setting heights.
+        mQsMinExpansionHeight = mHeader.getCollapsedHeight();
+        mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
+        if (mQsExpansionHeight == 0) {
+            mQsExpansionHeight = mQsMinExpansionHeight;
+        }
     }
 
     public void animateNextTopPaddingChange() {
@@ -90,6 +135,30 @@
         requestLayout();
     }
 
+    /**
+     * @return Whether Quick Settings are currently expanded.
+     */
+    public boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+
+    public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
+        mQsExpansionEnabled = qsExpansionEnabled;
+        mHeader.setExpansionEnabled(qsExpansionEnabled);
+    }
+
+    public void closeQs() {
+        cancelAnimation();
+        setQsExpansion(mQsMinExpansionHeight);
+    }
+
+    public void openQs() {
+        cancelAnimation();
+        if (mQsExpansionEnabled) {
+            setQsExpansion(mQsMaxExpansionHeight);
+        }
+    }
+
     @Override
     public void fling(float vel, boolean always) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
@@ -114,42 +183,245 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        // intercept for quick settings
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final View target = mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                    ? mKeyguardStatusView
-                    : mHeader;
-            final boolean inTarget = PhoneStatusBar.inBounds(target, event, true);
-            if (inTarget && !isInSettings()) {
-                mTrackingSettings = true;
-                requestDisallowInterceptTouchEvent(true);
-                return true;
-            }
-            if (!inTarget && isInSettings()) {
-                mTrackingSettings = true;
-                requestDisallowInterceptTouchEvent(true);
-                return true;
-            }
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
         }
-        return super.onInterceptTouchEvent(event);
+        final float x = event.getX(pointerIndex);
+        final float y = event.getY(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                initVelocityTracker();
+                trackMovement(event);
+                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                    getParent().requestDisallowInterceptTouchEvent(true);
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchX = event.getX(newIndex);
+                    mInitialTouchY = event.getY(newIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                trackMovement(event);
+                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+                        && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                    onQsExpansionStarted();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTracking = true;
+                    return true;
+                }
+                break;
+        }
+        return !mQsExpanded && super.onInterceptTouchEvent(event);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
-        if (mTrackingSettings) {
-            mStatusBar.onSettingsEvent(event);
-            if (event.getAction() == MotionEvent.ACTION_UP
-                    || event.getAction() == MotionEvent.ACTION_CANCEL) {
-                mTrackingSettings = false;
+        if (mTracking) {
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float y = event.getY(pointerIndex);
+            final float x = event.getX(pointerIndex);
+
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mTracking = true;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    onQsExpansionStarted();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    initVelocityTracker();
+                    trackMovement(event);
+                    break;
+
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        final float newY = event.getY(newIndex);
+                        final float newX = event.getX(newIndex);
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        mInitialHeightOnTouch = mQsExpansionHeight;
+                        mInitialTouchY = newY;
+                        mInitialTouchX = newX;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    final float h = y - mInitialTouchY;
+                    setQsExpansion(h + mInitialHeightOnTouch);
+                    trackMovement(event);
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mTracking = false;
+                    mTrackingPointer = -1;
+                    trackMovement(event);
+
+                    float vel = getCurrentVelocity();
+
+                    // TODO: Better logic whether we should expand or not.
+                    flingSettings(vel, vel > 0);
+
+                    if (mVelocityTracker != null) {
+                        mVelocityTracker.recycle();
+                        mVelocityTracker = null;
+                    }
+                    break;
             }
             return true;
         }
-        if (isInSettings()) {
-            return true;
+
+        // Consume touch events when QS are expanded.
+        return mQsExpanded || super.onTouchEvent(event);
+    }
+
+    private void onQsExpansionStarted() {
+        cancelAnimation();
+
+        // Reset scroll position and apply that position to the expanded height.
+        float height = mQsExpansionHeight - mScrollView.getScrollY();
+        mScrollView.scrollTo(0, 0);
+        setQsExpansion(height);
+    }
+
+    private void expandQs() {
+        mHeader.setExpanded(true);
+        mNotificationStackScroller.setEnabled(false);
+        mScrollView.setVisibility(View.VISIBLE);
+        mQsExpanded = true;
+    }
+
+    private void collapseQs() {
+        mHeader.setExpanded(false);
+        mNotificationStackScroller.setEnabled(true);
+        mScrollView.setVisibility(View.INVISIBLE);
+        mQsExpanded = false;
+    }
+
+    private void setQsExpansion(float height) {
+        height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
+        if (height > mQsMinExpansionHeight && !mQsExpanded) {
+            expandQs();
+        } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
+            collapseQs();
         }
-        return super.onTouchEvent(event);
+        mQsExpansionHeight = height;
+        mHeader.setExpansion(height);
+        setQsTranslation(height);
+        setQsStackScrollerPadding(height);
+    }
+
+    private void setQsTranslation(float height) {
+        mQsContainer.setY(height - mQsContainer.getHeight());
+    }
+
+    private void setQsStackScrollerPadding(float height) {
+        float start = height - mScrollView.getScrollY() + mNotificationTopPadding;
+        float stackHeight = mNotificationStackScroller.getHeight() - start;
+        if (stackHeight <= mMinStackHeight) {
+            float overflow = mMinStackHeight - stackHeight;
+            stackHeight = mMinStackHeight;
+            start = mNotificationStackScroller.getHeight() - stackHeight;
+            mNotificationStackScroller.setTranslationY(overflow);
+            mNotificationTranslation = overflow + mScrollView.getScrollY();
+        } else {
+            mNotificationStackScroller.setTranslationY(0);
+            mNotificationTranslation = mScrollView.getScrollY();
+        }
+        mNotificationStackScroller.setTopPadding(clampQsStackScrollerPadding((int) start), false);
+    }
+
+    private int clampQsStackScrollerPadding(int desiredPadding) {
+        return Math.max(desiredPadding, mStackScrollerIntrinsicPadding);
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getYVelocity();
+    }
+
+    private void cancelAnimation() {
+        if (mQsExpansionAnimator != null) {
+            mQsExpansionAnimator.cancel();
+        }
+    }
+    private void flingSettings(float vel, boolean expand) {
+
+        // TODO: Actually use velocity.
+
+        float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
+        ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
+        animator.setDuration(EXPANSION_ANIMATION_LENGTH);
+        animator.setInterpolator(mExpansionInterpolator);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                setQsExpansion((Float) animation.getAnimatedValue());
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mQsExpansionAnimator = null;
+            }
+        });
+        animator.start();
+        mQsExpansionAnimator = animator;
+    }
+
+    /**
+     * @return Whether we should intercept a gesture to open Quick Settings.
+     */
+    private boolean shouldIntercept(float x, float y, float yDiff) {
+        if (!mQsExpansionEnabled) {
+            return false;
+        }
+        View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded
+                ? mKeyguardStatusView
+                : mHeader;
+        boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight()
+                && y >= headerView.getTop() && y <= headerView.getBottom();
+        if (mQsExpanded) {
+            return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
+        } else {
+            return onHeader;
+        }
     }
 
     @Override
@@ -164,14 +436,16 @@
     protected int getMaxPanelHeight() {
         if (!isInSettings()) {
             int maxPanelHeight = super.getMaxPanelHeight();
-            int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+            int notificationMarginBottom = mStackScrollerContainer.getPaddingBottom();
+            int emptyBottomMargin = notificationMarginBottom
+                    + mNotificationStackScroller.getEmptyBottomMargin();
             return maxPanelHeight - emptyBottomMargin;
         }
         return super.getMaxPanelHeight();
     }
 
     private boolean isInSettings() {
-        return mStatusBar != null && mStatusBar.isFlippedToSettings();
+        return mQsExpanded;
     }
 
     @Override
@@ -200,4 +474,24 @@
     public void onHeightChanged(ExpandableView view) {
         requestPanelHeightUpdate();
     }
+
+    @Override
+    public void onScrollChanged() {
+        if (mQsExpanded) {
+            mNotificationStackScroller.setTranslationY(
+                    mNotificationTranslation - mScrollView.getScrollY());
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mHeader.getBackgroundView()) {
+            onQsExpansionStarted();
+            if (mQsExpanded) {
+                flingSettings(0 /* vel */, false /* expand */);
+            } else if (mQsExpansionEnabled) {
+                flingSettings(0 /* vel */, true /* expand */);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
new file mode 100644
index 0000000..f41e78d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * The container with notification stack scroller and quick settings inside.
+ */
+public class NotificationsQuickSettingsContainer extends FrameLayout {
+
+    public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        setPadding(0, 0, 0, insets.bottom);
+        insets.bottom = 0;
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
new file mode 100644
index 0000000..46484f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -0,0 +1,64 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ScrollView;
+
+/**
+ * A scroll view which can be observed for scroll change events.
+ */
+public class ObservableScrollView extends ScrollView {
+
+    private Listener mListener;
+
+    public ObservableScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    public boolean isScrolledToBottom() {
+        return getScrollY() == getMaxScrollY();
+    }
+
+    private int getMaxScrollY() {
+        int scrollRange = 0;
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            scrollRange = Math.max(0,
+                    child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+        }
+        return scrollRange;
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        if (mListener != null) {
+            mListener.onScrollChanged();
+        }
+    }
+
+    public interface Listener {
+        void onScrollChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index a853b2a..8c70517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -75,7 +75,7 @@
     private boolean mClosing;
     private boolean mTracking;
     private int mTrackingPointer;
-    private int mTouchSlop;
+    protected int mTouchSlop;
 
     private TimeAnimator mTimeAnimator;
     private ObjectAnimator mPeekAnimator;
@@ -220,9 +220,9 @@
     private float mVel, mAccel;
     protected int mMaxPanelHeight = 0;
     private String mViewName;
-    protected float mInitialTouchY;
-    protected float mInitialTouchX;
-    protected float mFinalTouchY;
+    private float mInitialTouchY;
+    private float mInitialTouchX;
+    private float mFinalTouchY;
 
     protected void onExpandingFinished() {
     }
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 3856ba1..92eee4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -73,7 +73,6 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
@@ -192,8 +191,6 @@
     int mIconHPadding = -1;
     Display mDisplay;
     Point mCurrentDisplaySize = new Point();
-    private float mHeadsUpVerticalOffset;
-    private int[] mStackScrollerPosition = new int[2];
 
     StatusBarWindowView mStatusBarWindow;
     PhoneStatusBarView mStatusBarView;
@@ -222,14 +219,13 @@
     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
     View mExpandedContents;
     int mNotificationPanelGravity;
-    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
+    int mNotificationPanelMarginBottomPx;
     float mNotificationPanelMinHeightFrac;
     boolean mNotificationPanelIsFullScreenWidth;
     TextView mNotificationPanelDebugText;
 
     // settings
     QuickSettings mQS;
-    boolean mHasQuickSettings;
     View mFlipSettingsView;
     QuickSettingsContainerView mSettingsContainer;
 
@@ -245,14 +241,14 @@
     int mKeyguardMaxNotificationCount;
     View mDateTimeView;
     View mClearButton;
-    FlipperButton mHeaderFlipper, mKeyguardFlipper;
+    ImageView mHeaderFlipper;
 
     // carrier/wifi label
     private TextView mCarrierLabel;
     private boolean mCarrierLabelVisible = false;
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
-    private int mNotificationHeaderHeight;
+    private int mStatusBarHeaderHeight;
     private View mKeyguardCarrierLabel;
 
     private boolean mShowCarrierInPanel = false;
@@ -326,11 +322,12 @@
             if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
                     "selfChange=%s userSetup=%s mUserSetup=%s",
                     selfChange, userSetup, mUserSetup));
-            mHeaderFlipper.userSetup(userSetup);
-            mKeyguardFlipper.userSetup(userSetup);
 
             if (userSetup != mUserSetup) {
                 mUserSetup = userSetup;
+                if (mNotificationPanel != null) {
+                    mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup);
+                }
                 if (!mUserSetup && mStatusBarView != null)
                     animateCollapseQuickSettings();
             }
@@ -631,8 +628,7 @@
                         R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
         mKeyguardIconOverflowContainer.setOnActivatedListener(this);
         mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
-        // TODO: Comment in when transition is ready.
-        //mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
+        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
         mStackScroller.addView(mKeyguardIconOverflowContainer);
 
         mExpandedContents = mStackScroller;
@@ -651,16 +647,13 @@
         mClearButton.setEnabled(false);
         mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
 
-        mHasQuickSettings = res.getBoolean(R.bool.config_hasQuickSettings);
-
         mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
         if (mDateTimeView != null) {
             mDateTimeView.setOnClickListener(mClockClickListener);
             mDateTimeView.setEnabled(true);
         }
 
-        mHeaderFlipper = new FlipperButton(mStatusBarWindow.findViewById(R.id.header_flipper));
-        mKeyguardFlipper =new FlipperButton(mStatusBarWindow.findViewById(R.id.keyguard_flipper));
+        mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper);
 
         if (!mNotificationPanelIsFullScreenWidth) {
             mNotificationPanel.setSystemUiVisibility(
@@ -735,26 +728,23 @@
 //                    updateCarrierLabelVisibility(false);
         }
 
-        // Quick Settings (where available, some restrictions apply)
-        if (mHasQuickSettings) {
-            // Quick Settings needs a container to survive
-            mSettingsContainer = (QuickSettingsContainerView)
-                    mStatusBarWindow.findViewById(R.id.quick_settings_container);
-            mFlipSettingsView = mSettingsContainer;
-            if (mSettingsContainer != null) {
-                mQS = new QuickSettings(mContext, mSettingsContainer);
-                if (!mNotificationPanelIsFullScreenWidth) {
-                    mSettingsContainer.setSystemUiVisibility(
-                            View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
-                            | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
-                }
-                mQS.setService(this);
-                mQS.setBar(mStatusBarView);
-                mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
-                        mLocationController, mRotationLockController);
-            } else {
-                mQS = null; // fly away, be free
+        // Quick Settings needs a container to survive
+        mSettingsContainer = (QuickSettingsContainerView)
+                mStatusBarWindow.findViewById(R.id.quick_settings_container);
+        mFlipSettingsView = mSettingsContainer;
+        if (mSettingsContainer != null) {
+            mQS = new QuickSettings(mContext, mSettingsContainer);
+            if (!mNotificationPanelIsFullScreenWidth) {
+                mSettingsContainer.setSystemUiVisibility(
+                        View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
+                        | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
             }
+            mQS.setService(this);
+            mQS.setBar(mStatusBarView);
+            mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
+                    mLocationController, mRotationLockController);
+        } else {
+            mQS = null; // fly away, be free
         }
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -775,101 +765,6 @@
         return mStatusBarView;
     }
 
-    public boolean onSettingsEvent(MotionEvent event) {
-        userActivity();
-        if (mSettingsClosing
-                && mFlipSettingsViewAnim != null && mFlipSettingsViewAnim.isRunning()) {
-            return true;
-        }
-        if (mSettingsTracker != null) {
-            mSettingsTracker.addMovement(event);
-        }
-        final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mSettingsTracker = VelocityTracker.obtain();
-            mSettingsDownY = event.getY();
-            mSettingsCancelled = false;
-            mSettingsStarted = false;
-            mSettingsClosing = mFlipSettingsView.getVisibility() == View.VISIBLE;
-            if (mSettingsClosing) {
-                mStackScroller.setVisibility(View.VISIBLE);
-            } else {
-                mFlipSettingsView.setTranslationY(-mNotificationPanel.getMeasuredHeight());
-            }
-            dispatchSettingsEvent(event);
-        } else if (mSettingsTracker != null && (event.getAction() == MotionEvent.ACTION_UP
-                || event.getAction() == MotionEvent.ACTION_CANCEL)) {
-            final float dy = event.getY() - mSettingsDownY;
-            final FlipperButton flipper = mState == StatusBarState.KEYGUARD
-                    ? mKeyguardFlipper
-                    : mHeaderFlipper;
-            final boolean inButton = flipper.inHolderBounds(event);
-            final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop;
-            if (!qsTap && !inButton) {
-                mSettingsTracker.computeCurrentVelocity(1000);
-                final float vy = mSettingsTracker.getYVelocity();
-                final boolean animate = true;
-                if (dy <= slop || vy <= 0) {
-                    flipToNotifications(animate);
-                } else {
-                    flipToSettings(animate);
-                }
-            }
-            mSettingsTracker.recycle();
-            mSettingsTracker = null;
-            dispatchSettingsEvent(event);
-        } else if (mSettingsTracker != null && event.getAction() == MotionEvent.ACTION_MOVE) {
-            final float dy = event.getY() - mSettingsDownY;
-            if (mSettingsClosing) {
-                positionSettings(dy);
-                final boolean qsTap = Math.abs(dy) < slop;
-                if (!mSettingsCancelled && !qsTap) {
-                    MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
-                    cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
-                    dispatchSettingsEvent(cancelEvent);
-                    mSettingsCancelled = true;
-                }
-            } else {
-                if (!mSettingsStarted && dy > slop) {
-                    mSettingsStarted = true;
-                    mFlipSettingsView.setVisibility(View.VISIBLE);
-                    mStackScroller.setVisibility(View.VISIBLE);
-                }
-                if (mSettingsStarted) {
-                    positionSettings(dy);
-                }
-                dispatchSettingsEvent(event);
-            }
-        }
-        return true;
-    }
-
-    private void dispatchSettingsEvent(MotionEvent event) {
-        final View target = mSettingsClosing ? mFlipSettingsView : mNotificationPanelHeader;
-        final int[] targetLoc = new int[2];
-        target.getLocationInWindow(targetLoc);
-        final int[] panelLoc = new int[2];
-        mNotificationPanel.getLocationInWindow(panelLoc);
-        final int dx = targetLoc[0] - panelLoc[0];
-        final int dy = targetLoc[1] - panelLoc[1];
-        event.offsetLocation(-dx, -dy);
-        target.dispatchTouchEvent(event);
-    }
-
-    private void positionSettings(float dy) {
-        if (mSettingsClosing) {
-            final int ph = mNotificationPanel.getMeasuredHeight();
-            dy = Math.min(Math.max(-ph, dy), 0);
-            mFlipSettingsView.setTranslationY(dy);
-            mStackScroller.setTranslationY(ph + dy);
-        } else {
-            final int h = mFlipSettingsView.getBottom();
-            dy = Math.min(Math.max(0, dy), h);
-            mFlipSettingsView.setTranslationY(-h + dy);
-            mStackScroller.setTranslationY(dy);
-        }
-    }
-
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
@@ -1230,8 +1125,11 @@
             ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
         }
 
-        mHeaderFlipper.refreshLayout();
-        mKeyguardFlipper.refreshLayout();
+        if (mHeaderFlipper != null) {
+            // Force asset reloading
+            mHeaderFlipper.setImageDrawable(null);
+            mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings);
+        }
 
         refreshAllStatusBarIcons();
     }
@@ -1286,8 +1184,7 @@
             }
         }
 
-        mHeaderFlipper.provisionCheck(provisioned);
-        mKeyguardFlipper.provisionCheck(provisioned);
+        mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
     }
 
     @Override
@@ -1362,7 +1259,7 @@
         final boolean makeVisible =
             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
             && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
-                    - mCarrierLabelHeight - mNotificationHeaderHeight)
+                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
             && mStackScroller.getVisibility() == View.VISIBLE
             && mState != StatusBarState.KEYGUARD;
 
@@ -1775,47 +1672,8 @@
     }
 
     public void flipToNotifications(boolean animate) {
-        cancelAnim(mFlipSettingsViewAnim);
-        cancelAnim(mScrollViewAnim);
-        cancelAnim(mClearButtonAnim);
-        mHeaderFlipper.cancel();
-        mKeyguardFlipper.cancel();
-        mStackScroller.setVisibility(View.VISIBLE);
-        final int h = mNotificationPanel.getMeasuredHeight();
-        if (animate) {
-            final float settingsY =
-                    mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0;
-            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h;
-            mScrollViewAnim = start(
-                    interpolator(mDecelerateInterpolator,
-                        ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0)
-                            .setDuration(FLIP_DURATION)
-                        ));
-            mFlipSettingsViewAnim = start(
-                setVisibilityWhenDone(
-                    interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(
-                                    mFlipSettingsView, View.TRANSLATION_Y, settingsY, -h))
-                        .setDuration(FLIP_DURATION),
-                    mFlipSettingsView, View.INVISIBLE));
-        } else {
-            mStackScroller.setTranslationY(0);
-            mFlipSettingsView.setTranslationY(-h);
-            mFlipSettingsView.setVisibility(View.INVISIBLE);
-        }
-        mHeaderFlipper.flipToNotifications(animate);
-        mKeyguardFlipper.flipToNotifications(animate);
-        mClearButton.setVisibility(View.VISIBLE);
-        mClearButton.setAlpha(0f);
-        setAreThereNotifications(); // this will show/hide the button as necessary
-        mNotificationPanel.postDelayed(new Runnable() {
-            public void run() {
-                updateCarrierLabelVisibility(false);
-            }
-        }, animate ? FLIP_DURATION - 150 : 0);
-        if (mOnFlipRunnable != null) {
-            mOnFlipRunnable.run();
-        }
+        // TODO: Animation
+        mNotificationPanel.closeQs();
     }
 
     @Override
@@ -1829,78 +1687,18 @@
         if (!mUserSetup) return;
 
         mNotificationPanel.expand();
-        if (mFlipSettingsView.getVisibility() != View.VISIBLE
-                || mFlipSettingsView.getTranslationY() < 0) {
-            flipToSettings(true /*animate*/);
-        }
+        mNotificationPanel.openQs();
 
         if (false) postStartTracing();
     }
 
     public boolean isFlippedToSettings() {
-        if (mFlipSettingsView != null) {
-            return mFlipSettingsView.getVisibility() == View.VISIBLE;
+        if (mNotificationPanel != null) {
+            return mNotificationPanel.isQsExpanded();
         }
         return false;
     }
 
-    public void flipToSettings(boolean animate) {
-        // Settings are not available in setup
-        if (!mUserSetup) return;
-
-        cancelAnim(mFlipSettingsViewAnim);
-        cancelAnim(mScrollViewAnim);
-        mHeaderFlipper.cancel();
-        mKeyguardFlipper.cancel();
-        cancelAnim(mClearButtonAnim);
-
-        mFlipSettingsView.setVisibility(View.VISIBLE);
-        final int h = mNotificationPanel.getMeasuredHeight();
-        if (animate) {
-            final float settingsY
-                    = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : -h;
-            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : 0;
-            mFlipSettingsViewAnim = start(
-                    startDelay(0,
-                        interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y,
-                                    settingsY, 0f)
-                                .setDuration(FLIP_DURATION)
-                            )));
-            mScrollViewAnim = start(
-                setVisibilityWhenDone(
-                    interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, h)
-                            )
-                        .setDuration(FLIP_DURATION),
-                        mStackScroller, View.INVISIBLE));
-        } else {
-            mFlipSettingsView.setTranslationY(0);
-            mStackScroller.setTranslationY(h);
-            mStackScroller.setVisibility(View.INVISIBLE);
-        }
-        mHeaderFlipper.flipToSettings(animate);
-        mKeyguardFlipper.flipToSettings(animate);
-        if (animate) {
-            mClearButtonAnim = start(
-                setVisibilityWhenDone(
-                    ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
-                    .setDuration(FLIP_DURATION),
-                    mClearButton, View.INVISIBLE));
-        } else {
-            mClearButton.setAlpha(0);
-            mClearButton.setVisibility(View.INVISIBLE);
-        }
-        mNotificationPanel.postDelayed(new Runnable() {
-            public void run() {
-                updateCarrierLabelVisibility(false);
-            }
-        }, animate ? FLIP_DURATION - 150 : 0);
-        if (mOnFlipRunnable != null) {
-            mOnFlipRunnable.run();
-        }
-    }
-
     public void animateCollapseQuickSettings() {
         mStatusBarView.collapseAllPanels(true);
     }
@@ -1927,12 +1725,10 @@
 
         mStackScroller.setVisibility(View.VISIBLE);
         mNotificationPanel.setVisibility(View.GONE);
-        mFlipSettingsView.setVisibility(View.GONE);
 
         setAreThereNotifications(); // show the clear button
 
-        mHeaderFlipper.reset();
-        mKeyguardFlipper.reset();
+        mNotificationPanel.closeQs();
 
         mExpandedVisible = false;
         if (mNavigationBarView != null)
@@ -2463,20 +2259,11 @@
         }
     }
 
-    void updateExpandedInvisiblePosition() {
-        mTrackingPosition = -mDisplayMetrics.heightPixels;
-    }
-
     static final float saturate(float a) {
         return a < 0f ? 0f : (a > 1f ? 1f : a);
     }
 
     @Override
-    protected int getExpandedViewMaxHeight() {
-        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
-    }
-
-    @Override
     public void updateExpandedViewPos(int thingy) {
         if (SPEW) Log.v(TAG, "updateExpandedViewPos");
 
@@ -2486,15 +2273,8 @@
 
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
         lp.gravity = mNotificationPanelGravity;
-        lp.setMarginStart(mNotificationPanelMarginPx);
         mNotificationPanel.setLayoutParams(lp);
 
-        if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
-            mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
-            mStackScroller.getLocationOnScreen(mStackScrollerPosition);
-            mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight;
-        }
-
         updateCarrierLabelVisibility(false);
     }
 
@@ -2542,17 +2322,6 @@
         animateCollapsePanels();
     }
 
-    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            if (mHasQuickSettings) {
-                animateExpandSettingsPanel();
-            } else {
-                startActivityDismissingKeyguard(
-                        new Intent(android.provider.Settings.ACTION_SETTINGS), true);
-            }
-        }
-    };
-
     private View.OnClickListener mClockClickListener = new View.OnClickListener() {
         public void onClick(View v) {
             startActivityDismissingKeyguard(
@@ -2653,17 +2422,6 @@
         }
     }
 
-    public void animateHeadsUp(boolean animateInto, float frac) {
-        if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return;
-        frac = frac / 0.4f;
-        frac = frac < 1.0f ? frac : 1.0f;
-        float alpha = 1.0f - frac;
-        float offset = mHeadsUpVerticalOffset * frac;
-        offset = animateInto ? offset : 0f;
-        mHeadsUpNotificationView.setAlpha(alpha);
-        mHeadsUpNotificationView.setY(offset);
-    }
-
     public void onHeadsUpDismissed() {
         if (mInterruptingNotificationEntry == null) return;
         mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
@@ -2736,17 +2494,13 @@
 
         mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
 
-        mNotificationPanelMarginBottomPx
-            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
-        mNotificationPanelMarginPx
-            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
         if (mNotificationPanelGravity <= 0) {
             mNotificationPanelGravity = Gravity.START | Gravity.TOP;
         }
 
         mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
-        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
+        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
 
         mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
         if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
@@ -3016,9 +2770,6 @@
 
     private void updateKeyguardState() {
         if (mState == StatusBarState.KEYGUARD) {
-            if (isFlippedToSettings()) {
-                flipToNotifications(false /*animate*/);
-            }
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
@@ -3026,7 +2777,7 @@
             mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
             mNotificationPanelHeader.setVisibility(View.GONE);
 
-            mKeyguardFlipper.setVisibility(View.VISIBLE);
+            mNotificationPanel.closeQs();
             mSettingsContainer.setKeyguardShowing(true);
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
@@ -3035,10 +2786,10 @@
             mKeyguardCarrierLabel.setVisibility(View.GONE);
             mNotificationPanelHeader.setVisibility(View.VISIBLE);
 
-            mKeyguardFlipper.setVisibility(View.GONE);
             mSettingsContainer.setKeyguardShowing(false);
         }
 
+        updateStackScrollerState();
         updatePublicMode();
         updateRowStates();
         checkBarModes();
@@ -3046,6 +2797,10 @@
         updateCarrierLabelVisibility(false);
     }
 
+    public void updateStackScrollerState() {
+        mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+    }
+
     public void userActivity() {
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardViewMediatorCallback.userActivity();
@@ -3099,7 +2854,7 @@
     public void onActivated(View view) {
         userActivity();
         mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again);
-        super.onActivated(view);
+        mStackScroller.setActivatedChild(view);
     }
 
     /**
@@ -3111,9 +2866,11 @@
     }
 
     @Override
-    public void onReset(View view) {
-        super.onReset(view);
-        mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+    public void onActivationReset(View view) {
+        if (view == mStackScroller.getActivatedChild()) {
+            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+            mStackScroller.setActivatedChild(null);
+        }
     }
 
     public void onTrackingStarted() {
@@ -3145,30 +2902,12 @@
     }
 
     @Override
-    public void onReset() {
-        int n = mNotificationData.size();
-        for (int i = 0; i < n; i++) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE) {
-                entry.row.setDimmed(true /* dimmed */, true /* fade */);
-            }
-        }
-        if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
-            mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, true /* fade */);
-        }
+    public void onDragDownReset() {
+        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
     }
 
     public void onThresholdReached() {
-        int n = mNotificationData.size();
-        for (int i = 0; i < n; i++) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE) {
-                entry.row.setDimmed(false /* dimmed */, true /* fade */);
-            }
-        }
-        if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
-            mKeyguardIconOverflowContainer.setDimmed(false /* dimmed */, true /* fade */);
-        }
+        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
     }
 
     /**
@@ -3202,131 +2941,6 @@
      * @return a ViewGroup that spans the entire panel which contains the quick settings
      */
     public ViewGroup getQuickSettingsOverlayParent() {
-        if (mHasQuickSettings) {
-            return mNotificationPanel;
-        } else {
-            return null;
-        }
-    }
-
-    public static boolean inBounds(View view, MotionEvent event, boolean orAbove) {
-        final int[] location = new int[2];
-        view.getLocationInWindow(location);
-        final int rx = (int) event.getRawX();
-        final int ry = (int) event.getRawY();
-        return rx >= location[0] && rx <= location[0] + view.getMeasuredWidth()
-                && (orAbove || ry >= location[1]) && ry <= location[1] + view.getMeasuredHeight();
-    }
-
-    private final class FlipperButton {
-        private final View mHolder;
-
-        private ImageView mSettingsButton, mNotificationButton;
-        private Animator mSettingsButtonAnim, mNotificationButtonAnim;
-
-        public FlipperButton(View holder) {
-            mHolder = holder;
-            mSettingsButton = (ImageView) holder.findViewById(R.id.settings_button);
-            if (mSettingsButton != null) {
-                mSettingsButton.setOnClickListener(mSettingsButtonListener);
-                if (mHasQuickSettings) {
-                    // the settings panel is hiding behind this button
-                    mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
-                    mSettingsButton.setVisibility(View.VISIBLE);
-                } else {
-                    // no settings panel, go straight to settings
-                    mSettingsButton.setVisibility(View.VISIBLE);
-                    mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
-                }
-            }
-            mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button);
-            if (mNotificationButton != null) {
-                mNotificationButton.setOnClickListener(mNotificationButtonListener);
-            }
-        }
-
-        public boolean inHolderBounds(MotionEvent event) {
-            return inBounds(mHolder, event, false);
-        }
-
-        public void provisionCheck(boolean provisioned) {
-            if (mSettingsButton != null) {
-                mSettingsButton.setEnabled(provisioned);
-            }
-        }
-
-        public void userSetup(boolean userSetup) {
-            if (mSettingsButton != null) {
-                mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
-            }
-        }
-
-        public void reset() {
-            cancel();
-            mSettingsButton.setVisibility(View.VISIBLE);
-            mNotificationButton.setVisibility(View.GONE);
-        }
-
-        public void refreshLayout() {
-            if (mSettingsButton != null) {
-                // Force asset reloading
-                mSettingsButton.setImageDrawable(null);
-                mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
-            }
-
-            if (mNotificationButton != null) {
-                // Force asset reloading
-                mNotificationButton.setImageDrawable(null);
-                mNotificationButton.setImageResource(R.drawable.ic_notifications);
-            }
-        }
-
-        public void flipToSettings(boolean animate) {
-            mNotificationButton.setVisibility(View.VISIBLE);
-            if (animate) {
-                mSettingsButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION_OUT),
-                        mStackScroller, View.INVISIBLE));
-                mNotificationButtonAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
-                            .setDuration(FLIP_DURATION_IN)));
-            } else {
-                mSettingsButton.setAlpha(0f);
-                mSettingsButton.setVisibility(View.INVISIBLE);
-                mNotificationButton.setAlpha(1f);
-            }
-        }
-
-        public void flipToNotifications(boolean animate) {
-            mSettingsButton.setVisibility(View.VISIBLE);
-            if (animate) {
-                mNotificationButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION_OUT),
-                        mNotificationButton, View.INVISIBLE));
-
-                mSettingsButtonAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
-                            .setDuration(FLIP_DURATION_IN)));
-            } else {
-                mNotificationButton.setVisibility(View.INVISIBLE);
-                mNotificationButton.setAlpha(0f);
-                mSettingsButton.setAlpha(1f);
-            }
-        }
-
-        public void cancel() {
-            cancelAnim(mSettingsButtonAnim);
-            cancelAnim(mNotificationButtonAnim);
-        }
-
-        public void setVisibility(int vis) {
-            mHolder.setVisibility(vis);
-        }
+        return mNotificationPanel;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 10c1625..e6de057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -220,8 +220,6 @@
             panel.setAlpha(alpha);
         }
 
-        mBar.animateHeadsUp(mNotificationPanel == panel, mPanelExpandedFractionSum);
-
         mBar.updateCarrierLabelVisibility(false);
         mBar.userActivity();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
new file mode 100644
index 0000000..9d33930
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -0,0 +1,97 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import com.android.systemui.R;
+
+/**
+ * The view to manage the header area in the expanded status bar.
+ */
+public class StatusBarHeaderView extends RelativeLayout {
+
+    private boolean mExpanded;
+    private View mBackground;
+    private View mFlipper;
+
+    private int mCollapsedHeight;
+    private int mExpandedHeight;
+
+    public StatusBarHeaderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBackground = findViewById(R.id.background);
+        mFlipper = findViewById(R.id.header_flipper);
+        loadDimens();
+    }
+
+    private void loadDimens() {
+        mCollapsedHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height);
+        mExpandedHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height_expanded);
+    }
+
+    public int getCollapsedHeight() {
+        return mCollapsedHeight;
+    }
+
+    public int getExpandedHeight() {
+        return mExpandedHeight;
+    }
+
+    public void setExpanded(boolean expanded) {
+        if (expanded != mExpanded) {
+            ViewGroup.LayoutParams lp = getLayoutParams();
+            lp.height = expanded ? mExpandedHeight : mCollapsedHeight;
+            setLayoutParams(lp);
+            mExpanded = expanded;
+        }
+    }
+
+    public void setExpansionEnabled(boolean enabled) {
+        mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE);
+    }
+
+    public void setExpansion(float height) {
+        if (height < mCollapsedHeight) {
+            height = mCollapsedHeight;
+        }
+        if (height > mExpandedHeight) {
+            height = mExpandedHeight;
+        }
+        if (mExpanded) {
+            mBackground.setTranslationY(-(mExpandedHeight - height));
+        } else {
+            mBackground.setTranslationY(0);
+        }
+    }
+
+    public View getBackgroundView() {
+        return mBackground;
+    }
+}
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 4c9264d..e802d185 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -56,11 +56,14 @@
     @Override
     protected boolean fitSystemWindows(Rect insets) {
         if (getFitsSystemWindows()) {
-            setPadding(insets.left, insets.top, insets.right, insets.bottom);
+            setPadding(insets.left, insets.top, insets.right, 0);
+            insets.left = 0;
+            insets.top = 0;
+            insets.right = 0;
         } else {
             setPadding(0, 0, 0, 0);
         }
-        return true;
+        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
new file mode 100644
index 0000000..4121a40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -0,0 +1,75 @@
+/*
+ * 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.stack;
+
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * A global state to track all input states for the algorithm.
+ */
+public class AmbientState {
+    private ArrayList<View> mDraggedViews = new ArrayList<View>();
+    private int mScrollY;
+    private boolean mDimmed;
+    private View mActivatedChild;
+
+    public int getScrollY() {
+        return mScrollY;
+    }
+
+    public void setScrollY(int scrollY) {
+        this.mScrollY = scrollY;
+    }
+
+    public void onBeginDrag(View view) {
+        mDraggedViews.add(view);
+    }
+
+    public void onDragFinished(View view) {
+        mDraggedViews.remove(view);
+    }
+
+    public ArrayList<View> getDraggedViews() {
+        return mDraggedViews;
+    }
+
+    /**
+     * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
+     *               translucent and everything is scaled back a bit.
+     */
+    public void setDimmed(boolean dimmed) {
+        mDimmed = dimmed;
+    }
+
+    /**
+     * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
+     * interaction. This child is then scaled normally and its background is fully opaque.
+     */
+    public void setActivatedChild(View activatedChild) {
+        mActivatedChild = activatedChild;
+    }
+
+    public boolean isDimmed() {
+        return mDimmed;
+    }
+
+    public View getActivatedChild() {
+        return mActivatedChild;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
new file mode 100644
index 0000000..41914ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.stack;
+
+import java.util.ArrayList;
+
+/**
+ * Filters the animations for only a certain type of properties.
+ */
+public class AnimationFilter {
+    boolean animateAlpha;
+    boolean animateY;
+    boolean animateZ;
+    boolean animateScale;
+    boolean animateHeight;
+    boolean animateDimmed;
+
+    public AnimationFilter animateAlpha() {
+        animateAlpha = true;
+        return this;
+    }
+
+    public AnimationFilter animateY() {
+        animateY = true;
+        return this;
+    }
+
+    public AnimationFilter animateZ() {
+        animateZ = true;
+        return this;
+    }
+
+    public AnimationFilter animateScale() {
+        animateScale = true;
+        return this;
+    }
+
+    public AnimationFilter animateHeight() {
+        animateHeight = true;
+        return this;
+    }
+
+    public AnimationFilter animateDimmed() {
+        animateDimmed = true;
+        return this;
+    }
+
+    /**
+     * Combines multiple filters into {@code this} filter, using or as the operand .
+     *
+     * @param events The animation events from the filters to combine.
+     */
+    public void applyCombination(ArrayList<NotificationStackScrollLayout.AnimationEvent> events) {
+        reset();
+        int size = events.size();
+        for (int i = 0; i < size; i++) {
+            combineFilter(events.get(i).filter);
+        }
+    }
+
+    private void combineFilter(AnimationFilter filter) {
+        animateAlpha |= filter.animateAlpha;
+        animateY |= filter.animateY;
+        animateZ |= filter.animateZ;
+        animateScale |= filter.animateScale;
+        animateHeight |= filter.animateHeight;
+        animateDimmed |= filter.animateDimmed;
+    }
+
+    private void reset() {
+        animateAlpha = false;
+        animateY = false;
+        animateZ = false;
+        animateScale = false;
+        animateHeight = false;
+        animateDimmed = false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index f8aab80..afd5068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -84,7 +84,6 @@
     private int mEmptyMarginBottom;
     private int mPaddingBetweenElements;
     private int mTopPadding;
-    private boolean mListenForHeightChanges = true;
 
     /**
      * The algorithm which calculates the properties for our children
@@ -95,6 +94,7 @@
      * The current State this Layout is in
      */
     private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
+    private AmbientState mAmbientState = new AmbientState();
     private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
     private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
     private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
@@ -108,6 +108,8 @@
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
     private boolean mNeedsAnimation;
     private boolean mTopPaddingNeedsAnimation;
+    private boolean mDimmedNeedsAnimation;
+    private boolean mActivateNeedsAnimation;
     private boolean mIsExpanded = true;
     private boolean mChildrenUpdateRequested;
     private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
@@ -267,8 +269,8 @@
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
      */
     private void updateChildren() {
-        mCurrentStackScrollState.setScrollY(mOwnScrollY);
-        mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
+        mAmbientState.setScrollY(mOwnScrollY);
+        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
             applyCurrentState();
         } else {
@@ -385,12 +387,12 @@
             mDragAnimPendingChildren.remove(v);
         }
         mSwipedOutViews.add(v);
-        mStackScrollAlgorithm.onDragFinished(v);
+        mAmbientState.onDragFinished(v);
     }
 
     @Override
     public void onChildSnappedBack(View animView) {
-        mStackScrollAlgorithm.onDragFinished(animView);
+        mAmbientState.onDragFinished(animView);
         if (!mDragAnimPendingChildren.contains(animView)) {
             mSnappedBackChildren.add(animView);
             requestChildrenUpdate();
@@ -404,7 +406,7 @@
     public void onBeginDrag(View v) {
         setSwipingInProgress(true);
         mDragAnimPendingChildren.add(v);
-        mStackScrollAlgorithm.onBeginDrag(v);
+        mAmbientState.onBeginDrag(v);
         requestChildrenUpdate();
         mNeedsAnimation = true;
     }
@@ -492,6 +494,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        if (!isEnabled()) {
+            return false;
+        }
         boolean scrollerWantsIt = false;
         if (!mSwipingInProgress) {
             scrollerWantsIt = onScrollTouch(ev);
@@ -511,7 +516,7 @@
 
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
-                if (getChildCount() == 0) {
+                if (getChildCount() == 0 || !isInContentBounds(ev)) {
                     return false;
                 }
                 boolean isBeingDragged = !mScroller.isFinished();
@@ -962,6 +967,8 @@
         generateSnapBackEvents();
         generateDragEvents();
         generateTopPaddingEvent();
+        generateActivateEvent();
+        generateDimmedEvent();
         mNeedsAnimation = false;
     }
 
@@ -1009,6 +1016,22 @@
         mTopPaddingNeedsAnimation = false;
     }
 
+    private void generateActivateEvent() {
+        if (mActivateNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
+        }
+        mActivateNeedsAnimation = false;
+    }
+
+    private void generateDimmedEvent() {
+        if (mDimmedNeedsAnimation) {
+            mAnimationEvents.add(
+                    new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
+        }
+        mDimmedNeedsAnimation = false;
+    }
+
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         /*
          * This method JUST determines whether we want to intercept the motion.
@@ -1117,6 +1140,13 @@
         return mIsBeingDragged;
     }
 
+    /**
+     * @return Whether the specified motion event is actually happening over the content.
+     */
+    private boolean isInContentBounds(MotionEvent event) {
+        return event.getY() < getHeight() - getEmptyBottomMargin();
+    }
+
     private void setIsBeingDragged(boolean isDragged) {
         mIsBeingDragged = isDragged;
         if (isDragged) {
@@ -1174,14 +1204,12 @@
 
     @Override
     public void onHeightChanged(ExpandableView view) {
-        if (mListenForHeightChanges && !isCurrentlyAnimating()) {
-            updateContentHeight();
-            updateScrollPositionIfNecessary();
-            if (mOnHeightChangedListener != null) {
-                mOnHeightChangedListener.onHeightChanged(view);
-            }
-            requestChildrenUpdate();
+        updateContentHeight();
+        updateScrollPositionIfNecessary();
+        if (mOnHeightChangedListener != null) {
+            mOnHeightChangedListener.onHeightChanged(view);
         }
+        requestChildrenUpdate();
     }
 
     public void setOnHeightChangedListener(
@@ -1194,10 +1222,34 @@
         mAnimationEvents.clear();
     }
 
+    /**
+     * See {@link AmbientState#setDimmed}.
+     */
+    public void setDimmed(boolean dimmed, boolean animate) {
+        mAmbientState.setDimmed(dimmed);
+        if (animate) {
+            mDimmedNeedsAnimation = true;
+            mNeedsAnimation =  true;
+        }
+        requestChildrenUpdate();
+    }
+
+    /**
+     * See {@link AmbientState#setActivatedChild}.
+     */
+    public void setActivatedChild(View activatedChild) {
+        mAmbientState.setActivatedChild(activatedChild);
+        mActivateNeedsAnimation = true;
+        mNeedsAnimation =  true;
+        requestChildrenUpdate();
+    }
+
+    public View getActivatedChild() {
+        return mAmbientState.getActivatedChild();
+    }
+
     private void applyCurrentState() {
-        mListenForHeightChanges = false;
         mCurrentStackScrollState.apply();
-        mListenForHeightChanges = true;
         if (mListener != null) {
             mListener.onChildLocationsChanged(this);
         }
@@ -1212,21 +1264,120 @@
 
     static class AnimationEvent {
 
-        static int ANIMATION_TYPE_ADD = 1;
-        static int ANIMATION_TYPE_REMOVE = 2;
-        static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3;
-        static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 4;
-        static int ANIMATION_TYPE_START_DRAG = 5;
-        static int ANIMATION_TYPE_SNAP_BACK = 6;
+        static AnimationFilter[] FILTERS = new AnimationFilter[] {
+
+                // ANIMATION_TYPE_ADD
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_REMOVE
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateY()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_TOP_PADDING_CHANGED
+                new AnimationFilter()
+                        .animateAlpha()
+                        .animateHeight()
+                        .animateY()
+                        .animateDimmed()
+                        .animateScale()
+                        .animateZ(),
+
+                // ANIMATION_TYPE_START_DRAG
+                new AnimationFilter()
+                        .animateAlpha(),
+
+                // ANIMATION_TYPE_SNAP_BACK
+                new AnimationFilter()
+                        .animateAlpha(),
+
+                // ANIMATION_TYPE_ACTIVATED_CHILD
+                new AnimationFilter()
+                        .animateScale()
+                        .animateAlpha(),
+
+                // ANIMATION_TYPE_DIMMED
+                new AnimationFilter()
+                        .animateScale()
+                        .animateDimmed()
+        };
+
+        static int[] LENGTHS = new int[] {
+
+                // ANIMATION_TYPE_ADD
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_REMOVE
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_TOP_PADDING_CHANGED
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_START_DRAG
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_SNAP_BACK
+                StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+                // ANIMATION_TYPE_ACTIVATED_CHILD
+                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+                // ANIMATION_TYPE_DIMMED
+                StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+        };
+
+        static int ANIMATION_TYPE_ADD = 0;
+        static int ANIMATION_TYPE_REMOVE = 1;
+        static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
+        static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
+        static int ANIMATION_TYPE_START_DRAG = 4;
+        static int ANIMATION_TYPE_SNAP_BACK = 5;
+        static int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
+        static int ANIMATION_TYPE_DIMMED = 7;
 
         final long eventStartTime;
         final View changingView;
         final int animationType;
+        final AnimationFilter filter;
+        final long length;
 
         AnimationEvent(View view, int type) {
             eventStartTime = AnimationUtils.currentAnimationTimeMillis();
             changingView = view;
             animationType = type;
+            filter = FILTERS[type];
+            length = LENGTHS[type];
+        }
+
+        /**
+         * Combines the length of several animation events into a single value.
+         *
+         * @param events The events of the lengths to combine.
+         * @return The combined length. This is just the maximum of all length.
+         */
+        static long combineLength(ArrayList<AnimationEvent> events) {
+            long length = 0;
+            int size = events.size();
+            for (int i = 0; i < size; i++) {
+                length = Math.max(length, events.get(i).length);
+            }
+            return length;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f7818c0..5e4d496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -39,6 +39,10 @@
     private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
     private static final int MAX_ITEMS_IN_TOP_STACK = 3;
 
+    /** When a child is activated, the other cards' alpha fade to this value. */
+    private static final float ACTIVATED_INVERSE_ALPHA = 0.9f;
+    private static final float DIMMED_SCALE = 0.95f;
+
     private int mPaddingBetweenElements;
     private int mCollapsedSize;
     private int mTopStackPeekSize;
@@ -61,7 +65,6 @@
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
     private int mTopStackTotalSize;
-    private ArrayList<View> mDraggedViews = new ArrayList<View>();
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
@@ -93,7 +96,7 @@
     }
 
 
-    public void getStackScrollState(StackScrollState resultState) {
+    public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
         // The state of the local variables are saved in an algorithmState to easily subdivide it
         // into multiple phases.
         StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
@@ -107,7 +110,7 @@
         algorithmState.scrolledPixelsTop = 0;
         algorithmState.itemsInBottomStack = 0.0f;
         algorithmState.partialInBottom = 0.0f;
-        algorithmState.scrollY = resultState.getScrollY() + mCollapsedSize;
+        algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
 
         updateVisibleChildren(resultState, algorithmState);
 
@@ -120,19 +123,42 @@
         // Phase 3:
         updateZValuesForState(resultState, algorithmState);
 
-        handleDraggedViews(resultState, algorithmState);
+        handleDraggedViews(ambientState, resultState, algorithmState);
+        updateDimmedActivated(ambientState, resultState, algorithmState);
+    }
+
+    /**
+     * Updates the dimmed and activated states of the children.
+     */
+    private void updateDimmedActivated(AmbientState ambientState, StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        boolean dimmed = ambientState.isDimmed();
+        View activatedChild = ambientState.getActivatedChild();
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
+            childViewState.dimmed = dimmed;
+            childViewState.scale = !dimmed || activatedChild == child
+                    ? 1.0f
+                    : DIMMED_SCALE;
+            if (dimmed && activatedChild != null && child != activatedChild) {
+                childViewState.alpha *= ACTIVATED_INVERSE_ALPHA;
+            }
+        }
     }
 
     /**
      * Handle the special state when views are being dragged
      */
-    private void handleDraggedViews(StackScrollState resultState,
+    private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
-        for (View draggedView : mDraggedViews) {
+        ArrayList<View> draggedViews = ambientState.getDraggedViews();
+        for (View draggedView : draggedViews) {
             int childIndex = algorithmState.visibleChildren.indexOf(draggedView);
             if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) {
                 View nextChild = algorithmState.visibleChildren.get(childIndex + 1);
-                if (!mDraggedViews.contains(nextChild)) {
+                if (!draggedViews.contains(nextChild)) {
                     // only if the view is not dragged itself we modify its state to be fully
                     // visible
                     StackScrollState.ViewState viewState = resultState.getViewStateForView(
@@ -595,14 +621,6 @@
         }
     }
 
-    public void onBeginDrag(View view) {
-        mDraggedViews.add(view);
-    }
-
-    public void onDragFinished(View view) {
-        mDraggedViews.remove(view);
-    }
-
     class StackScrollAlgorithmState {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 70126f5..8fc26d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -37,19 +37,10 @@
 
     private final ViewGroup mHostView;
     private Map<ExpandableView, ViewState> mStateMap;
-    private int mScrollY;
     private final Rect mClipRect = new Rect();
     private int mBackgroundRoundedRectCornerRadius;
     private final Outline mChildOutline = new Outline();
 
-    public int getScrollY() {
-        return mScrollY;
-    }
-
-    public void setScrollY(int scrollY) {
-        this.mScrollY = scrollY;
-    }
-
     public StackScrollState(ViewGroup hostView) {
         mHostView = hostView;
         mStateMap = new HashMap<ExpandableView, ViewState>();
@@ -106,10 +97,12 @@
                 float alpha = child.getAlpha();
                 float yTranslation = child.getTranslationY();
                 float zTranslation = child.getTranslationZ();
+                float scale = child.getScaleX();
                 int height = child.getActualHeight();
                 float newAlpha = state.alpha;
                 float newYTranslation = state.yTranslation;
                 float newZTranslation = state.zTranslation;
+                float newScale = state.scale;
                 int newHeight = state.height;
                 boolean becomesInvisible = newAlpha == 0.0f;
                 if (alpha != newAlpha) {
@@ -147,11 +140,20 @@
                     child.setTranslationZ(newZTranslation);
                 }
 
+                // apply scale
+                if (scale != newScale) {
+                    child.setScaleX(newScale);
+                    child.setScaleY(newScale);
+                }
+
                 // apply height
                 if (height != newHeight) {
-                    child.setActualHeight(newHeight);
+                    child.setActualHeight(newHeight, false /* notifyListeners */);
                 }
 
+                // apply dimming
+                child.setDimmed(state.dimmed, false /* animate */);
+
                 // apply clipping and shadow
                 float newNotificationEnd = newYTranslation + newHeight;
 
@@ -228,6 +230,8 @@
         float zTranslation;
         int height;
         boolean gone;
+        float scale;
+        boolean dimmed;
 
         /**
          * The location this view is currently rendered at.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 2e700aa..ca383aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.view.View;
 import android.view.animation.AnimationUtils;
@@ -37,17 +39,27 @@
  */
 public class StackStateAnimator {
 
-    private static final int ANIMATION_DURATION = 360;
+    public static final int ANIMATION_DURATION_STANDARD = 360;
+    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
+    private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
     private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
     private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
     private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
     private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
     private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
+    private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
     private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
     private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
     private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
+    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
+    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
+    private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag;
+    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
+    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
+    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
 
     private final Interpolator mFastOutSlowInInterpolator;
     public NotificationStackScrollLayout mHostLayout;
@@ -58,6 +70,8 @@
     private Set<Animator> mAnimatorSet = new HashSet<Animator>();
     private Stack<AnimatorListenerAdapter> mAnimationListenerPool
             = new Stack<AnimatorListenerAdapter>();
+    private AnimationFilter mAnimationFilter = new AnimationFilter();
+    private long mCurrentLength;
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
@@ -75,8 +89,9 @@
 
         processAnimationEvents(mAnimationEvents, finalState);
 
-        boolean hasNewEvents = !mNewEvents.isEmpty();
         int childCount = mHostLayout.getChildCount();
+        mAnimationFilter.applyCombination(mNewEvents);
+        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
             StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
@@ -84,7 +99,7 @@
                 continue;
             }
 
-            startAnimations(child, viewState, hasNewEvents);
+            startAnimations(child, viewState);
 
             child.setClipBounds(null);
         }
@@ -97,8 +112,7 @@
     /**
      * Start an animation to the given viewState
      */
-    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
-            boolean hasNewEvents) {
+    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
         int childVisibility = child.getVisibility();
         boolean wasVisible = childVisibility == View.VISIBLE;
         final float alpha = viewState.alpha;
@@ -107,47 +121,67 @@
         }
         // start translationY animation
         if (child.getTranslationY() != viewState.yTranslation) {
-            startYTranslationAnimation(child, viewState, hasNewEvents);
+            startYTranslationAnimation(child, viewState);
         }
         // start translationZ animation
         if (child.getTranslationZ() != viewState.zTranslation) {
-            startZTranslationAnimation(child, viewState, hasNewEvents);
+            startZTranslationAnimation(child, viewState);
+        }
+        // start scale animation
+        if (child.getScaleX() != viewState.scale) {
+            startScaleAnimation(child, viewState);
         }
         // start alpha animation
         if (alpha != child.getAlpha()) {
-            startAlphaAnimation(child, viewState, hasNewEvents);
+            startAlphaAnimation(child, viewState);
         }
         // start height animation
         if (viewState.height != child.getActualHeight()) {
-            startHeightAnimation(child, viewState, hasNewEvents);
+            startHeightAnimation(child, viewState);
         }
+        // start dimmed animation
+        child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
     }
 
     private void startHeightAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState, boolean hasNewEvents) {
-        Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT);
-        if (previousEndValue != null && previousEndValue == viewState.height) {
+            StackScrollState.ViewState viewState) {
+        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
+        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
+        int newEndValue = viewState.height;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
             return;
         }
         ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
-        if (newDuration <= 0) {
-            // no new animation needed, let's just apply the value
-            child.setActualHeight(viewState.height);
-            if (previousAnimator != null && !isRunning()) {
-                onAnimationFinished();
+        if (!mAnimationFilter.animateHeight) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_HEIGHT, newStartValue);
+                child.setTag(TAG_END_HEIGHT, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setActualHeight(newEndValue, false);
+                return;
             }
-            return;
         }
 
-        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), viewState.height);
+        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
-                child.setActualHeight((int) animation.getAnimatedValue());
+                child.setActualHeight((int) animation.getAnimatedValue(),
+                        false /* notifyListeners */);
             }
         });
         animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
         animator.setDuration(newDuration);
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
@@ -155,37 +189,49 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setTag(TAG_ANIMATOR_HEIGHT, null);
+                child.setTag(TAG_START_HEIGHT, null);
                 child.setTag(TAG_END_HEIGHT, null);
             }
         });
         startInstantly(animator);
         child.setTag(TAG_ANIMATOR_HEIGHT, animator);
-        child.setTag(TAG_END_HEIGHT, viewState.height);
+        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
+        child.setTag(TAG_END_HEIGHT, newEndValue);
     }
 
     private void startAlphaAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
-        final float endAlpha = viewState.alpha;
+            final StackScrollState.ViewState viewState) {
+        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
         Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
-        if (previousEndValue != null && previousEndValue == endAlpha) {
+        final float newEndValue = viewState.alpha;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
             return;
         }
         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
-        if (newDuration <= 0) {
-            // no new animation needed, let's just apply the value
-            child.setAlpha(endAlpha);
-            if (endAlpha == 0) {
-                child.setVisibility(View.INVISIBLE);
+        if (!mAnimationFilter.animateAlpha) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_ALPHA, newStartValue);
+                child.setTag(TAG_END_ALPHA, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setAlpha(newEndValue);
+                if (newEndValue == 0) {
+                    child.setVisibility(View.INVISIBLE);
+                }
             }
-            if (previousAnimator != null && !isRunning()) {
-                onAnimationFinished();
-            }
-            return;
         }
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
-                child.getAlpha(), endAlpha);
+                child.getAlpha(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
         // Handle layer type
         final int currentLayerType = child.getLayerType();
@@ -196,10 +242,11 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setLayerType(currentLayerType, null);
-                if (endAlpha == 0 && !mWasCancelled) {
+                if (newEndValue == 0 && !mWasCancelled) {
                     child.setVisibility(View.INVISIBLE);
                 }
                 child.setTag(TAG_ANIMATOR_ALPHA, null);
+                child.setTag(TAG_START_ALPHA, null);
                 child.setTag(TAG_END_ALPHA, null);
             }
 
@@ -213,6 +260,7 @@
                 mWasCancelled = false;
             }
         });
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
         animator.setDuration(newDuration);
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
@@ -224,30 +272,42 @@
         });
         startInstantly(animator);
         child.setTag(TAG_ANIMATOR_ALPHA, animator);
-        child.setTag(TAG_END_ALPHA, endAlpha);
+        child.setTag(TAG_START_ALPHA, child.getAlpha());
+        child.setTag(TAG_END_ALPHA, newEndValue);
     }
 
     private void startZTranslationAnimation(final ExpandableView child,
-            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
+            final StackScrollState.ViewState viewState) {
+        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
-        if (previousEndValue != null && previousEndValue == viewState.zTranslation) {
+        float newEndValue = viewState.zTranslation;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
             return;
         }
         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
-        if (newDuration <= 0) {
-            // no new animation needed, let's just apply the value
-            child.setTranslationZ(viewState.zTranslation);
-
-            if (previousAnimator != null && !isRunning()) {
-                onAnimationFinished();
+        if (!mAnimationFilter.animateZ) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
+                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setTranslationZ(newEndValue);
             }
-            return;
         }
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
-                child.getTranslationZ(), viewState.zTranslation);
+                child.getTranslationZ(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
         animator.setDuration(newDuration);
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
@@ -255,34 +315,49 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
+                child.setTag(TAG_START_TRANSLATION_Z, null);
                 child.setTag(TAG_END_TRANSLATION_Z, null);
             }
         });
         startInstantly(animator);
         child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
-        child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation);
+        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
+        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
     }
 
     private void startYTranslationAnimation(final ExpandableView child,
-            StackScrollState.ViewState viewState, boolean hasNewEvents) {
+            StackScrollState.ViewState viewState) {
+        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
-        if (previousEndValue != null && previousEndValue == viewState.yTranslation) {
+        float newEndValue = viewState.yTranslation;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
             return;
         }
         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
-        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
-        if (newDuration <= 0) {
-            // no new animation needed, let's just apply the value
-            child.setTranslationY(viewState.yTranslation);
-            if (previousAnimator != null && !isRunning()) {
-                onAnimationFinished();
+        if (!mAnimationFilter.animateY) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
+                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setTranslationY(newEndValue);
+                return;
             }
-            return;
         }
 
         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
-                child.getTranslationY(), viewState.yTranslation);
+                child.getTranslationY(), newEndValue);
         animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
         animator.setDuration(newDuration);
         animator.addListener(getGlobalAnimationFinishedListener());
         // remove the tag when the animation is finished
@@ -290,12 +365,68 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
+                child.setTag(TAG_START_TRANSLATION_Y, null);
                 child.setTag(TAG_END_TRANSLATION_Y, null);
             }
         });
         startInstantly(animator);
         child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
-        child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation);
+        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
+        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
+    }
+
+    private void startScaleAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState) {
+        Float previousStartValue = getChildTag(child, TAG_START_SCALE);
+        Float previousEndValue = getChildTag(child, TAG_END_SCALE);
+        float newEndValue = viewState.scale;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
+        if (!mAnimationFilter.animateScale) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                float relativeDiff = newEndValue - previousEndValue;
+                float newStartValue = previousStartValue + relativeDiff;
+                values[0].setFloatValues(newStartValue, newEndValue);
+                values[1].setFloatValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_SCALE, newStartValue);
+                child.setTag(TAG_END_SCALE, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setScaleX(newEndValue);
+                child.setScaleY(newEndValue);
+            }
+        }
+
+        PropertyValuesHolder holderX =
+                PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue);
+        PropertyValuesHolder holderY =
+                PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        animator.setDuration(newDuration);
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_SCALE, null);
+                child.setTag(TAG_START_SCALE, null);
+                child.setTag(TAG_END_SCALE, null);
+            }
+        });
+        startInstantly(animator);
+        child.setTag(TAG_ANIMATOR_SCALE, animator);
+        child.setTag(TAG_START_SCALE, child.getScaleX());
+        child.setTag(TAG_END_SCALE, newEndValue);
     }
 
     /**
@@ -349,22 +480,16 @@
      * Cancel the previous animator and get the duration of the new animation.
      *
      * @param previousAnimator the animator which was running before
-     * @param hasNewEvents indicating whether new events came in in this animation
      * @return the new duration
      */
-    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator,
-            boolean hasNewEvents) {
-        long newDuration = ANIMATION_DURATION;
+    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) {
+        long newDuration = mCurrentLength;
         if (previousAnimator != null) {
-            if (!hasNewEvents) {
-                // This is only an update, no new event came in. lets just take the remaining
-                // duration as the new duration
-                newDuration = previousAnimator.getDuration()
-                        - previousAnimator.getCurrentPlayTime();
-            }
+            // We take either the desired length of the new animation or the remaining time of
+            // the previous animator, whichever is longer.
+            newDuration = Math.max(previousAnimator.getDuration()
+                    - previousAnimator.getCurrentPlayTime(), newDuration);
             previousAnimator.cancel();
-        } else if (!hasNewEvents){
-            newDuration = 0;
         }
         return newDuration;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 4b3d3b0..9006c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -121,11 +121,6 @@
     }
 
     @Override
-    protected int getExpandedViewMaxHeight() {
-        return 0;
-    }
-
-    @Override
     protected boolean shouldDisableNavbarGestures() {
         return true;
     }
@@ -155,4 +150,11 @@
     protected void refreshLayout(int layoutDirection) {
     }
 
+    @Override
+    public void onActivated(View view) {
+    }
+
+    @Override
+    public void onActivationReset(View view) {
+    }
 }
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index dce4f581..a62d1fd 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -63,6 +63,25 @@
     static Method registerNativeAllocation;
     static Method registerNativeFree;
 
+    /*
+     * Context creation flag which specifies a normal context.
+    */
+    public static final long CREATE_FLAG_NONE = 0x0000;
+
+    /*
+     * Context creation flag which specifies a context optimized for low
+     * latency over peak performance. This is a hint and may have no effect
+     * on some implementations.
+    */
+    public static final long CREATE_FLAG_LOW_LATENCY = 0x0001;
+
+    /*
+     * Context creation flag which specifies a context optimized for long
+     * battery life over peak performance. This is a hint and may have no effect
+     * on some implementations.
+    */
+    public static final long CREATE_FLAG_LOW_POWER = 0x0002;
+
     static {
         sInitialized = false;
         if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
@@ -1145,7 +1164,7 @@
      * @hide
      */
     public static RenderScript create(Context ctx, int sdkVersion) {
-        return create(ctx, sdkVersion, ContextType.NORMAL);
+        return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
     }
 
     /**
@@ -1155,7 +1174,7 @@
      * @param ctx The context.
      * @return RenderScript
      */
-    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) {
+    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, long flags) {
         if (!sInitialized) {
             Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
             return null;
@@ -1194,7 +1213,21 @@
      */
     public static RenderScript create(Context ctx, ContextType ct) {
         int v = ctx.getApplicationInfo().targetSdkVersion;
-        return create(ctx, v, ct);
+        return create(ctx, v, ct, CREATE_FLAG_NONE);
+    }
+
+     /**
+     * Create a RenderScript context.
+     *
+     *
+     * @param ctx The context.
+     * @param ct The type of context to be created.
+     * @param flags The OR of the CREATE_FLAG_* options desired
+     * @return RenderScript
+     */
+    public static RenderScript create(Context ctx, ContextType ct, long flags) {
+        int v = ctx.getApplicationInfo().targetSdkVersion;
+        return create(ctx, v, ct, flags);
     }
 
     /**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 45cdb65..ea03eb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -74,7 +74,7 @@
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyDataTracker;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.SamplingDataTracker;
 import android.net.Uri;
@@ -406,12 +406,12 @@
     private ArrayList mInetLog;
 
     // track the current default http proxy - tell the world if we get a new one (real change)
-    private ProxyProperties mDefaultProxy = null;
+    private ProxyInfo mDefaultProxy = null;
     private Object mProxyLock = new Object();
     private boolean mDefaultProxyDisabled = false;
 
     // track the global proxy.
-    private ProxyProperties mGlobalProxy = null;
+    private ProxyInfo mGlobalProxy = null;
 
     private PacManager mPacManager = null;
 
@@ -3192,7 +3192,7 @@
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
-                    handleApplyDefaultProxy((ProxyProperties)msg.obj);
+                    handleApplyDefaultProxy((ProxyInfo)msg.obj);
                     break;
                 }
             }
@@ -3410,19 +3410,19 @@
         return;
     }
 
-    public ProxyProperties getProxy() {
+    public ProxyInfo getProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
         // of proxy info to all the JVMs.
         // enforceAccessPermission();
         synchronized (mProxyLock) {
-            ProxyProperties ret = mGlobalProxy;
+            ProxyInfo ret = mGlobalProxy;
             if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
             return ret;
         }
     }
 
-    public void setGlobalProxy(ProxyProperties proxyProperties) {
+    public void setGlobalProxy(ProxyInfo proxyProperties) {
         enforceConnectivityInternalPermission();
 
         synchronized (mProxyLock) {
@@ -3435,18 +3435,18 @@
             String exclList = "";
             String pacFileUrl = "";
             if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
-                    !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
+                    (proxyProperties.getPacFileUrl() != null))) {
                 if (!proxyProperties.isValid()) {
                     if (DBG)
                         log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
                     return;
                 }
-                mGlobalProxy = new ProxyProperties(proxyProperties);
+                mGlobalProxy = new ProxyInfo(proxyProperties);
                 host = mGlobalProxy.getHost();
                 port = mGlobalProxy.getPort();
-                exclList = mGlobalProxy.getExclusionList();
+                exclList = mGlobalProxy.getExclusionListAsString();
                 if (proxyProperties.getPacFileUrl() != null) {
-                    pacFileUrl = proxyProperties.getPacFileUrl();
+                    pacFileUrl = proxyProperties.getPacFileUrl().toString();
                 }
             } else {
                 mGlobalProxy = null;
@@ -3478,11 +3478,11 @@
                 Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
         String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
         if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
-            ProxyProperties proxyProperties;
+            ProxyInfo proxyProperties;
             if (!TextUtils.isEmpty(pacFileUrl)) {
-                proxyProperties = new ProxyProperties(pacFileUrl);
+                proxyProperties = new ProxyInfo(pacFileUrl);
             } else {
-                proxyProperties = new ProxyProperties(host, port, exclList);
+                proxyProperties = new ProxyInfo(host, port, exclList);
             }
             if (!proxyProperties.isValid()) {
                 if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
@@ -3495,7 +3495,7 @@
         }
     }
 
-    public ProxyProperties getGlobalProxy() {
+    public ProxyInfo getGlobalProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
         // of proxy info to all the JVMs.
@@ -3505,9 +3505,9 @@
         }
     }
 
-    private void handleApplyDefaultProxy(ProxyProperties proxy) {
+    private void handleApplyDefaultProxy(ProxyInfo proxy) {
         if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && TextUtils.isEmpty(proxy.getPacFileUrl())) {
+                && (proxy.getPacFileUrl() == null)) {
             proxy = null;
         }
         synchronized (mProxyLock) {
@@ -3544,13 +3544,13 @@
                     return;
                 }
             }
-            ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
+            ProxyInfo p = new ProxyInfo(data[0], proxyPort, "");
             setGlobalProxy(p);
         }
     }
 
-    private void sendProxyBroadcast(ProxyProperties proxy) {
-        if (proxy == null) proxy = new ProxyProperties("", 0, "");
+    private void sendProxyBroadcast(ProxyInfo proxy) {
+        if (proxy == null) proxy = new ProxyInfo("", 0, "");
         if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
         if (DBG) log("sending Proxy Broadcast for " + proxy);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 7ec9b82..d5f045e 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -201,8 +201,8 @@
     public static final String[] CRYPTO_TYPES
         = { "password", "default", "pattern", "pin" };
 
-    private Context mContext;
-    private NativeDaemonConnector mConnector;
+    private final Context mContext;
+    private final NativeDaemonConnector mConnector;
 
     private final Object mVolumesLock = new Object();
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cbb8377..29c781b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -136,7 +136,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Proxy;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -983,7 +983,7 @@
 
     /**
      * This is set if we had to do a delayed dexopt of an app before launching
-     * it, to increasing the ANR timeouts in that case.
+     * it, to increase the ANR timeouts in that case.
      */
     boolean mDidDexOpt;
 
@@ -1323,7 +1323,7 @@
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
-                ProxyProperties proxy = (ProxyProperties)msg.obj;
+                ProxyInfo proxy = (ProxyInfo)msg.obj;
                 String host = "";
                 String port = "";
                 String exclList = "";
@@ -1331,8 +1331,8 @@
                 if (proxy != null) {
                     host = proxy.getHost();
                     port = Integer.toString(proxy.getPort());
-                    exclList = proxy.getExclusionList();
-                    pacFileUrl = proxy.getPacFileUrl();
+                    exclList = proxy.getExclusionListAsString();
+                    pacFileUrl = proxy.getPacFileUrl().toString();
                 }
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -13758,7 +13758,7 @@
         }
 
         if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
-            ProxyProperties proxy = intent.getParcelableExtra("proxy");
+            ProxyInfo proxy = intent.getParcelableExtra("proxy");
             mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
         }
 
@@ -16523,7 +16523,7 @@
                 }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
-                    if (userId != 0) {
+                    if (userId != UserHandle.USER_OWNER) {
                         Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
                         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                         broadcastIntentLocked(null, null, intent, null,
@@ -16552,6 +16552,8 @@
                     EventLogTags.writeAmSwitchUser(userId);
                     getUserManagerLocked().userForeground(userId);
                     sendUserSwitchBroadcastsLocked(oldUserId, userId);
+                } else {
+                    mStackSupervisor.startBackgroundUserLocked(userId, uss);
                 }
 
                 if (needStart) {
@@ -16727,7 +16729,7 @@
         }
     }
 
-    void finishUserSwitch(UserStartedState uss) {
+    void finishUserBoot(UserStartedState uss) {
         synchronized (this) {
             if (uss.mState == UserStartedState.STATE_BOOTING
                     && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
@@ -16741,6 +16743,12 @@
                         android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE,
                         true, false, MY_PID, Process.SYSTEM_UID, userId);
             }
+        }
+    }
+
+    void finishUserSwitch(UserStartedState uss) {
+        synchronized (this) {
+            finishUserBoot(uss);
 
             startProfilesLocked();
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7c3f288..efd2b57 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import android.os.Trace;
-import com.android.internal.R.styleable;
 import com.android.internal.app.ResolverActivity;
 import com.android.server.AttributeCache;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -480,7 +479,7 @@
     void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
         if (task != null && task.removeActivity(this)) {
             if (task != newTask) {
-                task.stack.removeTask(task, false);
+                task.stack.removeTask(task);
             } else {
                 Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" +
                         (newTask == null ? null : newTask.stack));
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ee39b67..a95710b 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,8 +36,6 @@
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
-import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
-
 import android.service.voice.IVoiceInteractionSession;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.BatteryStatsImpl;
@@ -2841,7 +2839,7 @@
             if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
                 mStackSupervisor.moveHomeToTop();
             }
-            removeTask(task, false);
+            removeTask(task);
         }
         cleanUpActivityServicesLocked(r);
         r.removeUriPermissionsLocked();
@@ -3717,7 +3715,7 @@
         return starting;
     }
 
-    void removeTask(TaskRecord task, boolean moving) {
+    void removeTask(TaskRecord task) {
         mStackSupervisor.endLockTaskModeIfTaskEnding(task);
         mWindowManager.removeTask(task.taskId);
         final ActivityRecord r = mResumedActivity;
@@ -3731,14 +3729,20 @@
             mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
         }
         mTaskHistory.remove(task);
-        if (!moving && task.voiceSession != null) {
-            // This task was a voice interaction, so it should not remain on the
-            // recent tasks list.
-            try {
-                task.voiceSession.taskFinished(task.intent, task.taskId);
-            } catch (RemoteException e) {
+
+        if (task.mActivities.isEmpty()) {
+            final boolean isVoiceSession = task.voiceSession != null;
+            if (isVoiceSession) {
+                try {
+                    task.voiceSession.taskFinished(task.intent, task.taskId);
+                } catch (RemoteException e) {
+                }
             }
-            mService.mRecentTasks.remove(task);
+            if (task.autoRemoveFromRecents() || isVoiceSession) {
+                // Task creator asked to remove this when done, or this task was a voice
+                // interaction, so it should not remain on the recent tasks list.
+                mService.mRecentTasks.remove(task);
+            }
         }
 
         if (mTaskHistory.isEmpty()) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2e979d2..00327ac 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -193,6 +193,9 @@
     /** Used on user changes */
     final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>();
 
+    /** Used to queue up any background users being started */
+    final ArrayList<UserStartedState> mStartingBackgroundUsers = new ArrayList<UserStartedState>();
+
     /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
      * is being brought in front of us. */
     boolean mUserLeaving = false;
@@ -1471,6 +1474,17 @@
             }
         }
 
+        switch (r.info.documentLaunchMode) {
+            case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+                break;
+            case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+                intent.addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+                break;
+            case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+                break;
+        }
         final boolean newDocument = intent.isDocument();
         if (sourceRecord == null) {
             // This activity is not being started from another...  in this
@@ -1988,9 +2002,20 @@
 
         if (booting) {
             mService.finishBooting();
-        } else if (startingUsers != null) {
-            for (int i = 0; i < startingUsers.size(); i++) {
-                mService.finishUserSwitch(startingUsers.get(i));
+        } else {
+            // Complete user switch
+            if (startingUsers != null) {
+                for (int i = 0; i < startingUsers.size(); i++) {
+                    mService.finishUserSwitch(startingUsers.get(i));
+                }
+            }
+            // Complete starting up of background users
+            if (mStartingBackgroundUsers.size() > 0) {
+                startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers);
+                mStartingBackgroundUsers.clear();
+                for (int i = 0; i < startingUsers.size(); i++) {
+                    mService.finishUserBoot(startingUsers.get(i));
+                }
             }
         }
 
@@ -2237,7 +2262,7 @@
             Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
             return;
         }
-        task.stack.removeTask(task, true);
+        task.stack.removeTask(task);
         stack.addTask(task, toTop, true);
         mWindowManager.addTask(taskId, stackId, toTop);
         resumeTopActivitiesLocked();
@@ -2496,6 +2521,15 @@
         return homeInFront;
     }
 
+    /**
+     * Add background users to send boot completed events to.
+     * @param userId The user being started in the background
+     * @param uss The state object for the user.
+     */
+    public void startBackgroundUserLocked(int userId, UserStartedState uss) {
+        mStartingBackgroundUsers.add(uss);
+    }
+
     final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
         int N = mStoppingActivities.size();
         if (N <= 0) return null;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9f0bc10..862932c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -139,6 +139,9 @@
 
         userId = UserHandle.getUserId(info.applicationInfo.uid);
         creatorUid = info.applicationInfo.uid;
+        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
+        }
     }
 
     void disposeThumbnail() {
@@ -246,6 +249,11 @@
         return mActivities.size() == 0;
     }
 
+    boolean autoRemoveFromRecents() {
+        return intent != null &&
+                (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
+    }
+
     /**
      * Completely remove all activities associated with an existing
      * task starting at a specified index.
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 8815d0f..0749f24 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -24,7 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -157,14 +157,14 @@
      * @param proxy Proxy information that is about to be broadcast.
      * @return Returns true when the broadcast should not be sent
      */
-    public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
-        if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
+    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+        if (proxy.getPacFileUrl() != null) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
                 return false;
             }
             synchronized (mProxyLock) {
-                mPacUrl = proxy.getPacFileUrl();
+                mPacUrl = proxy.getPacFileUrl().toString();
             }
             mCurrentDelay = DELAY_1;
             mHasSentBroadcast = false;
@@ -268,7 +268,7 @@
         // Already bound no need to bind again.
         if ((mProxyConnection != null) && (mConnection != null)) {
             if (mLastPort != -1) {
-                sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
+                sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
             } else {
                 Log.e(TAG, "Received invalid port from Local Proxy,"
                         + " PAC will not be operational");
@@ -362,7 +362,7 @@
         mLastPort = -1;
     }
 
-    private void sendPacBroadcast(ProxyProperties proxy) {
+    private void sendPacBroadcast(ProxyInfo proxy) {
         mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
     }
 
@@ -371,7 +371,7 @@
             return;
         }
         if (!mHasSentBroadcast) {
-            sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
+            sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
             mHasSentBroadcast = true;
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7aa5d79..fce86e8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,8 +198,7 @@
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
     private ConditionProviders mConditionProviders;
-
-    private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
+    private NotificationUsageStats mUsageStats;
 
     private static final String EXTRA_INTERCEPT = "android.intercept";
 
@@ -472,6 +471,7 @@
             pw.println(prefix + String.format("  defaults=0x%08x flags=0x%08x",
                     notification.defaults, notification.flags));
             pw.println(prefix + "  sound=" + notification.sound);
+            pw.println(prefix + String.format("  color=0x%08x", notification.color));
             pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
             pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
                     notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
@@ -858,6 +858,7 @@
         });
         final File systemDir = new File(Environment.getDataDirectory(), "system");
         mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
+        mUsageStats = new NotificationUsageStats(getContext());
 
         importOldBlockDb();
 
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d9e2b91..45ab3d3 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -18,8 +18,17 @@
 
 import com.android.server.notification.NotificationManagerService.NotificationRecord;
 
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
 import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -37,9 +46,13 @@
  * {@hide}
  */
 public class NotificationUsageStats {
-
     // Guarded by synchronized(this).
     private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
+    private final SQLiteLog mSQLiteLog;
+
+    public NotificationUsageStats(Context context) {
+        mSQLiteLog = new SQLiteLog(context);
+    }
 
     /**
      * Called when a notification has been posted.
@@ -49,6 +62,7 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numPostedByApp++;
         }
+        mSQLiteLog.logPosted(notification);
     }
 
     /**
@@ -68,6 +82,7 @@
             stats.numRemovedByApp++;
             stats.collect(notification.stats);
         }
+        mSQLiteLog.logRemoved(notification);
     }
 
     /**
@@ -79,6 +94,7 @@
             stats.numDismissedByUser++;
             stats.collect(notification.stats);
         }
+        mSQLiteLog.logDismissed(notification);
     }
 
     /**
@@ -89,6 +105,7 @@
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numClickedByUser++;
         }
+        mSQLiteLog.logClicked(notification);
     }
 
     /**
@@ -146,6 +163,7 @@
         for (AggregatedStats as : mStats.values()) {
             as.dump(pw, indent);
         }
+        mSQLiteLog.dump(pw, indent);
     }
 
     /**
@@ -274,4 +292,211 @@
                     '}';
         }
     }
+
+    private static class SQLiteLog {
+        private static final String TAG = "NotificationSQLiteLog";
+
+        // Message types passed to the background handler.
+        private static final int MSG_POST = 1;
+        private static final int MSG_CLICK = 2;
+        private static final int MSG_REMOVE = 3;
+        private static final int MSG_DISMISS = 4;
+
+        private static final String DB_NAME = "notification_log.db";
+        private static final int DB_VERSION = 1;
+
+        /** Age in ms after which events are pruned from the DB. */
+        private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L;  // 1 week
+        /** Delay between pruning the DB. Used to throttle pruning. */
+        private static final long PRUNE_MIN_DELAY_MS = 6 * 60 * 60 * 1000L;  // 6 hours
+        /** Mininum number of writes between pruning the DB. Used to throttle pruning. */
+        private static final long PRUNE_MIN_WRITES = 1024;
+
+        // Table 'log'
+        private static final String TAB_LOG = "log";
+        private static final String COL_EVENT_USER_ID = "event_user_id";
+        private static final String COL_EVENT_TYPE = "event_type";
+        private static final String COL_EVENT_TIME = "event_time_ms";
+        private static final String COL_KEY = "key";
+        private static final String COL_PKG = "pkg";
+        private static final String COL_NOTIFICATION_ID = "nid";
+        private static final String COL_TAG = "tag";
+        private static final String COL_WHEN_MS = "when_ms";
+        private static final String COL_DEFAULTS = "defaults";
+        private static final String COL_FLAGS = "flags";
+        private static final String COL_PRIORITY = "priority";
+        private static final String COL_CATEGORY = "category";
+        private static final String COL_ACTION_COUNT = "action_count";
+
+        private static final int EVENT_TYPE_POST = 1;
+        private static final int EVENT_TYPE_CLICK = 2;
+        private static final int EVENT_TYPE_REMOVE = 3;
+        private static final int EVENT_TYPE_DISMISS = 4;
+
+        private static long sLastPruneMs;
+        private static long sNumWrites;
+
+        private final SQLiteOpenHelper mHelper;
+        private final Handler mWriteHandler;
+
+        private static final long DAY_MS = 24 * 60 * 60 * 1000;
+
+        public SQLiteLog(Context context) {
+            HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
+                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
+            backgroundThread.start();
+            mWriteHandler = new Handler(backgroundThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    NotificationRecord r = (NotificationRecord) msg.obj;
+                    long nowMs = System.currentTimeMillis();
+                    switch (msg.what) {
+                        case MSG_POST:
+                            writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r, true);
+                            break;
+                        case MSG_CLICK:
+                            writeEvent(nowMs, EVENT_TYPE_CLICK, r, false);
+                            break;
+                        case MSG_REMOVE:
+                            writeEvent(nowMs, EVENT_TYPE_REMOVE, r, false);
+                            break;
+                        case MSG_DISMISS:
+                            writeEvent(nowMs, EVENT_TYPE_DISMISS, r, false);
+                            break;
+                        default:
+                            Log.wtf(TAG, "Unknown message type: " + msg.what);
+                            break;
+                    }
+                }
+            };
+            mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
+                @Override
+                public void onCreate(SQLiteDatabase db) {
+                    db.execSQL("CREATE TABLE " + TAB_LOG + " (" +
+                            "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+                            COL_EVENT_USER_ID + " INT," +
+                            COL_EVENT_TYPE + " INT," +
+                            COL_EVENT_TIME + " INT," +
+                            COL_KEY + " TEXT," +
+                            COL_PKG + " TEXT," +
+                            COL_NOTIFICATION_ID + " INT," +
+                            COL_TAG + " TEXT," +
+                            COL_WHEN_MS + " INT," +
+                            COL_DEFAULTS + " INT," +
+                            COL_FLAGS + " INT," +
+                            COL_PRIORITY + " INT," +
+                            COL_CATEGORY + " TEXT," +
+                            COL_ACTION_COUNT + " INT" +
+                            ")");
+                }
+
+                @Override
+                public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+                    db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
+                    onCreate(db);
+                }
+            };
+        }
+
+        public void logPosted(NotificationRecord notification) {
+            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_POST, notification));
+        }
+
+        public void logClicked(NotificationRecord notification) {
+            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_CLICK, notification));
+        }
+
+        public void logRemoved(NotificationRecord notification) {
+            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_REMOVE, notification));
+        }
+
+        public void logDismissed(NotificationRecord notification) {
+            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
+        }
+
+        public void printPostFrequencies(PrintWriter pw, String indent) {
+            SQLiteDatabase db = mHelper.getReadableDatabase();
+            long nowMs = System.currentTimeMillis();
+            String q = "SELECT " +
+                    COL_EVENT_USER_ID + ", " +
+                    COL_PKG + ", " +
+                    // Bucket by day by looking at 'floor((nowMs - eventTimeMs) / dayMs)'
+                    "CAST(((" + nowMs + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
+                        "AS day, " +
+                    "COUNT(*) AS cnt " +
+                    "FROM " + TAB_LOG + " " +
+                    "WHERE " +
+                    COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " +
+                    "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+            Cursor cursor = db.rawQuery(q, null);
+            try {
+                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+                    int userId = cursor.getInt(0);
+                    String pkg = cursor.getString(1);
+                    int day = cursor.getInt(2);
+                    int count = cursor.getInt(3);
+                    pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
+                            ",day=" + day + ",count=" + count + "}");
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+
+        private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r,
+                boolean populateNotificationDetails) {
+            ContentValues cv = new ContentValues();
+            cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier());
+            cv.put(COL_EVENT_TIME, eventTimeMs);
+            cv.put(COL_EVENT_TYPE, eventType);
+            putNotificationIdentifiers(r, cv);
+            if (populateNotificationDetails) {
+                putNotificationDetails(r, cv);
+            }
+            SQLiteDatabase db = mHelper.getWritableDatabase();
+            if (db.insert(TAB_LOG, null, cv) < 0) {
+                Log.wtf(TAG, "Error while trying to insert values: " + cv);
+            }
+            sNumWrites++;
+            pruneIfNecessary(db);
+        }
+
+        private void pruneIfNecessary(SQLiteDatabase db) {
+            // Prune if we haven't in a while.
+            long nowMs = System.currentTimeMillis();
+            if (sNumWrites > PRUNE_MIN_WRITES ||
+                    nowMs - sLastPruneMs > PRUNE_MIN_DELAY_MS) {
+                sNumWrites = 0;
+                sLastPruneMs = nowMs;
+                long horizonStartMs = nowMs - HORIZON_MS;
+                int deletedRows = db.delete(TAB_LOG, COL_EVENT_TIME + " < ?",
+                        new String[] { String.valueOf(horizonStartMs) });
+                Log.d(TAG, "Pruned event entries: " + deletedRows);
+            }
+        }
+
+        private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
+            outCv.put(COL_KEY, r.sbn.getKey());
+            outCv.put(COL_PKG, r.sbn.getPackageName());
+        }
+
+        private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
+            outCv.put(COL_NOTIFICATION_ID, r.sbn.getId());
+            if (r.sbn.getTag() != null) {
+                outCv.put(COL_TAG, r.sbn.getTag());
+            }
+            outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
+            outCv.put(COL_FLAGS, r.getNotification().flags);
+            outCv.put(COL_PRIORITY, r.getNotification().priority);
+            if (r.getNotification().category != null) {
+                outCv.put(COL_CATEGORY, r.getNotification().category);
+            }
+            outCv.put(COL_ACTION_COUNT, r.getNotification().actions != null ?
+                    r.getNotification().actions.length : 0);
+        }
+
+        public void dump(PrintWriter pw, String indent) {
+            printPostFrequencies(pw, indent);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
new file mode 100644
index 0000000..f2db791
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.server.pm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * {@hide}
+ */
+public class BackgroundDexOptService {
+
+    static final String TAG = "BackgroundDexOptService";
+
+    private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
+                onIdleStart();
+            } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
+                onIdleStop();
+            }
+        }
+    };
+
+    final PackageManagerService mPackageManager;
+
+    final AtomicBoolean mIdleTime = new AtomicBoolean(false);
+
+    public BackgroundDexOptService(Context context) {
+        mPackageManager = (PackageManagerService)ServiceManager.getService("package");
+
+        IntentFilter idleMaintenanceFilter = new IntentFilter();
+        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
+        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END);
+        context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
+                                       idleMaintenanceFilter, null, null);
+    }
+
+    public boolean onIdleStart() {
+        Log.i(TAG, "onIdleStart");
+        if (mPackageManager.isStorageLow()) {
+            return false;
+        }
+        final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt();
+        if (pkgs == null) {
+            return false;
+        }
+        mIdleTime.set(true);
+        new Thread("BackgroundDexOptService_DexOpter") {
+            @Override
+            public void run() {
+                for (String pkg : pkgs) {
+                    if (!mIdleTime.get()) {
+                        break;
+                    }
+                    mPackageManager.performDexOpt(pkg, false);
+                }
+            }
+        }.start();
+        return true;
+    }
+
+    public void onIdleStop() {
+        Log.i(TAG, "onIdleStop");
+        mIdleTime.set(false);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 48e9737..bd28e04 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -239,8 +239,10 @@
 
     private class MyPackageMonitor extends PackageMonitor {
 
-        /** Checks if user is a profile of or same as listeningUser. */
-        private boolean isProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) {
+        /** Checks if user is a profile of or same as listeningUser.
+          * and the user is enabled. */
+        private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
+                String debugMsg) {
             if (user.getIdentifier() == listeningUser.getIdentifier()) {
                 if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
                 return true;
@@ -251,7 +253,8 @@
                 UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
                 if (userInfo == null || listeningUserInfo == null
                         || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
-                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId) {
+                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId
+                        || !userInfo.isEnabled()) {
                     if (DEBUG) {
                         Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
                                 + debugMsg);
@@ -276,7 +279,7 @@
             for (int i = 0; i < n; i++) {
                 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isProfileOf(user, listeningUser, "onPackageAdded")) continue;
+                if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
                 try {
                     listener.onPackageAdded(user, packageName);
                 } catch (RemoteException re) {
@@ -295,7 +298,7 @@
             for (int i = 0; i < n; i++) {
                 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+                if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
                 try {
                     listener.onPackageRemoved(user, packageName);
                 } catch (RemoteException re) {
@@ -314,7 +317,7 @@
             for (int i = 0; i < n; i++) {
                 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isProfileOf(user, listeningUser, "onPackageModified")) continue;
+                if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
                 try {
                     listener.onPackageChanged(user, packageName);
                 } catch (RemoteException re) {
@@ -333,7 +336,7 @@
             for (int i = 0; i < n; i++) {
                 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
+                if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
                 try {
                     listener.onPackagesAvailable(user, packages, isReplacing());
                 } catch (RemoteException re) {
@@ -352,7 +355,7 @@
             for (int i = 0; i < n; i++) {
                 IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                 UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
-                if (!isProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
+                if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
                 try {
                     listener.onPackagesUnavailable(user, packages, isReplacing());
                 } catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 72fc295..c8b61f1 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23,17 +23,19 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.system.OsConstants.S_IRWXU;
+import static android.os.Process.PACKAGE_INFO_GID;
+import static android.os.Process.SYSTEM_UID;
 import static android.system.OsConstants.S_IRGRP;
-import static android.system.OsConstants.S_IXGRP;
 import static android.system.OsConstants.S_IROTH;
+import static android.system.OsConstants.S_IRWXU;
+import static android.system.OsConstants.S_IXGRP;
 import static android.system.OsConstants.S_IXOTH;
-import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
+import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.ArrayUtils.removeInt;
 
-import android.content.pm.PackageParser.*;
+import com.android.internal.R;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.NativeLibraryHelper;
@@ -43,10 +45,13 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
-import com.android.server.ServiceThread;
-
 import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
+import com.android.server.pm.Settings.DatabaseVersion;
+import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.storage.DeviceStorageMonitorInternal;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -82,6 +87,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
 import android.content.pm.PackageUserState;
@@ -125,6 +131,7 @@
 import android.system.Os;
 import android.system.StructStat;
 import android.text.TextUtils;
+import android.util.AtomicFile;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -135,6 +142,7 @@
 import android.util.Xml;
 import android.view.Display;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -144,7 +152,9 @@
 import java.io.FileReader;
 import java.io.FilenameFilter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.cert.Certificate;
@@ -163,14 +173,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
 
+import dalvik.system.DexFile;
+import dalvik.system.StaleDexCacheError;
 import dalvik.system.VMRuntime;
 import libcore.io.IoUtils;
 
-import com.android.internal.R;
-import com.android.server.pm.Settings.DatabaseVersion;
-import com.android.server.storage.DeviceStorageMonitorInternal;
-
 /**
  * Keep track of all those .apks everywhere.
  * 
@@ -197,6 +207,7 @@
     private static final boolean DEBUG_PACKAGE_SCANNING = false;
     private static final boolean DEBUG_APP_DIR_OBSERVER = false;
     private static final boolean DEBUG_VERIFY = false;
+    private static final boolean DEBUG_DEXOPT = false;
 
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
@@ -287,7 +298,6 @@
     final Context mContext;
     final boolean mFactoryTest;
     final boolean mOnlyCore;
-    final boolean mNoDexOpt;
     final DisplayMetrics mMetrics;
     final int mDefParseFlags;
     final String[] mSeparateProcesses;
@@ -593,6 +603,139 @@
 
     private final String mRequiredVerifierPackage;
 
+    private final PackageUsage mPackageUsage = new PackageUsage();
+
+    private class PackageUsage {
+        private static final int WRITE_INTERVAL
+            = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms
+
+        private final Object mFileLock = new Object();
+        private final AtomicLong mLastWritten = new AtomicLong(0);
+        private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
+
+        void write(boolean force) {
+            if (force) {
+                write();
+                return;
+            }
+            if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
+                && !DEBUG_DEXOPT) {
+                return;
+            }
+            if (mBackgroundWriteRunning.compareAndSet(false, true)) {
+                new Thread("PackageUsage_DiskWriter") {
+                    @Override
+                    public void run() {
+                        try {
+                            write(true);
+                        } finally {
+                            mBackgroundWriteRunning.set(false);
+                        }
+                    }
+                }.start();
+            }
+        }
+
+        private void write() {
+            synchronized (mPackages) {
+                synchronized (mFileLock) {
+                    AtomicFile file = getFile();
+                    FileOutputStream f = null;
+                    try {
+                        f = file.startWrite();
+                        BufferedOutputStream out = new BufferedOutputStream(f);
+                        FileUtils.setPermissions(file.getBaseFile().getPath(), 0660, SYSTEM_UID, PACKAGE_INFO_GID);
+                        StringBuilder sb = new StringBuilder();
+                        for (PackageParser.Package pkg : mPackages.values()) {
+                            if (pkg.mLastPackageUsageTimeInMills == 0) {
+                                continue;
+                            }
+                            sb.setLength(0);
+                            sb.append(pkg.packageName);
+                            sb.append(' ');
+                            sb.append((long)pkg.mLastPackageUsageTimeInMills);
+                            sb.append('\n');
+                            out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+                        }
+                        out.flush();
+                        file.finishWrite(f);
+                    } catch (IOException e) {
+                        if (f != null) {
+                            file.failWrite(f);
+                        }
+                        Log.e(TAG, "Failed to write package usage times", e);
+                    }
+                }
+            }
+            mLastWritten.set(SystemClock.elapsedRealtime());
+        }
+
+        void readLP() {
+            synchronized (mFileLock) {
+                AtomicFile file = getFile();
+                BufferedInputStream in = null;
+                try {
+                    in = new BufferedInputStream(file.openRead());
+                    StringBuffer sb = new StringBuffer();
+                    while (true) {
+                        String packageName = readToken(in, sb, ' ');
+                        if (packageName == null) {
+                            break;
+                        }
+                        String timeInMillisString = readToken(in, sb, '\n');
+                        if (timeInMillisString == null) {
+                            throw new IOException("Failed to find last usage time for package "
+                                                  + packageName);
+                        }
+                        PackageParser.Package pkg = mPackages.get(packageName);
+                        if (pkg == null) {
+                            continue;
+                        }
+                        long timeInMillis;
+                        try {
+                            timeInMillis = Long.parseLong(timeInMillisString.toString());
+                        } catch (NumberFormatException e) {
+                            throw new IOException("Failed to parse " + timeInMillisString
+                                                  + " as a long.", e);
+                        }
+                        pkg.mLastPackageUsageTimeInMills = timeInMillis;
+                    }
+                } catch (FileNotFoundException expected) {
+                } catch (IOException e) {
+                    Log.w(TAG, "Failed to read package usage times", e);
+                } finally {
+                    IoUtils.closeQuietly(in);
+                }
+            }
+            mLastWritten.set(SystemClock.elapsedRealtime());
+        }
+
+        private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+                throws IOException {
+            sb.setLength(0);
+            while (true) {
+                int ch = in.read();
+                if (ch == -1) {
+                    if (sb.length() == 0) {
+                        return null;
+                    }
+                    throw new IOException("Unexpected EOF");
+                }
+                if (ch == endOfToken) {
+                    return sb.toString();
+                }
+                sb.append((char)ch);
+            }
+        }
+
+        private AtomicFile getFile() {
+            File dataDir = Environment.getDataDirectory();
+            File systemDir = new File(dataDir, "system");
+            File fname = new File(systemDir, "package-usage.list");
+            return new AtomicFile(fname);
+        }
+    }
+
     class PackageHandler extends Handler {
         private boolean mBound = false;
         final ArrayList<HandlerParams> mPendingInstalls =
@@ -1137,7 +1280,6 @@
         mContext = context;
         mFactoryTest = factoryTest;
         mOnlyCore = onlyCore;
-        mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
         mMetrics = new DisplayMetrics();
         mSettings = new Settings(context);
         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
@@ -1223,10 +1365,6 @@
             // Set flag to monitor and not change apk file paths when
             // scanning install directories.
             int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
-            if (mNoDexOpt) {
-                Slog.w(TAG, "Running ENG build: no pre-dexopt!");
-                scanMode |= SCAN_NO_DEX;
-            }
 
             final HashSet<String> alreadyDexOpted = new HashSet<String>();
 
@@ -1245,7 +1383,7 @@
                 Slog.w(TAG, "No BOOTCLASSPATH found!");
             }
 
-            boolean didDexOpt = false;
+            boolean didDexOptLibraryOrTool = false;
 
             final List<String> instructionSets = getAllInstructionSets();
 
@@ -1265,13 +1403,12 @@
                         }
 
                         try {
-                            if (dalvik.system.DexFile.isDexOptNeededInternal(
-                                    lib, null, instructionSet, false)) {
+                            if (DexFile.isDexOptNeededInternal(lib, null, instructionSet, false)) {
                                 alreadyDexOpted.add(lib);
 
                                 // The list of "shared libraries" we have at this point is
                                 mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet);
-                                didDexOpt = true;
+                                didDexOptLibraryOrTool = true;
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
@@ -1317,9 +1454,9 @@
                             continue;
                         }
                         try {
-                            if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) {
+                            if (DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) {
                                 mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet);
-                                didDexOpt = true;
+                                didDexOptLibraryOrTool = true;
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Jar not found: " + path);
@@ -1330,7 +1467,7 @@
                 }
             }
 
-            if (didDexOpt) {
+            if (didDexOptLibraryOrTool) {
                 pruneDexFiles(new File(dataDir, "dalvik-cache"));
             }
 
@@ -1509,12 +1646,15 @@
             // the correct library paths.
             updateAllSharedLibrariesLPw();
 
-
             for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                 adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */,
                         false /* force dexopt */, false /* defer dexopt */);
             }
 
+            // Now that we know all the packages we are keeping,
+            // read and update their last usage times.
+            mPackageUsage.readLP();
+
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                     SystemClock.uptimeMillis());
             Slog.i(TAG, "Time to scan packages: "
@@ -1573,6 +1713,14 @@
         //
         // Additionally, delete all dex files from the root directory
         // since there shouldn't be any there anyway.
+        //
+        // Note: This isn't as good an indicator as it used to be. It
+        // used to include the boot classpath but at some point
+        // DexFile.isDexOptNeeded started returning false for the boot
+        // class path files in all cases. It is very possible in a
+        // small maintenance release update that the library and tool
+        // jars may be unchanged but APK could be removed resulting in
+        // unused dalvik-cache files.
         File[] files = cacheDir.listFiles();
         if (files != null) {
             for (File file : files) {
@@ -1595,10 +1743,12 @@
         }
     }
 
+    @Override
     public boolean isFirstBoot() {
         return !mRestoredSettings;
     }
 
+    @Override
     public boolean isOnlyCoreApps() {
         return mOnlyCore;
     }
@@ -1895,6 +2045,7 @@
                 state, userId);
     }
 
+    @Override
     public boolean isPackageAvailable(String packageName, int userId) {
         if (!sUserManager.exists(userId)) return false;
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "is package available");
@@ -1932,6 +2083,7 @@
         return null;
     }
 
+    @Override
     public String[] currentToCanonicalPackageNames(String[] names) {
         String[] out = new String[names.length];
         // reader
@@ -1944,6 +2096,7 @@
         return out;
     }
     
+    @Override
     public String[] canonicalToCurrentPackageNames(String[] names) {
         String[] out = new String[names.length];
         // reader
@@ -2004,6 +2157,7 @@
         return pi;
     }
     
+    @Override
     public PermissionInfo getPermissionInfo(String name, int flags) {
         // reader
         synchronized (mPackages) {
@@ -2015,6 +2169,7 @@
         }
     }
 
+    @Override
     public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
         // reader
         synchronized (mPackages) {
@@ -2038,6 +2193,7 @@
         }
     }
 
+    @Override
     public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
         // reader
         synchronized (mPackages) {
@@ -2046,6 +2202,7 @@
         }
     }
 
+    @Override
     public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
         // reader
         synchronized (mPackages) {
@@ -2131,6 +2288,7 @@
     }
 
 
+    @Override
     public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CLEAR_APP_CACHE, null);
@@ -2156,6 +2314,7 @@
         });
     }
 
+    @Override
     public void freeStorage(final long freeStorageSize, final IntentSender pi) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CLEAR_APP_CACHE, null);
@@ -2277,6 +2436,7 @@
         return null;
     }
 
+    @Override
     public String[] getSystemSharedLibraryNames() {
         Set<String> libSet;
         synchronized (mPackages) {
@@ -2291,6 +2451,7 @@
         return null;
     }
 
+    @Override
     public FeatureInfo[] getSystemAvailableFeatures() {
         Collection<FeatureInfo> featSet;
         synchronized (mPackages) {
@@ -2309,6 +2470,7 @@
         return null;
     }
 
+    @Override
     public boolean hasSystemFeature(String name) {
         synchronized (mPackages) {
             return mAvailableFeatures.containsKey(name);
@@ -2323,6 +2485,7 @@
                 + " is not privileged to communicate with user=" + userId);
     }
 
+    @Override
     public int checkPermission(String permName, String pkgName) {
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(pkgName);
@@ -2340,6 +2503,7 @@
         return PackageManager.PERMISSION_DENIED;
     }
 
+    @Override
     public int checkUidPermission(String permName, int uid) {
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
@@ -2514,18 +2678,21 @@
         return added;
     }
 
+    @Override
     public boolean addPermission(PermissionInfo info) {
         synchronized (mPackages) {
             return addPermissionLocked(info, false);
         }
     }
 
+    @Override
     public boolean addPermissionAsync(PermissionInfo info) {
         synchronized (mPackages) {
             return addPermissionLocked(info, true);
         }
     }
 
+    @Override
     public void removePermission(String name) {
         synchronized (mPackages) {
             checkPermissionTreeLP(name);
@@ -2570,6 +2737,7 @@
         }
     }
 
+    @Override
     public void grantPermission(String packageName, String permissionName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
@@ -2599,6 +2767,7 @@
         }
     }
 
+    @Override
     public void revokePermission(String packageName, String permissionName) {
         int changedAppId = -1;
 
@@ -2657,12 +2826,14 @@
         }
     }
 
+    @Override
     public boolean isProtectedBroadcast(String actionName) {
         synchronized (mPackages) {
             return mProtectedBroadcasts.contains(actionName);
         }
     }
 
+    @Override
     public int checkSignatures(String pkg1, String pkg2) {
         synchronized (mPackages) {
             final PackageParser.Package p1 = mPackages.get(pkg1);
@@ -2675,6 +2846,7 @@
         }
     }
 
+    @Override
     public int checkUidSignatures(int uid1, int uid2) {
         // Map to base uids.
         uid1 = UserHandle.getAppId(uid1);
@@ -2814,6 +2986,7 @@
         return PackageManager.SIGNATURE_NO_MATCH;
     }
 
+    @Override
     public String[] getPackagesForUid(int uid) {
         uid = UserHandle.getAppId(uid);
         // reader
@@ -2837,6 +3010,7 @@
         return null;
     }
 
+    @Override
     public String getNameForUid(int uid) {
         // reader
         synchronized (mPackages) {
@@ -2852,6 +3026,7 @@
         return null;
     }
 
+    @Override
     public int getUidForSharedUser(String sharedUserName) {
         if(sharedUserName == null) {
             return -1;
@@ -2866,6 +3041,7 @@
         }
     }
 
+    @Override
     public int getFlagsForUid(int uid) {
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
@@ -3797,6 +3973,7 @@
         }
     }
 
+    @Override
     public List<ProviderInfo> queryContentProviders(String processName,
             int uid, int flags) {
         ArrayList<ProviderInfo> finalList = null;
@@ -3834,6 +4011,7 @@
         return finalList;
     }
 
+    @Override
     public InstrumentationInfo getInstrumentationInfo(ComponentName name,
             int flags) {
         // reader
@@ -3843,6 +4021,7 @@
         }
     }
 
+    @Override
     public List<InstrumentationInfo> queryInstrumentation(String targetPackage,
             int flags) {
         ArrayList<InstrumentationInfo> finalList =
@@ -4289,21 +4468,60 @@
         }
 
         if (pkgs != null) {
+            // Filter out packages that aren't recently used.
+            //
+            // The exception is first boot of a non-eng device, which
+            // should do a full dexopt.
+            boolean eng = "eng".equals(SystemProperties.get("ro.build.type"));
+            if (eng || !isFirstBoot()) {
+                // TODO: add a property to control this?
+                long dexOptLRUThresholdInMinutes;
+                if (eng) {
+                    dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.
+                } else {
+                    dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.
+                }
+                long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
+
+                int total = pkgs.size();
+                int skipped = 0;
+                long now = System.currentTimeMillis();
+                for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
+                    PackageParser.Package pkg = i.next();
+                    long then = pkg.mLastPackageUsageTimeInMills;
+                    if (then + dexOptLRUThresholdInMills < now) {
+                        if (DEBUG_DEXOPT) {
+                            Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
+                                  ((then == 0) ? "never" : new Date(then)));
+                        }
+                        i.remove();
+                        skipped++;
+                    }
+                }
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
+                }
+            }
+
             int i = 0;
             for (PackageParser.Package pkg : pkgs) {
+                i++;
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Optimizing app " + i + " of " + pkgs.size()
+                          + ": " + pkg.packageName);
+                }
                 if (!isFirstBoot()) {
-                    i++;
                     try {
                         ActivityManagerNative.getDefault().showBootMessage(
                                 mContext.getResources().getString(
-                                        com.android.internal.R.string.android_upgrading_apk,
+                                        R.string.android_upgrading_apk,
                                         i, pkgs.size()), true);
                     } catch (RemoteException e) {
                     }
                 }
                 PackageParser.Package p = pkg;
                 synchronized (mInstallLock) {
-                    if (!p.mDidDexOpt) {
+                    if (p.mDexOptNeeded) {
                         performDexOptLI(p, false /* force dex */, false /* defer */,
                                 true /* include dependencies */);
                     }
@@ -4315,25 +4533,57 @@
     @Override
     public boolean performDexOpt(String packageName) {
         enforceSystemOrRoot("Only the system can request dexopt be performed");
-        if (!mNoDexOpt) {
-            return false;
-        }
+        return performDexOpt(packageName, true);
+    }
+
+    public boolean performDexOpt(String packageName, boolean updateUsage) {
 
         PackageParser.Package p;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
-            if (p == null || p.mDidDexOpt) {
+            if (p == null) {
+                return false;
+            }
+            if (updateUsage) {
+                p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
+            }
+            mPackageUsage.write();
+            if (!p.mDexOptNeeded) {
                 return false;
             }
         }
+
         synchronized (mInstallLock) {
             return performDexOptLI(p, false /* force dex */, false /* defer */,
                     true /* include dependencies */) == DEX_OPT_PERFORMED;
         }
     }
 
-    private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, boolean forceDex,
-            boolean defer, HashSet<String> done) {
+    public HashSet<String> getPackagesThatNeedDexOpt() {
+        HashSet<String> pkgs = null;
+        synchronized (mPackages) {
+            for (PackageParser.Package p : mPackages.values()) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, p.packageName + " mDexOptNeeded=" + p.mDexOptNeeded);
+                }
+                if (!p.mDexOptNeeded) {
+                    continue;
+                }
+                if (pkgs == null) {
+                    pkgs = new HashSet<String>();
+                }
+                pkgs.add(p.packageName);
+            }
+        }
+        return pkgs;
+    }
+
+    public void shutdown() {
+        mPackageUsage.write(true);
+    }
+
+    private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet,
+             boolean forceDex, boolean defer, HashSet<String> done) {
         for (int i=0; i<libs.size(); i++) {
             PackageParser.Package libPkg;
             String libName;
@@ -4358,8 +4608,7 @@
     static final int DEX_OPT_FAILED = -1;
 
     private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride,
-            boolean forceDex,
-            boolean defer, HashSet<String> done) {
+            boolean forceDex, boolean defer, HashSet<String> done) {
         final String instructionSet = instructionSetOverride != null ?
                 instructionSetOverride : getAppInstructionSet(pkg.applicationInfo);
 
@@ -4376,47 +4625,52 @@
         boolean performed = false;
         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
             String path = pkg.mScanPath;
-            int ret = 0;
             try {
-                if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path,
-                        pkg.packageName, instructionSet, defer)) {
-                    if (!forceDex && defer) {
-                        if (mDeferredDexOpt == null) {
-                            mDeferredDexOpt = new HashSet<PackageParser.Package>();
-                        }
-                        mDeferredDexOpt.add(pkg);
-                        return DEX_OPT_DEFERRED;
-                    } else {
-                        Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName +
-                                " (instructionSet=" + instructionSet + ")");
-
-                        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-                        ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
+                boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
+                                                                                pkg.packageName,
+                                                                                instructionSet,
+                                                                                defer);
+                // There are three basic cases here:
+                // 1.) we need to dexopt, either because we are forced or it is needed
+                // 2.) we are defering a needed dexopt
+                // 3.) we are skipping an unneeded dexopt
+                if (forceDex || (!defer && isDexOptNeededInternal)) {
+                    Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
+                    final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+                    int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
                                                 pkg.packageName, instructionSet);
-                        pkg.mDidDexOpt = true;
-                        performed = true;
+                    // Note that we ran dexopt, since rerunning will
+                    // probably just result in an error again.
+                    pkg.mDexOptNeeded = false;
+                    if (ret < 0) {
+                        return DEX_OPT_FAILED;
                     }
+                    return DEX_OPT_PERFORMED;
                 }
+                if (defer && isDexOptNeededInternal) {
+                    if (mDeferredDexOpt == null) {
+                        mDeferredDexOpt = new HashSet<PackageParser.Package>();
+                    }
+                    mDeferredDexOpt.add(pkg);
+                    return DEX_OPT_DEFERRED;
+                }
+                pkg.mDexOptNeeded = false;
+                return DEX_OPT_SKIPPED;
             } catch (FileNotFoundException e) {
                 Slog.w(TAG, "Apk not found for dexopt: " + path);
-                ret = -1;
+                return DEX_OPT_FAILED;
             } catch (IOException e) {
                 Slog.w(TAG, "IOException reading apk: " + path, e);
-                ret = -1;
-            } catch (dalvik.system.StaleDexCacheError e) {
+                return DEX_OPT_FAILED;
+            } catch (StaleDexCacheError e) {
                 Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
-                ret = -1;
+                return DEX_OPT_FAILED;
             } catch (Exception e) {
                 Slog.w(TAG, "Exception when doing dexopt : ", e);
-                ret = -1;
-            }
-            if (ret < 0) {
-                //error from installer
                 return DEX_OPT_FAILED;
             }
         }
-
-        return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+        return DEX_OPT_SKIPPED;
     }
 
     private String getAppInstructionSet(ApplicationInfo info) {
@@ -4705,7 +4959,7 @@
                     mResolveActivity.processName = "system:ui";
                     mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                     mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-                    mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert;
+                    mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
                     mResolveActivity.exported = true;
                     mResolveActivity.enabled = true;
                     mResolveInfo.activityInfo = mResolveActivity;
@@ -7064,6 +7318,7 @@
         return mMediaMounted || Environment.isExternalStorageEmulated();
     }
 
+    @Override
     public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
         // writer
         synchronized (mPackages) {
@@ -7249,6 +7504,7 @@
     }
 
     /* Called when a downloaded package installation has been confirmed by the user */
+    @Override
     public void installPackage(
             final Uri packageURI, final IPackageInstallObserver observer, final int flags,
             final String installerPackageName) {
@@ -7266,6 +7522,7 @@
                 installerPackageName, verificationParams, encryptionParams);
     }
 
+    @Override
     public void installPackageWithVerificationAndEncryption(Uri packageURI,
             IPackageInstallObserver observer, int flags, String installerPackageName,
             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
@@ -7675,6 +7932,7 @@
         }
     }
 
+    @Override
     public void finishPackageInstall(int token) {
         enforceSystemOrRoot("Only the system is allowed to finish installs");
 
@@ -7746,6 +8004,7 @@
                 -1);
     }
 
+    @Override
     public void setInstallerPackageName(String targetPackage, String installerPackageName) {
         final int uid = Binder.getCallingUid();
         // writer
@@ -9701,22 +9960,22 @@
 
     // Utility method used to move dex files during install.
     private int moveDexFilesLI(PackageParser.Package newPackage) {
-        int retCode;
         if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
-                    getAppInstructionSet(newPackage.applicationInfo));
+            final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
+            int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
+                                             instructionSet);
             if (retCode != 0) {
-                if (mNoDexOpt) {
-                    /*
-                     * If we're in an engineering build, programs are lazily run
-                     * through dexopt. If the .dex file doesn't exist yet, it
-                     * will be created when the program is run next.
-                     */
-                    Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath);
-                } else {
-                    Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
-                    return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                }
+                /*
+                 * Programs may be lazily run through dexopt, so the
+                 * source may not exist. However, something seems to
+                 * have gone wrong, so note that dexopt needs to be
+                 * run again and remove the source file. In addition,
+                 * remove the target to make sure there isn't a stale
+                 * file from a previous version of the package.
+                 */
+                newPackage.mDexOptNeeded = true;
+                mInstaller.rmdex(newPackage.mScanPath, instructionSet);
+                mInstaller.rmdex(newPackage.mPath, instructionSet);
             }
         }
         return PackageManager.INSTALL_SUCCEEDED;
@@ -10680,6 +10939,7 @@
         }
     }
 
+    @Override
     public void deleteApplicationCacheFiles(final String packageName,
             final IPackageDataObserver observer) {
         mContext.enforceCallingOrSelfPermission(
@@ -10732,6 +10992,7 @@
         return true;
     }
 
+    @Override
     public void getPackageSizeInfo(final String packageName, int userHandle,
             final IPackageStatsObserver observer) {
         mContext.enforceCallingOrSelfPermission(
@@ -10811,14 +11072,17 @@
     }
 
 
+    @Override
     public void addPackageToPreferred(String packageName) {
         Slog.w(TAG, "addPackageToPreferred: this is now a no-op");
     }
 
+    @Override
     public void removePackageFromPreferred(String packageName) {
         Slog.w(TAG, "removePackageFromPreferred: this is now a no-op");
     }
 
+    @Override
     public List<PackageInfo> getPreferredPackages(int flags) {
         return new ArrayList<PackageInfo>();
     }
@@ -10846,6 +11110,7 @@
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
 
+    @Override
     public void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, int userId) {
         addPreferredActivityInternal(filter, match, set, activity, true, userId);
@@ -10882,6 +11147,7 @@
         }
     }
 
+    @Override
     public void replacePreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity) {
         if (filter.countActions() != 1) {
@@ -10938,6 +11204,7 @@
         }
     }
 
+    @Override
     public void clearPackagePreferredActivities(String packageName) {
         final int uid = Binder.getCallingUid();
         // writer
@@ -11001,6 +11268,7 @@
         return changed;
     }
 
+    @Override
     public void resetPreferredActivities(int userId) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
@@ -11014,6 +11282,7 @@
         }
     }
 
+    @Override
     public int getPreferredActivities(List<IntentFilter> outFilters,
             List<ComponentName> outActivities, String packageName) {
 
@@ -11321,6 +11590,7 @@
                 new int[] {UserHandle.getUserId(packageUid)});
     }
 
+    @Override
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
         if (!sUserManager.exists(userId)) return;
         final int uid = Binder.getCallingUid();
@@ -11337,6 +11607,7 @@
         }
     }
 
+    @Override
     public String getInstallerPackageName(String packageName) {
         // reader
         synchronized (mPackages) {
@@ -11366,6 +11637,7 @@
         }
     }
 
+    @Override
     public void enterSafeMode() {
         enforceSystemOrRoot("Only the system can request entering safe mode");
 
@@ -11374,6 +11646,7 @@
         }
     }
 
+    @Override
     public void systemReady() {
         mSystemReady = true;
 
@@ -11419,10 +11692,12 @@
         sUserManager.systemReady();
     }
 
+    @Override
     public boolean isSafeMode() {
         return mSafeMode;
     }
 
+    @Override
     public boolean hasSystemUidErrors() {
         return mHasSystemUidErrors;
     }
@@ -11920,6 +12195,7 @@
     /*
      * Update media status on PackageManager.
      */
+    @Override
     public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
         int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
@@ -12514,6 +12790,7 @@
         });
     }
 
+    @Override
     public boolean setInstallLocation(int loc) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
                 null);
@@ -12529,6 +12806,7 @@
         return false;
    }
 
+    @Override
     public int getInstallLocation() {
         return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                 android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION,
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 8fed79f..956e3e6 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -45,6 +45,7 @@
 import android.os.storage.IMountShutdownObserver;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.server.pm.PackageManagerService;
 
 import android.util.Log;
 import android.view.WindowManager;
@@ -329,6 +330,14 @@
             }
         }
 
+        Log.i(TAG, "Shutting down package manager...");
+
+        final PackageManagerService pm = (PackageManagerService)
+            ServiceManager.getService("package");
+        if (pm != null) {
+            pm.shutdown();
+        }
+
         // Shutdown radios.
         shutdownRadios(MAX_RADIO_WAIT_TIME);
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4318b0e..e746c1a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -299,6 +299,7 @@
 
     boolean mHasSurface = false;
 
+    boolean mNotOnAppsDisplay = false;
     DisplayContent  mDisplayContent;
 
     /** When true this window can be displayed on screens owther than mOwnerUid's */
@@ -430,6 +431,10 @@
         }
         mRootToken = appToken;
         mAppToken = appToken.appWindowToken;
+        if (mAppToken != null) {
+            final DisplayContent appDisplay = getDisplayContent();
+            mNotOnAppsDisplay = displayContent != appDisplay;
+        }
 
         mWinAnimator = new WindowStateAnimator(this);
         mWinAnimator.mAlpha = a.alpha;
@@ -717,7 +722,8 @@
     }
 
     public DisplayContent getDisplayContent() {
-        return mAppToken == null ? mDisplayContent : getStack().getDisplayContent();
+        return mAppToken == null || mNotOnAppsDisplay ?
+                mDisplayContent : getStack().getDisplayContent();
     }
 
     public int getDisplayId() {
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 163692b..163225e 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -46,33 +46,16 @@
 // ----------------------------------------------------------------------------
 
 static struct {
-    jfieldID mFinalizer;
-    jfieldID mNativeCanvas;
+    jmethodID safeCanvasSwap;
 } gCanvasClassInfo;
 
-static struct {
-    jfieldID mNativeCanvas;
-} gCanvasFinalizerClassInfo;
-
-#define GET_LONG(object, field) \
-    env->GetLongField(object, field)
-
-#define SET_LONG(object, field, value) \
-    env->SetLongField(object, field, value)
+#define INVOKEV(object, method, ...) \
+    env->CallVoidMethod(object, method, __VA_ARGS__)
 
 // ----------------------------------------------------------------------------
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
-    jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
-    SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
-            GET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas));
-    SET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas, (long) newCanvas);
-    SET_LONG(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (long) newCanvas);
-    SkSafeUnref(previousCanvas);
-}
-
 static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject,
         jobject canvas, jint width, jint height) {
 
@@ -82,7 +65,7 @@
     bitmap->eraseColor(0);
 
     SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap));
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     return reinterpret_cast<jlong>(bitmap);
 }
@@ -92,7 +75,7 @@
 
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     SkCanvas* nativeCanvas = SkNEW(SkCanvas);
-    swapCanvasPtr(env, canvas, nativeCanvas);
+    INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false);
 
     delete bitmap;
 }
@@ -242,9 +225,9 @@
         var = env->FindClass(className); \
         LOG_FATAL_IF(! var, "Unable to find class " className);
 
-#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
-        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
-        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(!var, "Unable to find method " methodName);
 
 const char* const kClassPathName = "com/android/server/AssetAtlasService";
 
@@ -261,12 +244,7 @@
     jclass clazz;
 
     FIND_CLASS(clazz, "android/graphics/Canvas");
-    GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
-            "Landroid/graphics/Canvas$CanvasFinalizer;");
-    GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
-
-    FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
-    GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J");
+    GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V");
 
     return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 23b912f..9a9f1c8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -50,7 +50,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -100,6 +100,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -232,6 +233,8 @@
     static class ActiveAdmin {
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
+        private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+        private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
         private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
         private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
@@ -297,6 +300,7 @@
 
         boolean encryptionRequested = false;
         boolean disableCamera = false;
+        Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
         boolean specifiesGlobalProxy = false;
@@ -413,6 +417,15 @@
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
                 out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
             }
+            if (!accountTypesWithManagementDisabled.isEmpty()) {
+                out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+                for (String ac : accountTypesWithManagementDisabled) {
+                    out.startTag(null, TAG_ACCOUNT_TYPE);
+                    out.attribute(null, ATTR_VALUE, ac);
+                    out.endTag(null, TAG_ACCOUNT_TYPE);
+                }
+                out.endTag(null,  TAG_DISABLE_ACCOUNT_MANAGEMENT);
+            }
         }
 
         void readFromXml(XmlPullParser parser)
@@ -484,6 +497,23 @@
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
+                    int outerDepthDAM = parser.getDepth();
+                    int typeDAM;
+                    while ((typeDAM=parser.next()) != XmlPullParser.END_DOCUMENT
+                            && (typeDAM != XmlPullParser.END_TAG
+                                    || parser.getDepth() > outerDepthDAM)) {
+                        if (typeDAM == XmlPullParser.END_TAG || typeDAM == XmlPullParser.TEXT) {
+                            continue;
+                        }
+                        String tagDAM = parser.getName();
+                        if (TAG_ACCOUNT_TYPE.equals(tagDAM)) {
+                            accountTypesWithManagementDisabled.add(
+                                    parser.getAttributeValue(null, ATTR_VALUE));
+                        } else {
+                            Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                        }
+                    }
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                 }
@@ -2533,7 +2563,7 @@
         exclusionList = exclusionList.trim();
         ContentResolver res = mContext.getContentResolver();
 
-        ProxyProperties proxyProperties = new ProxyProperties(data[0], proxyPort, exclusionList);
+        ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList);
         if (!proxyProperties.isValid()) {
             Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
             return;
@@ -3178,7 +3208,6 @@
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
             }
-
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
@@ -3277,4 +3306,42 @@
         ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
         return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
     }
+
+    @Override
+    public void setAccountManagementDisabled(ComponentName who, String accountType,
+            boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (disabled) {
+                ap.accountTypesWithManagementDisabled.add(accountType);
+            } else {
+                ap.accountTypesWithManagementDisabled.remove(accountType);
+            }
+            saveSettingsLocked(UserHandle.getCallingUserId());
+        }
+    }
+
+    @Override
+    public String[] getAccountTypesWithManagementDisabled() {
+        if (!mHasFeature) {
+            return null;
+        }
+        synchronized (this) {
+            DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+            final int N = policy.mAdminList.size();
+            HashSet<String> resultSet = new HashSet<String>();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = policy.mAdminList.get(i);
+                resultSet.addAll(admin.accountTypesWithManagementDisabled);
+            }
+            return resultSet.toArray(new String[resultSet.size()]);
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7c9f7a8..716823c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -73,6 +73,7 @@
 import com.android.server.net.NetworkStatsService;
 import com.android.server.notification.NotificationManagerService;
 import com.android.server.os.SchedulingPolicyService;
+import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.PackageManagerService;
@@ -604,6 +605,14 @@
 
             if (!disableNetwork) {
                 try {
+                    Slog.i(TAG, "Network Score Service");
+                    networkScore = new NetworkScoreService(context);
+                    ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+                } catch (Throwable e) {
+                    reportWtf("starting Network Score Service", e);
+                }
+
+                try {
                     Slog.i(TAG, "NetworkStats Service");
                     networkStats = new NetworkStatsService(context, networkManagement, alarm);
                     ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
@@ -641,6 +650,15 @@
                 }
 
                 try {
+                    Slog.i(TAG, "Wi-Fi Scanning Service");
+                    mSystemServiceManager.startService(
+                            "com.android.server.wifi.WifiScanningService");
+
+                } catch (Throwable e) {
+                    reportWtf("starting Wi-Fi Scanning Service", e);
+                }
+
+                try {
                     Slog.i(TAG, "Connectivity Service");
                     connectivity = new ConnectivityService(
                             context, networkManagement, networkStats, networkPolicy);
@@ -652,14 +670,6 @@
                 }
 
                 try {
-                    Slog.i(TAG, "Network Score Service");
-                    networkScore = new NetworkScoreService(context);
-                    ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
-                } catch (Throwable e) {
-                    reportWtf("starting Network Score Service", e);
-                }
-
-                try {
                     Slog.i(TAG, "Network Service Discovery Service");
                     serviceDiscovery = NsdService.create(context);
                     ServiceManager.addService(
@@ -962,6 +972,13 @@
                 } catch (Throwable e) {
                     Slog.e(TAG, "Failure starting TrustManagerService", e);
                 }
+
+                try {
+                    Slog.i(TAG, "BackgroundDexOptService");
+                    new BackgroundDexOptService(context);
+                } catch (Throwable e) {
+                    reportWtf("starting BackgroundDexOptService", e);
+                }
             }
 
             try {
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index af0d0ad..3e9cf43 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -232,6 +232,15 @@
                 <category android:name="com.android.test.hwui.TEST" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="LooperAcceleration"
+                android:label="Misc/LooperAcceleration">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="TextFadeActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java
new file mode 100644
index 0000000..20d8e11
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java
@@ -0,0 +1,96 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Canvas;
+import android.os.Bundle;
+import android.os.Looper;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.LinearLayout;
+
+public class LooperAcceleration extends Activity {
+
+    static final boolean INCLUDE_WEBVIEW = false;
+
+    static class IsAcceleratedView extends View {
+
+        public IsAcceleratedView(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            if (canvas.isHardwareAccelerated()) {
+                canvas.drawARGB(0xFF, 0x00, 0xFF, 0x00);
+            } else {
+                canvas.drawARGB(0xFF, 0xFF, 0x00, 0x00);
+            }
+        }
+
+    }
+
+    private View makeView() {
+        LinearLayout layout = new LinearLayout(this);
+        layout.addView(new IsAcceleratedView(this), LayoutParams.MATCH_PARENT, 60);
+
+        if (INCLUDE_WEBVIEW) {
+            WebView wv = new WebView(this);
+            wv.setWebViewClient(new WebViewClient());
+            wv.setWebChromeClient(new WebChromeClient());
+            wv.loadUrl("http://www.webkit.org/blog-files/3d-transforms/poster-circle.html");
+            layout.addView(wv, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        }
+        return layout;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(makeView());
+
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                final Context context = LooperAcceleration.this;
+                Dialog dlg = new Dialog(context);
+                dlg.addContentView(makeView(), new LayoutParams(300, 400));
+                dlg.setCancelable(true);
+                dlg.setCanceledOnTouchOutside(true);
+                dlg.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        Looper.myLooper().quit();
+                    }
+                });
+                dlg.setTitle("Not Looper.getMainLooper() check");
+                dlg.show();
+                Looper.loop();
+            }
+        }.start();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index bb05d45..aede236 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -71,7 +71,7 @@
      * Returns the native delegate associated to a given {@link Canvas} object.
      */
     public static Canvas_Delegate getDelegate(Canvas canvas) {
-        return sManager.getDelegate(canvas.mNativeCanvas);
+        return sManager.getDelegate(canvas.getNativeCanvas());
     }
 
     /**
@@ -102,7 +102,7 @@
     @LayoutlibDelegate
     /*package*/ static boolean isOpaque(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return false;
         }
@@ -113,7 +113,7 @@
     @LayoutlibDelegate
     /*package*/ static int getWidth(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -124,7 +124,7 @@
     @LayoutlibDelegate
     /*package*/ static int getHeight(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -135,7 +135,7 @@
     @LayoutlibDelegate
    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -146,7 +146,7 @@
     @LayoutlibDelegate
     /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -157,7 +157,7 @@
     @LayoutlibDelegate
    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -168,7 +168,7 @@
     @LayoutlibDelegate
    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -204,7 +204,7 @@
     /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
             float bottom) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return false;
         }
@@ -227,7 +227,7 @@
     @LayoutlibDelegate
     /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -238,7 +238,7 @@
     @LayoutlibDelegate
     /*package*/ static void restore(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -249,7 +249,7 @@
     @LayoutlibDelegate
     /*package*/ static int getSaveCount(Canvas thisCanvas) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return 0;
         }
@@ -260,7 +260,7 @@
     @LayoutlibDelegate
     /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
         // get the delegate from the native int.
-        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas());
         if (canvasDelegate == null) {
             return;
         }
@@ -287,7 +287,7 @@
     /*package*/ static void drawLines(Canvas thisCanvas,
             final float[] pts, final int offset, final int count,
             Paint paint) {
-        draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
+        draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/,
                 false /*forceSrcMode*/, new GcSnapshot.Drawable() {
                     @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
diff --git a/core/res/res/drawable/stat_sys_adb.xml b/wifi/java/android/net/wifi/IWifiScanner.aidl
similarity index 61%
rename from core/res/res/drawable/stat_sys_adb.xml
rename to wifi/java/android/net/wifi/IWifiScanner.aidl
index dfc8563..fef2d11 100644
--- a/core/res/res/drawable/stat_sys_adb.xml
+++ b/wifi/java/android/net/wifi/IWifiScanner.aidl
@@ -1,13 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /*
- * Copyright 2013, The Android Open Source Project
+ * Copyright (C) 2008 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
+ *      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,
@@ -15,9 +13,15 @@
  * 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/stat_sys_adb_am"
-        android:autoMirrored="true">
-</bitmap>
+package android.net.wifi;
+
+import android.os.Messenger;
+
+/**
+ * {@hide}
+ */
+interface IWifiScanner
+{
+    Messenger getMessenger();
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index d7ecaff..1cb9546 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -56,6 +56,13 @@
     public long timestamp;
 
     /**
+     * Timestamp representing date when this result was last seen, in milliseconds from 1970
+     * {@hide}
+     */
+    public long seen;
+
+
+    /**
      * The approximate distance to the AP in centimeter, if available.  Else
      * {@link UNSPECIFIED}.
      * {@hide}
@@ -114,9 +121,17 @@
             timestamp = source.timestamp;
             distanceCm = source.distanceCm;
             distanceSdCm = source.distanceSdCm;
+            seen = source.seen;
         }
     }
 
+    /** empty scan result
+     *
+     * {@hide}
+     * */
+    public ScanResult() {
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6562462..ce8c8b8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.text.TextUtils;
 
+import java.util.HashMap;
 import java.util.BitSet;
 
 /**
@@ -302,6 +303,156 @@
 
     /**
      * @hide
+     * dhcp server MAC address if known
+     */
+    public String dhcpServer;
+
+    /**
+     * @hide
+     * default Gateway MAC address if known
+     */
+    public String defaultGwMacAddress;
+
+    /**
+     * @hide
+     * BSSID list on which this configuration was seen.
+     * TODO: prevent this list to grow infinitely, age-out the results
+     */
+    public HashMap<String, ScanResult> scanResultCache;
+
+    /** @hide **/
+    public static int INVALID_RSSI = -127;
+
+    /**
+     * @hide
+     * A summary of the RSSI and Band status for that configuration
+     * This is used as a temporary value by the auto-join controller
+     */
+    public final class Visibility
+    {
+        public int rssi5;   // strongest 5GHz RSSI
+        public int rssi24;  // strongest 2.4GHz RSSI
+        public int num5;    // number of BSSIDs on 5GHz
+        public int num24;   // number of BSSIDs on 2.4GHz
+        public long age5;  // timestamp of the strongest 5GHz BSSID (last time it was seen)
+        public long age24;   // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
+        public Visibility()
+        {
+            rssi5 = INVALID_RSSI;
+            rssi24 = INVALID_RSSI;
+        }
+        public Visibility(Visibility source)
+        {
+            rssi5 = source.rssi5;
+            rssi24 = source.rssi24;
+            age24 = source.age24;
+            age5 = source.age5;
+            num24 = source.num24;
+            num5 = source.num5;
+        }
+    }
+
+    /** @hide
+     * Cache the visibility status of this configuration.
+     * Visibility can change at any time depending on scan results availability.
+     * Owner of the WifiConfiguration is responsible to set this field based on
+     * recent scan results.
+     ***/
+    public Visibility visibility;
+
+    /** @hide
+     * calculate and set Visibility for that configuration.
+     *
+     * age in milliseconds: we will consider only ScanResults that are more recent,
+     * i.e. younger.
+     ***/
+    public Visibility setVisibility(long age) {
+        if (scanResultCache == null) {
+            visibility = null;
+            return null;
+        }
+
+        Visibility status = new Visibility();
+
+        long now_ms = System.currentTimeMillis();
+        for(ScanResult result : scanResultCache.values()) {
+            if (result.seen == 0)
+                continue;
+
+            if ((result.frequency > 4900) && (result.frequency < 5900)) {
+                //strictly speaking: [4915, 5825]
+                //number of known BSSID on 5GHz band
+                status.num5 = status.num5 + 1;
+            } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+                //strictly speaking: [2412, 2482]
+                //number of known BSSID on 2.4Ghz band
+                status.num24 = status.num24 + 1;
+            }
+
+            if ((now_ms - result.seen) > age) continue;
+
+            if ((result.frequency > 4900) && (result.frequency < 5900)) {
+                if (result.level > status.rssi5) {
+                    status.rssi5 = result.level;
+                    status.age5 = result.seen;
+                }
+            } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+                if (result.level > status.rssi24) {
+                    status.rssi24 = result.level;
+                    status.age24 = result.seen;
+                }
+            }
+        }
+        visibility = status;
+        return status;
+    }
+
+    /** @hide */
+    public static final int AUTO_JOIN_ENABLED                   = 0;
+    /** @hide */
+    public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 1;
+    /**
+     * @hide
+     */
+    public int autoJoinStatus;
+
+    /**
+     * @hide
+     * Indicate that a WifiConfiguration is temporary and should not be saved
+     * nor considered by AutoJoin.
+     */
+    public boolean ephemeral;
+
+    /**
+     * @hide
+     * Connect choices
+     *
+     * remember the keys identifying the known WifiConfiguration over which this configuration
+     * was preferred by user or a "WiFi Network Management app", that is,
+     * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
+     * was visible to the user:
+     * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
+     *
+     * The integer represents the configuration's RSSI at that time (useful?)
+     *
+     * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
+     * the exact algorithm still fluctuating as of 5/7/2014
+     *
+     */
+    public HashMap<String, Integer> connectChoices;
+
+    /**
+     * @hide
+     * Linked Configurations: represent the set of Wificonfigurations that are equivalent
+     * regarding roaming and auto-joining.
+     * The linked configuration may or may not have same SSID, and may or may not have same
+     * credentials.
+     * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
+     */
+    public HashMap<String, Integer>  linkedConfigurations;
+
+    /**
+     * @hide
      */
     public enum ProxySettings {
         /* No proxy is to be used. Any existing proxy settings
@@ -346,6 +497,7 @@
         ipAssignment = IpAssignment.UNASSIGNED;
         proxySettings = ProxySettings.UNASSIGNED;
         linkProperties = new LinkProperties();
+        autoJoinStatus = AUTO_JOIN_ENABLED;
     }
 
     /**
@@ -369,6 +521,32 @@
 
         // TODO: Add more checks
         return true;
+
+    }
+
+    /**
+     * most recent time we have seen this configuration
+     * @return most recent scanResult
+     * @hide
+     */
+    public ScanResult lastSeen() {
+        ScanResult mostRecent = null;
+
+        if (scanResultCache == null) {
+            return null;
+        }
+
+        for (ScanResult result : scanResultCache.values()) {
+            if (mostRecent == null) {
+                if (result.seen != 0)
+                   mostRecent = result;
+            } else {
+                if (result.seen > mostRecent.seen) {
+                   mostRecent = result;
+                }
+            }
+        }
+        return mostRecent;
     }
 
     @Override
@@ -570,7 +748,48 @@
         return KeyMgmt.NONE;
     }
 
-    /** Implement the Parcelable interface {@hide} */
+    /* @hide
+     * Cache the config key, this seems useful as a speed up since a lot of
+     * lookups in the config store are done and based on this key.
+     */
+    String mCachedConfigKey;
+
+    /** @hide
+     *  return the string used to calculate the hash in WifiConfigStore
+     *  and uniquely identify this WifiConfiguration
+     */
+    public String configKey(boolean allowCached) {
+        String key;
+        if (allowCached && mCachedConfigKey != null) {
+            key = mCachedConfigKey;
+        } else {
+            key = this.SSID;
+            if (key == null)
+                key = "";
+            if (this.wepKeys[0] != null) {
+                key = key + "-WEP";
+            }
+            if (this.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+                key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+            }
+            if (this.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+                    this.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+                key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+            }
+            mCachedConfigKey = key;
+        }
+        return key;
+    }
+
+    /** @hide
+     * get configKey, force calculating the config string
+     */
+    public String configKey() {
+        return configKey(false);
+    }
+
+
+        /** Implement the Parcelable interface {@hide} */
     public int describeContents() {
         return 0;
     }
@@ -603,8 +822,32 @@
 
             ipAssignment = source.ipAssignment;
             proxySettings = source.proxySettings;
+
+            defaultGwMacAddress = source.defaultGwMacAddress;
+
             linkProperties = new LinkProperties(source.linkProperties);
-        }
+            if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
+                scanResultCache = new HashMap<String, ScanResult>();
+                scanResultCache.putAll(source.scanResultCache);
+            }
+
+            if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
+                connectChoices = new HashMap<String, Integer>();
+                connectChoices.putAll(source.connectChoices);
+            }
+
+            if ((source.linkedConfigurations != null)
+                    && (source.linkedConfigurations.size() > 0)) {
+                linkedConfigurations = new HashMap<String, Integer>();
+                linkedConfigurations.putAll(source.linkedConfigurations);
+            }
+            mCachedConfigKey = null; //force null configKey
+            autoJoinStatus = source.autoJoinStatus;
+
+            if (source.visibility != null) {
+                visibility = new Visibility(source.visibility);
+            }
+       }
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -633,6 +876,10 @@
         dest.writeString(ipAssignment.name());
         dest.writeString(proxySettings.name());
         dest.writeParcelable(linkProperties, flags);
+
+        dest.writeString(dhcpServer);
+        dest.writeString(defaultGwMacAddress);
+        dest.writeInt(autoJoinStatus);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -664,6 +911,10 @@
                 config.proxySettings = ProxySettings.valueOf(in.readString());
                 config.linkProperties = in.readParcelable(null);
 
+                config.dhcpServer = in.readString();
+                config.defaultGwMacAddress = in.readString();
+                config.autoJoinStatus = in.readInt();
+
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
new file mode 100644
index 0000000..e02e14c
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2008 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.net.wifi;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+
+/**
+ * This class provides a way to scan the Wifi universe around the device
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context
+ * .WIFI_SCANNING_SERVICE)}.
+ * @hide
+ */
+public class WifiScanner {
+
+    public static final int WIFI_BAND_UNSPECIFIED = 0;      /* not specified */
+    public static final int WIFI_BAND_24_GHZ = 1;           /* 2.4 GHz band */
+    public static final int WIFI_BAND_5_GHZ = 2;            /* 5 GHz band without DFS channels */
+    public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 4;  /* 5 GHz band with DFS channels */
+    public static final int WIFI_BAND_5_GHZ_WITH_DFS  = 6;  /* 5 GHz band with DFS channels */
+    public static final int WIFI_BAND_BOTH = 3;             /* both bands without DFS channels */
+    public static final int WIFI_BAND_BOTH_WITH_DFS = 7;    /* both bands with DFS channels */
+
+    public static final int MIN_SCAN_PERIOD_MS = 300;       /* minimum supported period */
+    public static final int MAX_SCAN_PERIOD_MS = 1024000;   /* maximum supported period */
+
+    public static final int REASON_SUCCEEDED = 0;
+    public static final int REASON_UNSPECIFIED = -1;
+    public static final int REASON_INVALID_LISTENER = -2;
+    public static final int REASON_INVALID_REQUEST = -3;
+    public static final int REASON_CONFLICTING_REQUEST = -4;
+
+    public static interface ActionListener {
+        public void onSuccess(Object result);
+        public void onFailure(int reason, Object exception);
+    }
+
+    /**
+     * gives you all the possible channels; channel is specified as an
+     * integer with frequency in MHz i.e. channel 1 is 2412
+     */
+    public List<Integer> getAvailableChannels(int band) {
+        return null;
+    }
+
+    /**
+     * provides channel specification to the APIs
+     */
+    public static class ChannelSpec {
+        public int frequency;
+        public boolean passive;                                    /* ignored on DFS channels */
+        public int dwellTimeMS;                                    /* not supported for now */
+
+        public ChannelSpec(int frequency) {
+            this.frequency = frequency;
+            passive = false;
+            dwellTimeMS = 0;
+        }
+    }
+
+    public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0;
+    public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1;
+    public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2;
+
+    /**
+     * scan configuration parameters
+     */
+    public static class ScanSettings implements Parcelable {
+
+        public int band;                                           /* ignore channels if specified */
+        public ChannelSpec[] channels;                             /* list of channels to scan */
+        public int periodInMs;                                     /* period of scan */
+        public int reportEvents;                                   /* a valid REPORT_EVENT value */
+
+        /** Implement the Parcelable interface {@hide} */
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(band);
+            dest.writeInt(periodInMs);
+            dest.writeInt(channels.length);
+
+            for (int i = 0; i < channels.length; i++) {
+                dest.writeInt(channels[i].frequency);
+                dest.writeInt(channels[i].dwellTimeMS);
+                dest.writeInt(channels[i].passive ? 1 : 0);
+            }
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<ScanSettings> CREATOR =
+                new Creator<ScanSettings>() {
+                    public ScanSettings createFromParcel(Parcel in) {
+
+                        ScanSettings settings = new ScanSettings();
+                        settings.band = in.readInt();
+                        settings.periodInMs = in.readInt();
+                        int num_channels = in.readInt();
+                        settings.channels = new ChannelSpec[num_channels];
+                        for (int i = 0; i < num_channels; i++) {
+                            int frequency = in.readInt();
+
+                            ChannelSpec spec = new ChannelSpec(frequency);
+                            spec.dwellTimeMS = in.readInt();
+                            spec.passive = in.readInt() == 1;
+                            settings.channels[i] = spec;
+                        }
+
+                        return settings;
+                    }
+
+                    public ScanSettings[] newArray(int size) {
+                        return new ScanSettings[size];
+                    }
+                };
+
+    }
+
+    public static class InformationElement {
+        public int id;
+        public byte[] bytes;
+    }
+
+    public static class FullScanResult {
+        public ScanResult result;
+        public InformationElement informationElements[];
+    }
+
+    /** @hide */
+    public static class ParcelableScanResults implements Parcelable {
+        public ScanResult mResults[];
+
+        public ParcelableScanResults(ScanResult[] results) {
+            mResults = results;
+        }
+
+        public ScanResult[] getResults() {
+            return mResults;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mResults.length);
+            for (int i = 0; i < mResults.length; i++) {
+                ScanResult result = mResults[i];
+                result.writeToParcel(dest, flags);
+            }
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<ParcelableScanResults> CREATOR =
+                new Creator<ParcelableScanResults>() {
+                    public ParcelableScanResults createFromParcel(Parcel in) {
+                        int n = in.readInt();
+                        ScanResult results[] = new ScanResult[n];
+                        for (int i = 0; i < n; i++) {
+                            results[i] = ScanResult.CREATOR.createFromParcel(in);
+                        }
+                        return new ParcelableScanResults(results);
+                    }
+
+                    public ParcelableScanResults[] newArray(int size) {
+                        return new ParcelableScanResults[size];
+                    }
+                };
+    }
+
+    /**
+     * Framework is co-ordinating scans across multiple apps; so it may not give exactly the
+     * same period requested. The period granted is stated on the onSuccess() event; and
+     * onPeriodChanged() will be called if/when it is changed because of multiple conflicting
+     * requests. This is similar to the way timers are handled.
+     */
+    public interface ScanListener extends ActionListener {
+        public void onPeriodChanged(int periodInMs);
+        public void onResults(ScanResult[] results);
+        public void onFullResult(FullScanResult fullScanResult);
+    }
+
+    public void scan(ScanSettings settings, ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_SCAN, 0, putListener(listener), settings);
+    }
+    public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings);
+    }
+    public void stopBackgroundScan(boolean flush, ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener));
+    }
+    public void retrieveScanResults(boolean flush, ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener));
+    }
+
+    public static class HotspotInfo {
+        public String bssid;
+        public int low;                                            /* minimum RSSI */
+        public int high;                                           /* maximum RSSI */
+    }
+
+    public static class WifiChangeSettings {
+        public int rssiSampleSize;                                 /* sample size for RSSI averaging */
+        public int lostApSampleSize;                               /* samples to confirm AP's loss */
+        public int unchangedSampleSize;                            /* samples to confirm no change */
+        public int minApsBreachingThreshold;                       /* change threshold to trigger event */
+        public HotspotInfo[] hotspotInfos;
+    }
+
+    /* overrides the significant wifi change state machine configuration */
+    public void configureSignificantWifiChange(
+            int rssiSampleSize,                             /* sample size for RSSI averaging */
+            int lostApSampleSize,                           /* samples to confirm AP's loss */
+            int unchangedSampleSize,                        /* samples to confirm no change */
+            int minApsBreachingThreshold,                   /* change threshold to trigger event */
+            HotspotInfo[] hotspotInfos                      /* signal thresholds to crosss */
+            )
+    {
+        validateChannel();
+        WifiChangeSettings settings = new WifiChangeSettings();
+        settings.rssiSampleSize = rssiSampleSize;
+        settings.lostApSampleSize = lostApSampleSize;
+        settings.unchangedSampleSize = unchangedSampleSize;
+        settings.minApsBreachingThreshold = minApsBreachingThreshold;
+        settings.hotspotInfos = hotspotInfos;
+
+        sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+    }
+
+    public interface SignificantWifiChangeListener extends ActionListener {
+        public void onChanging(ScanResult[] results);           /* changes are found */
+        public void onQuiescence(ScanResult[] results);         /* changes settled down */
+    }
+
+    public void trackSignificantWifiChange(SignificantWifiChangeListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener));
+    }
+    public void untrackSignificantWifiChange(SignificantWifiChangeListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener));
+    }
+
+    public void configureSignificantWifiChange(WifiChangeSettings settings) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+    }
+
+    public static interface HotlistListener extends ActionListener {
+        public void onFound(ScanResult[] results);
+    }
+
+    /** @hide */
+    public static class HotlistSettings implements Parcelable {
+        public HotspotInfo[] hotspotInfos;
+        public int apLostThreshold;
+
+        /** Implement the Parcelable interface {@hide} */
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(apLostThreshold);
+            dest.writeInt(hotspotInfos.length);
+            for (int i = 0; i < hotspotInfos.length; i++) {
+                HotspotInfo info = hotspotInfos[i];
+                dest.writeString(info.bssid);
+                dest.writeInt(info.low);
+                dest.writeInt(info.high);
+            }
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<HotlistSettings> CREATOR =
+                new Creator<HotlistSettings>() {
+                    public HotlistSettings createFromParcel(Parcel in) {
+                        HotlistSettings settings = new HotlistSettings();
+                        settings.apLostThreshold = in.readInt();
+                        int n = in.readInt();
+                        settings.hotspotInfos = new HotspotInfo[n];
+                        for (int i = 0; i < n; i++) {
+                            HotspotInfo info = new HotspotInfo();
+                            info.bssid = in.readString();
+                            info.low = in.readInt();
+                            info.high = in.readInt();
+                            settings.hotspotInfos[i] = info;
+                        }
+                        return settings;
+                    }
+
+                    public HotlistSettings[] newArray(int size) {
+                        return new HotlistSettings[size];
+                    }
+                };
+    }
+
+    public void setHotlist(HotspotInfo[] hotspots,
+            int apLostThreshold, HotlistListener listener) {
+        validateChannel();
+        HotlistSettings settings = new HotlistSettings();
+        settings.hotspotInfos = hotspots;
+        sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings);
+    }
+
+    public void resetHotlist(HotlistListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
+    }
+
+
+    /* private members and methods */
+
+    private static final String TAG = "WifiScanner";
+    private static final boolean DBG = true;
+
+    /* commands for Wifi Service */
+    private static final int BASE = Protocol.BASE_WIFI_SCANNER;
+
+    /** @hide */
+    public static final int CMD_SCAN                        = BASE + 0;
+    /** @hide */
+    public static final int CMD_START_BACKGROUND_SCAN       = BASE + 2;
+    /** @hide */
+    public static final int CMD_STOP_BACKGROUND_SCAN        = BASE + 3;
+    /** @hide */
+    public static final int CMD_GET_SCAN_RESULTS            = BASE + 4;
+    /** @hide */
+    public static final int CMD_SCAN_RESULT                 = BASE + 5;
+    /** @hide */
+    public static final int CMD_SET_HOTLIST                 = BASE + 6;
+    /** @hide */
+    public static final int CMD_RESET_HOTLIST               = BASE + 7;
+    /** @hide */
+    public static final int CMD_AP_FOUND                    = BASE + 9;
+    /** @hide */
+    public static final int CMD_AP_LOST                     = BASE + 10;
+    /** @hide */
+    public static final int CMD_START_TRACKING_CHANGE       = BASE + 11;
+    /** @hide */
+    public static final int CMD_STOP_TRACKING_CHANGE        = BASE + 12;
+    /** @hide */
+    public static final int CMD_CONFIGURE_WIFI_CHANGE       = BASE + 13;
+    /** @hide */
+    public static final int CMD_WIFI_CHANGE_DETECTED        = BASE + 15;
+    /** @hide */
+    public static final int CMD_WIFI_CHANGES_STABILIZED     = BASE + 16;
+    /** @hide */
+    public static final int CMD_OP_SUCCEEDED                = BASE + 17;
+    /** @hide */
+    public static final int CMD_OP_FAILED                   = BASE + 18;
+    /** @hide */
+    public static final int CMD_PERIOD_CHANGED              = BASE + 19;
+    /** @hide */
+    public static final int CMD_FULL_SCAN_RESULT            = BASE + 20;
+
+    private Context mContext;
+    private IWifiScanner mService;
+
+    private static final int INVALID_KEY = 0;
+    private static int sListenerKey = 1;
+
+    private static final SparseArray sListenerMap = new SparseArray();
+    private static final Object sListenerMapLock = new Object();
+
+    private static AsyncChannel sAsyncChannel;
+    private static CountDownLatch sConnected;
+
+    private static final Object sThreadRefLock = new Object();
+    private static int sThreadRefCount;
+    private static HandlerThread sHandlerThread;
+
+    /**
+     * Create a new WifiScanner instance.
+     * Applications will almost always want to use
+     * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+     * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+     * @param context the application context
+     * @param service the Binder interface
+     * @hide
+     */
+    public WifiScanner(Context context, IWifiScanner service) {
+        mContext = context;
+        mService = service;
+        init();
+    }
+
+    private void init() {
+        synchronized (sThreadRefLock) {
+            if (++sThreadRefCount == 1) {
+                Messenger messenger = null;
+                try {
+                    messenger = mService.getMessenger();
+                } catch (RemoteException e) {
+                    /* do nothing */
+                } catch (SecurityException e) {
+                    /* do nothing */
+                }
+
+                if (messenger == null) {
+                    sAsyncChannel = null;
+                    return;
+                }
+
+                sHandlerThread = new HandlerThread("WifiScanner");
+                sAsyncChannel = new AsyncChannel();
+                sConnected = new CountDownLatch(1);
+
+                sHandlerThread.start();
+                Handler handler = new ServiceHandler(sHandlerThread.getLooper());
+                sAsyncChannel.connect(mContext, handler, messenger);
+                try {
+                    sConnected.await();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "interrupted wait at init");
+                }
+            }
+        }
+    }
+
+    private void validateChannel() {
+        if (sAsyncChannel == null) throw new IllegalStateException(
+                "No permission to access and change wifi or a bad initialization");
+    }
+
+    private static int putListener(Object listener) {
+        if (listener == null) return INVALID_KEY;
+        int key;
+        synchronized (sListenerMapLock) {
+            do {
+                key = sListenerKey++;
+            } while (key == INVALID_KEY);
+            sListenerMap.put(key, listener);
+        }
+        return key;
+    }
+
+    private static Object getListener(int key) {
+        if (key == INVALID_KEY) return null;
+        synchronized (sListenerMapLock) {
+            Object listener = sListenerMap.get(key);
+            return listener;
+        }
+    }
+
+    private static int getListenerKey(Object listener) {
+        if (listener == null) return INVALID_KEY;
+        synchronized (sListenerMapLock) {
+            int index = sListenerMap.indexOfValue(listener);
+            if (index == -1) {
+                return INVALID_KEY;
+            } else {
+                return sListenerMap.keyAt(index);
+            }
+        }
+    }
+
+    private static Object removeListener(int key) {
+        if (key == INVALID_KEY) return null;
+        synchronized (sListenerMapLock) {
+            Object listener = sListenerMap.get(key);
+            sListenerMap.remove(key);
+            return listener;
+        }
+    }
+
+    private static int removeListener(Object listener) {
+        int key = getListenerKey(listener);
+        if (key == INVALID_KEY) return key;
+        synchronized (sListenerMapLock) {
+            sListenerMap.remove(key);
+            return key;
+        }
+    }
+
+    private static class ServiceHandler extends Handler {
+        ServiceHandler(Looper looper) {
+            super(looper);
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+                    } else {
+                        Log.e(TAG, "Failed to set up channel connection");
+                        // This will cause all further async API calls on the WifiManager
+                        // to fail and throw an exception
+                        sAsyncChannel = null;
+                    }
+                    sConnected.countDown();
+                    return;
+                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+                    return;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                    Log.e(TAG, "Channel connection lost");
+                    // This will cause all further async API calls on the WifiManager
+                    // to fail and throw an exception
+                    sAsyncChannel = null;
+                    getLooper().quit();
+                    return;
+            }
+
+            Object listener = getListener(msg.arg2);
+            if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
+
+            switch (msg.what) {
+                    /* ActionListeners grouped together */
+                case CMD_OP_SUCCEEDED :
+                    ((ActionListener) listener).onSuccess(msg.obj);
+                    break;
+                case CMD_OP_FAILED :
+                    ((ActionListener) listener).onFailure(msg.arg1, msg.obj);
+                    break;
+                case CMD_SCAN_RESULT :
+                    ((ScanListener) listener).onResults(
+                            ((ParcelableScanResults) msg.obj).getResults());
+                    return;
+                case CMD_FULL_SCAN_RESULT :
+                    FullScanResult result = (FullScanResult) msg.obj;
+                    ((ScanListener) listener).onFullResult(result);
+                    return;
+                case CMD_AP_FOUND:
+                    ((HotlistListener) listener).onFound(
+                            ((ParcelableScanResults) msg.obj).getResults());
+                    return;
+                case CMD_WIFI_CHANGE_DETECTED:
+                    ((SignificantWifiChangeListener) listener).onChanging(
+                            ((ParcelableScanResults) msg.obj).getResults());
+                   return;
+                case CMD_WIFI_CHANGES_STABILIZED:
+                    ((SignificantWifiChangeListener) listener).onQuiescence(
+                            ((ParcelableScanResults) msg.obj).getResults());
+                    return;
+                default:
+                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
+                    return;
+            }
+        }
+    }
+}