Merge "Correcting a bug related to Uri permissions." into lmp-preview-dev
diff --git a/api/current.txt b/api/current.txt
index 8953d7e..636e201c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3335,6 +3335,7 @@
     method public void openContextMenu(android.view.View);
     method public void openOptionsMenu();
     method public void overridePendingTransition(int, int);
+    method public void postponeEnterTransition();
     method public void recreate();
     method public void registerForContextMenu(android.view.View);
     method public final deprecated void removeDialog(int);
@@ -3390,6 +3391,7 @@
     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 startPostponedEnterTransition();
     method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean);
     method public void stopLockTask();
     method public deprecated void stopManagingCursor(android.database.Cursor);
@@ -25827,21 +25829,28 @@
     method public final void cancelNotification(java.lang.String);
     method public final void cancelNotifications(java.lang.String[]);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
-    method public android.service.notification.NotificationListenerService.Ranking getCurrentRanking();
+    method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onListenerConnected();
-    method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
-    method public void onNotificationRankingUpdate();
-    method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
+    method public void onNotificationPosted(android.service.notification.StatusBarNotification);
+    method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+    method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
+    method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
+    method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
   }
 
-  public static class NotificationListenerService.Ranking implements android.os.Parcelable {
+  public static class NotificationListenerService.Ranking {
+    method public java.lang.String getKey();
+    method public int getRank();
+    method public boolean isAmbient();
+    method public boolean isInterceptedByDoNotDisturb();
+  }
+
+  public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String[] getOrderedKeys();
-    method public int getRank(java.lang.String);
-    method public boolean isAmbient(java.lang.String);
-    method public boolean isInterceptedByDoNotDisturb(java.lang.String);
+    method public android.service.notification.NotificationListenerService.Ranking getRanking(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
index dce0a75..e6847a9 100644
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -20,6 +20,7 @@
 import android.app.IActivityManager;
 import android.app.IActivityManager.ContentProviderHolder;
 import android.content.IContentProvider;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -33,7 +34,8 @@
     enum CommandVerb {
         UNSPECIFIED,
         GET,
-        PUT
+        PUT,
+        DELETE
     }
 
     static String[] mArgs;
@@ -74,6 +76,8 @@
                         mVerb = CommandVerb.GET;
                     } else if ("put".equalsIgnoreCase(arg)) {
                         mVerb = CommandVerb.PUT;
+                    } else if ("delete".equalsIgnoreCase(arg)) {
+                        mVerb = CommandVerb.DELETE;
                     } else {
                         // invalid
                         System.err.println("Invalid command: " + arg);
@@ -87,7 +91,7 @@
                         break;  // invalid
                     }
                     mTable = arg.toLowerCase();
-                } else if (mVerb == CommandVerb.GET) {
+                } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) {
                     mKey = arg;
                     if (mNextArg >= mArgs.length) {
                         valid = true;
@@ -136,6 +140,10 @@
                         case PUT:
                             putForUser(provider, mUser, mTable, mKey, mValue);
                             break;
+                        case DELETE:
+                            System.out.println("Deleted "
+                                    + deleteForUser(provider, mUser, mTable, mKey) + " rows");
+                            break;
                         default:
                             System.err.println("Unspecified command");
                             break;
@@ -211,9 +219,31 @@
         }
     }
 
+    int deleteForUser(IContentProvider provider, int userHandle,
+            final String table, final String key) {
+        Uri targetUri;
+        if ("system".equals(table)) targetUri = Settings.System.getUriFor(key);
+        else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key);
+        else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key);
+        else {
+            System.err.println("Invalid table; no delete performed");
+            throw new IllegalArgumentException("Invalid table " + table);
+        }
+
+        int num = 0;
+        try {
+            num = provider.delete(null, targetUri, null, null);
+        } catch (RemoteException e) {
+            System.err.println("Can't clear key " + key + " in " + table + " for user "
+                    + userHandle);
+        }
+        return num;
+    }
+
     private static void printUsage() {
         System.err.println("usage:  settings [--user NUM] get namespace key");
         System.err.println("        settings [--user NUM] put namespace key value");
+        System.err.println("        settings [--user NUM] delete namespace key");
         System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
         System.err.println("If '--user NUM' is not given, the operations are performed on the owner user.");
     }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 946555f..f6883e2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5616,6 +5616,34 @@
         mExitTransitionListener = listener;
     }
 
+    /**
+     * Postpone the entering activity transition when Activity was started with
+     * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+     * android.util.Pair[])}.
+     * <p>This method gives the Activity the ability to delay starting the entering and
+     * shared element transitions until all data is loaded. Until then, the Activity won't
+     * draw into its window, leaving the window transparent. This may also cause the
+     * returning animation to be delayed until data is ready. This method should be
+     * called in {@link #onCreate(android.os.Bundle)} or in
+     * {@link #onActivityReenter(int, android.content.Intent)}.
+     * {@link #startPostponedEnterTransition()} must be called to allow the Activity to
+     * start the transitions. If the Activity did not use
+     * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+     * android.util.Pair[])}, then this method does nothing.</p>
+     */
+    public void postponeEnterTransition() {
+        mActivityTransitionState.postponeEnterTransition();
+    }
+
+    /**
+     * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
+     * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
+     * to have your Activity start drawing.
+     */
+    public void startPostponedEnterTransition() {
+        mActivityTransitionState.startPostponedEnterTransition();
+    }
+
     // ------------------ Internal API ------------------
     
     final void setParent(Activity parent) {
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index b658597..703df51 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -214,11 +214,21 @@
             ArrayList<String> allSharedElementNames,
             ArrayList<String> accepted, ArrayList<String> localNames,
             SharedElementListener listener, boolean isReturning) {
+        this(window, allSharedElementNames, listener, isReturning);
+        viewsReady(accepted, localNames);
+    }
+
+    public ActivityTransitionCoordinator(Window window,
+            ArrayList<String> allSharedElementNames,
+            SharedElementListener listener, boolean isReturning) {
         super(new Handler());
         mWindow = window;
         mListener = listener;
         mAllSharedElementNames = allSharedElementNames;
         mIsReturning = isReturning;
+    }
+
+    protected void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
         setSharedElements(accepted, localNames);
         if (getViewsTransition() != null) {
             getDecor().captureTransitioningViews(mTransitioningViews);
@@ -274,6 +284,8 @@
         return names;
     }
 
+    public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
+
     public static void setViewVisibility(Collection<View> views, int visibility) {
         if (views != null) {
             for (View view : views) {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index b32e9ad..d94dadd 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -87,6 +87,11 @@
      */
     private boolean mHasExited;
 
+    /**
+     * Postpone painting and starting the enter transition until this is false.
+     */
+    private boolean mIsEnterPostponed;
+
     public ActivityTransitionState() {
     }
 
@@ -140,15 +145,38 @@
         if (mEnterActivityOptions.isReturning()) {
             restoreExitedViews();
             activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
-            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
-                    resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
+        }
+        mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
+                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
+
+        if (!mIsEnterPostponed) {
+            startEnter();
+        }
+    }
+
+    public void postponeEnterTransition() {
+        mIsEnterPostponed = true;
+    }
+
+    public void startPostponedEnterTransition() {
+        if (mIsEnterPostponed) {
+            mIsEnterPostponed = false;
+            if (mEnterTransitionCoordinator != null) {
+                startEnter();
+            }
+        }
+    }
+
+    private void startEnter() {
+        if (mEnterActivityOptions.isReturning()) {
+            mEnterTransitionCoordinator.viewsReady(mExitingFrom, mExitingTo);
         } else {
-            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
-                    resultReceiver, sharedElementNames, null, null);
-            mEnteringNames = sharedElementNames;
+            mEnterTransitionCoordinator.viewsReady(null, null);
+            mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
             mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
             mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
         }
+
         mExitingFrom = null;
         mExitingTo = null;
         mEnterActivityOptions = null;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4b052e7..779e3de 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -53,18 +53,37 @@
     private boolean mIsCanceled;
     private ObjectAnimator mBackgroundAnimator;
     private boolean mIsExitTransitionComplete;
+    private boolean mIsReadyForTransition;
+    private Bundle mSharedElementsBundle;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
-            ArrayList<String> sharedElementNames,
-            ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
-        super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
-                getListener(activity, acceptedNames), acceptedNames != null);
+            ArrayList<String> sharedElementNames, boolean isReturning) {
+        super(activity.getWindow(), sharedElementNames,
+                getListener(activity, isReturning), isReturning);
         mActivity = activity;
         setResultReceiver(resultReceiver);
         prepareEnter();
         Bundle resultReceiverBundle = new Bundle();
         resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
         mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
+        getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                if (mIsReadyForTransition) {
+                    getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                }
+                return mIsReadyForTransition;
+            }
+        });
+    }
+
+    public void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
+        if (mIsReadyForTransition) {
+            return;
+        }
+        super.viewsReady(accepted, localNames);
+
+        mIsReadyForTransition = true;
         if (mIsReturning) {
             mHandler = new Handler() {
                 @Override
@@ -75,6 +94,13 @@
             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
             send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null);
         }
+        setViewVisibility(mSharedElements, View.INVISIBLE);
+        if (getViewsTransition() != null) {
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+        }
+        if (mSharedElementsBundle != null) {
+            onTakeSharedElements();
+        }
     }
 
     private void sendSharedElementDestination() {
@@ -94,9 +120,7 @@
         }
     }
 
-    private static SharedElementListener getListener(Activity activity,
-            ArrayList<String> acceptedNames) {
-        boolean isReturning = acceptedNames != null;
+    private static SharedElementListener getListener(Activity activity, boolean isReturning) {
         return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
     }
 
@@ -108,7 +132,8 @@
                     if (mHandler != null) {
                         mHandler.removeMessages(MSG_CANCEL);
                     }
-                    onTakeSharedElements(resultData);
+                    mSharedElementsBundle = resultData;
+                    onTakeSharedElements();
                 }
                 break;
             case MSG_EXIT_TRANSITION_COMPLETE:
@@ -139,7 +164,7 @@
             mSharedElementNames.clear();
             mSharedElements.clear();
             mAllSharedElementNames.clear();
-            onTakeSharedElements(null);
+            startSharedElementTransition(null);
             onRemoteExitTransitionComplete();
         }
     }
@@ -149,10 +174,6 @@
     }
 
     protected void prepareEnter() {
-        setViewVisibility(mSharedElements, View.INVISIBLE);
-        if (getViewsTransition() != null) {
-            setViewVisibility(mTransitioningViews, View.INVISIBLE);
-        }
         mActivity.overridePendingTransition(0, 0);
         if (!mIsReturning) {
             mActivity.convertToTranslucent(null, null);
@@ -185,7 +206,25 @@
         }
     }
 
-    protected void onTakeSharedElements(Bundle sharedElementState) {
+    protected void onTakeSharedElements() {
+        if (!mIsReadyForTransition || mSharedElementsBundle == null) {
+            return;
+        }
+        final Bundle sharedElementState = mSharedElementsBundle;
+        mSharedElementsBundle = null;
+        getDecor().getViewTreeObserver()
+                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                        startSharedElementTransition(sharedElementState);
+                        return false;
+                    }
+                });
+        getDecor().invalidate();
+    }
+
+    private void startSharedElementTransition(Bundle sharedElementState) {
         setEpicenter();
         // Remove rejected shared elements
         ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
@@ -299,8 +338,8 @@
     }
 
     public void stop() {
+        makeOpaque();
         mHasStopped = true;
-        mActivity = null;
         mIsCanceled = true;
         mResultReceiver = null;
         if (mBackgroundAnimator != null) {
@@ -310,7 +349,7 @@
     }
 
     private void makeOpaque() {
-        if (!mHasStopped) {
+        if (!mHasStopped && mActivity != null) {
             mActivity.convertFromTranslucent();
             mActivity = null;
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bd988a6..55c66ba 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6029,6 +6029,7 @@
 
         /**
          * Battery level [1-99] at which low power mode automatically turns on.
+         * If 0, it will not automatically turn on.
          * @hide
          */
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index fd475cd..8bd0f4d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -29,6 +29,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import java.util.List;
@@ -54,7 +55,7 @@
             + "[" + getClass().getSimpleName() + "]";
 
     private INotificationListenerWrapper mWrapper = null;
-    private Ranking mRanking;
+    private RankingMap mRankingMap;
 
     private INotificationManager mNoMan;
 
@@ -75,7 +76,22 @@
      *            object as well as its identifying information (tag and id) and source
      *            (package name).
      */
-    public abstract void onNotificationPosted(StatusBarNotification sbn);
+    public void onNotificationPosted(StatusBarNotification sbn) {
+        // optional
+    }
+
+    /**
+     * Implement this method to learn about new notifications as they are posted by apps.
+     *
+     * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+     *            object as well as its identifying information (tag and id) and source
+     *            (package name).
+     * @param rankingMap The current ranking map that can be used to retrieve ranking information
+     *                   for active notifications, including the newly posted one.
+     */
+    public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+        onNotificationPosted(sbn);
+    }
 
     /**
      * Implement this method to learn when notifications are removed.
@@ -94,7 +110,33 @@
      *            and source (package name) used to post the {@link android.app.Notification} that
      *            was just removed.
      */
-    public abstract void onNotificationRemoved(StatusBarNotification sbn);
+    public void onNotificationRemoved(StatusBarNotification sbn) {
+        // optional
+    }
+
+    /**
+     * Implement this method to learn when notifications are removed.
+     * <P>
+     * This might occur because the user has dismissed the notification using system UI (or another
+     * notification listener) or because the app has withdrawn the notification.
+     * <P>
+     * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
+     * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
+     * fields such as {@link android.app.Notification#contentView} and
+     * {@link android.app.Notification#largeIcon}. However, all other fields on
+     * {@link StatusBarNotification}, sufficient to match this call with a prior call to
+     * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
+     *
+     * @param sbn A data structure encapsulating at least the original information (tag and id)
+     *            and source (package name) used to post the {@link android.app.Notification} that
+     *            was just removed.
+     * @param rankingMap The current ranking map that can be used to retrieve ranking information
+     *                   for active notifications.
+     *
+     */
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+        onNotificationRemoved(sbn);
+    }
 
     /**
      * Implement this method to learn about when the listener is enabled and connected to
@@ -107,10 +149,11 @@
 
     /**
      * Implement this method to be notified when the notification ranking changes.
-     * <P>
-     * Call {@link #getCurrentRanking()} to retrieve the new ranking.
+     *
+     * @param rankingMap The current ranking map that can be used to retrieve ranking information
+     *                   for active notifications.
      */
-    public void onNotificationRankingUpdate() {
+    public void onNotificationRankingUpdate(RankingMap rankingMap) {
         // optional
     }
 
@@ -241,16 +284,19 @@
      *
      * <p>
      * The returned object represents the current ranking snapshot and only
-     * applies for currently active notifications. Hence you must retrieve a
-     * new Ranking after each notification event such as
-     * {@link #onNotificationPosted(StatusBarNotification)},
-     * {@link #onNotificationRemoved(StatusBarNotification)}, etc.
+     * applies for currently active notifications.
+     * <p>
+     * Generally you should use the RankingMap that is passed with events such
+     * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
+     * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
+     * so on. This method should only be used when needing access outside of
+     * such events, for example to retrieve the RankingMap right after
+     * initialization.
      *
-     * @return A {@link NotificationListenerService.Ranking} object providing
-     *     access to ranking information
+     * @return A {@link RankingMap} object providing access to ranking information
      */
-    public Ranking getCurrentRanking() {
-        return mRanking;
+    public RankingMap getCurrentRanking() {
+        return mRankingMap;
     }
 
     @Override
@@ -313,7 +359,7 @@
             synchronized (mWrapper) {
                 applyUpdate(update);
                 try {
-                    NotificationListenerService.this.onNotificationPosted(sbn);
+                    NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
                 } catch (Throwable t) {
                     Log.w(TAG, "Error running onNotificationPosted", t);
                 }
@@ -326,7 +372,7 @@
             synchronized (mWrapper) {
                 applyUpdate(update);
                 try {
-                    NotificationListenerService.this.onNotificationRemoved(sbn);
+                    NotificationListenerService.this.onNotificationRemoved(sbn, mRankingMap);
                 } catch (Throwable t) {
                     Log.w(TAG, "Error running onNotificationRemoved", t);
                 }
@@ -351,7 +397,7 @@
             synchronized (mWrapper) {
                 applyUpdate(update);
                 try {
-                    NotificationListenerService.this.onNotificationRankingUpdate();
+                    NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
                 } catch (Throwable t) {
                     Log.w(TAG, "Error running onNotificationRankingUpdate", t);
                 }
@@ -360,7 +406,65 @@
     }
 
     private void applyUpdate(NotificationRankingUpdate update) {
-        mRanking = new Ranking(update);
+        mRankingMap = new RankingMap(update);
+    }
+
+    /**
+     * Provides access to ranking information on a currently active
+     * notification.
+     *
+     * <p>
+     * Note that this object is not updated on notification events (such as
+     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
+     * {@link #onNotificationRemoved(StatusBarNotification)}, etc.). Make sure
+     * to retrieve a new Ranking from the current {@link RankingMap} whenever
+     * a notification event occurs.
+     */
+    public static class Ranking {
+        private final String mKey;
+        private final int mRank;
+        private final boolean mIsAmbient;
+        private final boolean mIsInterceptedByDnd;
+
+        private Ranking(String key, int rank, boolean isAmbient, boolean isInterceptedByDnd) {
+            mKey = key;
+            mRank = rank;
+            mIsAmbient = isAmbient;
+            mIsInterceptedByDnd = isInterceptedByDnd;
+        }
+
+        /**
+         * Returns the key of the notification this Ranking applies to.
+         */
+        public String getKey() {
+            return mKey;
+        }
+
+        /**
+         * Returns the rank of the notification.
+         *
+         * @return the rank of the notification, that is the 0-based index in
+         *     the list of active notifications.
+         */
+        public int getRank() {
+            return mRank;
+        }
+
+        /**
+         * Returns whether the notification is an ambient notification, that is
+         * a notification that doesn't require the user's immediate attention.
+         */
+        public boolean isAmbient() {
+            return mIsAmbient;
+        }
+
+        /**
+         * Returns whether the notification was intercepted by
+         * &quot;Do not disturb&quot;.
+         */
+        public boolean isInterceptedByDoNotDisturb() {
+            return mIsInterceptedByDnd;
+        }
     }
 
     /**
@@ -371,11 +475,14 @@
      * Note that this object represents a ranking snapshot that only applies to
      * notifications active at the time of retrieval.
      */
-    public static class Ranking implements Parcelable {
+    public static class RankingMap implements Parcelable {
         private final NotificationRankingUpdate mRankingUpdate;
+        private final ArrayMap<String, Ranking> mRankingCache;
+        private boolean mRankingCacheInitialized;
 
-        private Ranking(NotificationRankingUpdate rankingUpdate) {
+        private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
+            mRankingCache = new ArrayMap<>(rankingUpdate.getOrderedKeys().length);
         }
 
         /**
@@ -389,56 +496,37 @@
         }
 
         /**
-         * Returns the rank of the notification with the given key, that is the
-         * index of <code>key</code> in the array of keys returned by
-         * {@link #getOrderedKeys()}.
+         * Returns the Ranking for the notification with the given key.
          *
-         * @return The rank of the notification with the given key; -1 when the
-         *      given key is unknown.
+         * @return the Ranking of the notification with the given key;
+         *     <code>null</code> when the key is unknown.
          */
-        public int getRank(String key) {
-            // TODO: Optimize.
+        public Ranking getRanking(String key) {
+            synchronized (mRankingCache) {
+                if (!mRankingCacheInitialized) {
+                    initializeRankingCache();
+                    mRankingCacheInitialized = true;
+                }
+            }
+            return mRankingCache.get(key);
+        }
+
+        private void initializeRankingCache() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
-            for (int i = 0; i < orderedKeys.length; i++) {
-                if (orderedKeys[i].equals(key)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        /**
-         * Returns whether the notification with the given key was intercepted
-         * by &quot;Do not disturb&quot;.
-         */
-        public boolean isInterceptedByDoNotDisturb(String key) {
-            // TODO: Optimize.
-            for (String interceptedKey : mRankingUpdate.getDndInterceptedKeys()) {
-                if (interceptedKey.equals(key)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns whether the notification with the given key is an ambient
-         * notification, that is a notification that doesn't require the user's
-         * immediate attention.
-         */
-        public boolean isAmbient(String key) {
-            // TODO: Optimize.
             int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex();
-            if (firstAmbientIndex < 0) {
-                return false;
-            }
-            String[] orderedKeys = mRankingUpdate.getOrderedKeys();
-            for (int i = firstAmbientIndex; i < orderedKeys.length; i++) {
-                if (orderedKeys[i].equals(key)) {
-                    return true;
+            for (int i = 0; i < orderedKeys.length; i++) {
+                String key = orderedKeys[i];
+                boolean isAmbient = firstAmbientIndex > -1 && firstAmbientIndex <= i;
+                boolean isInterceptedByDnd = false;
+                // TODO: Optimize.
+                for (String s : mRankingUpdate.getDndInterceptedKeys()) {
+                    if (s.equals(key)) {
+                        isInterceptedByDnd = true;
+                        break;
+                    }
                 }
+                mRankingCache.put(key, new Ranking(key, i, isAmbient, isInterceptedByDnd));
             }
-            return false;
         }
 
         // ----------- Parcelable
@@ -453,16 +541,16 @@
             dest.writeParcelable(mRankingUpdate, flags);
         }
 
-        public static final Creator<Ranking> CREATOR = new Creator<Ranking>() {
+        public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
             @Override
-            public Ranking createFromParcel(Parcel source) {
+            public RankingMap createFromParcel(Parcel source) {
                 NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
-                return new Ranking(rankingUpdate);
+                return new RankingMap(rankingUpdate);
             }
 
             @Override
-            public Ranking[] newArray(int size) {
-                return new Ranking[size];
+            public RankingMap[] newArray(int size) {
+                return new RankingMap[size];
             }
         };
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 11db996..72b9d3e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -74,7 +74,8 @@
     private boolean mProfilingEnabled;
 
     ThreadedRenderer(boolean translucent) {
-        AtlasInitializer.sInstance.init();
+        // Temporarily disabled
+        //AtlasInitializer.sInstance.init();
 
         long rootNodePtr = nCreateRootRenderNode();
         mRootNode = RenderNode.adopt(rootNodePtr);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8768779..c5fd83d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2688,7 +2688,8 @@
                 android:theme="@style/Theme.Holo.Dialog.Alert"
                 android:finishOnCloseSystemDialogs="true"
                 android:excludeFromRecents="true"
-                android:multiprocess="true">
+                android:multiprocess="true"
+                android:documentLaunchMode="never">
             <intent-filter>
                 <action android:name="android.intent.action.CHOOSER" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 59d01de..27ac6c30 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -595,10 +595,11 @@
     <integer name="config_shutdownBatteryTemperature">680</integer>
 
     <!-- Display low battery warning when battery level dips to this value -->
-    <integer name="config_lowBatteryWarningLevel">20</integer>
+    <integer name="config_lowBatteryWarningLevel">15</integer>
 
-    <!-- Close low battery warning when battery level reaches this value -->
-    <integer name="config_lowBatteryCloseWarningLevel">25</integer>
+    <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
+         plus this -->
+    <integer name="config_lowBatteryCloseWarningBump">5</integer>
 
     <!-- Default color for notification LED. -->
     <color name="config_defaultNotificationColor">#ffffffff</color>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 853df6e..d8e31ea 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1509,7 +1509,7 @@
   <java-symbol type="integer" name="config_defaultNotificationLedOn" />
   <java-symbol type="integer" name="config_deskDockKeepsScreenOn" />
   <java-symbol type="integer" name="config_lightSensorWarmupTime" />
-  <java-symbol type="integer" name="config_lowBatteryCloseWarningLevel" />
+  <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
   <java-symbol type="integer" name="config_lowBatteryWarningLevel" />
   <java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
   <java-symbol type="integer" name="config_networkTransitionTimeout" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
deleted file mode 100644
index 5ce8708..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
deleted file mode 100644
index bdf0f67..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png
deleted file mode 100644
index 461535c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png
deleted file mode 100644
index 7b03a11..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_05.xml b/packages/SystemUI/res/drawable/ic_notify_zen.xml
similarity index 61%
copy from packages/SystemUI/res/drawable/ic_qs_location_05.xml
copy to packages/SystemUI/res/drawable/ic_notify_zen.xml
index 1a21e2f..c46455b 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_05.xml
+++ b/packages/SystemUI/res/drawable/ic_notify_zen.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="24dp"
+        android:height="24dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M12.0,13.0c-1.74,0.0 -3.15,1.41 -3.15,3.15C8.85,18.51 12.0,22.0 12.0,22.0s3.15,-3.49 3.15,-5.85C15.15,14.41 13.74,13.0 12.0,13.0zM12.0,17.27c-0.62,0.0 -1.13,-0.5 -1.13,-1.12c0.0,-0.62 0.5,-1.12 1.13,-1.12c0.62,0.0 1.12,0.5 1.12,1.12C13.12,16.77 12.62,17.27 12.0,17.27z"/>
+        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml b/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml
deleted file mode 100644
index dc30a53..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
new file mode 100644
index 0000000..b6a5cad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
@@ -0,0 +1,28 @@
+<!--
+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="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#4DFFFFFF"
+        android:pathData="M41.3,41.7L36.6,37.0L24.0,24.5l-7.1,-7.1L14.0,14.5L8.5,9.0L6.0,11.5l5.6,5.6c-5.1,6.3 -4.7,15.5 1.1,21.4c3.1,3.1 7.2,4.7 11.3,4.7c3.6,0.0 7.1,-1.2 10.1,-3.6l5.4,5.4l2.5,-2.5L41.3,41.7zM24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5c0.0,-2.6 0.9,-5.1 2.4,-7.2l9.6,9.6L24.0,39.2zM24.0,10.2l0.0,9.2l14.5,14.5c2.7,-5.9 1.7,-13.1 -3.2,-18.0L24.0,4.5l0.0,0.0l0.0,0.0L16.6,12.0l2.8,2.8L24.0,10.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_05.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
similarity index 68%
rename from packages/SystemUI/res/drawable/ic_qs_location_05.xml
rename to packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
index 1a21e2f..e8d59e0 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_05.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
@@ -19,10 +19,10 @@
         android:height="64dp"/>
 
     <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
 
     <path
         android:fill="#FFFFFFFF"
-        android:pathData="M12.0,13.0c-1.74,0.0 -3.15,1.41 -3.15,3.15C8.85,18.51 12.0,22.0 12.0,22.0s3.15,-3.49 3.15,-5.85C15.15,14.41 13.74,13.0 12.0,13.0zM12.0,17.27c-0.62,0.0 -1.13,-0.5 -1.13,-1.12c0.0,-0.62 0.5,-1.12 1.13,-1.12c0.62,0.0 1.12,0.5 1.12,1.12C13.12,16.77 12.62,17.27 12.0,17.27z"/>
+        android:pathData="M35.3,15.9L24.0,4.5l0.0,0.0l0.0,0.0L12.7,15.9c-6.2,6.2 -6.2,16.4 0.0,22.6c3.1,3.1 7.2,4.7 11.3,4.7s8.2,-1.6 11.3,-4.7C41.6,32.2 41.6,22.1 35.3,15.9zM24.0,39.2L24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5s1.2,-6.2 3.5,-8.5l8.5,-8.5L24.0,39.2z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_01.xml b/packages/SystemUI/res/drawable/ic_qs_location_01.xml
deleted file mode 100644
index ff37d9a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_01.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.0,2.0C8.13,2.0 5.0,5.13 5.0,9.0c0.0,5.25 7.0,13.0 7.0,13.0s7.0,-7.75 7.0,-13.0C19.0,5.13 15.87,2.0 12.0,2.0zM12.0,11.5c-1.38,0.0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5c1.38,0.0 2.5,1.12 2.5,2.5S13.38,11.5 12.0,11.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_02.xml b/packages/SystemUI/res/drawable/ic_qs_location_02.xml
deleted file mode 100644
index bb4465f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_02.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.0,4.0c-3.48,0.0 -6.3,2.82 -6.3,6.3C5.7,15.02 12.0,22.0 12.0,22.0s6.3,-6.98 6.3,-11.7C18.3,6.82 15.48,4.0 12.0,4.0zM12.0,12.55c-1.24,0.0 -2.25,-1.01 -2.25,-2.25S10.76,8.05 12.0,8.05c1.24,0.0 2.25,1.01 2.25,2.25S13.24,12.55 12.0,12.55z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_03.xml b/packages/SystemUI/res/drawable/ic_qs_location_03.xml
deleted file mode 100644
index 956a8c3..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_03.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.0,7.0c-2.9,0.0 -5.25,2.35 -5.25,5.25C6.75,16.19 12.0,22.0 12.0,22.0s5.25,-5.81 5.25,-9.75C17.25,9.35 14.9,7.0 12.0,7.0zM12.0,14.12c-1.04,0.0 -1.88,-0.84 -1.88,-1.88s0.84,-1.88 1.88,-1.88c1.04,0.0 1.87,0.84 1.87,1.88S13.04,14.12 12.0,14.12z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_04.xml b/packages/SystemUI/res/drawable/ic_qs_location_04.xml
deleted file mode 100644
index 0c0fb3b..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_04.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.0,10.0c-2.32,0.0 -4.2,1.88 -4.2,4.2C7.8,17.35 12.0,22.0 12.0,22.0s4.2,-4.65 4.2,-7.8C16.2,11.88 14.32,10.0 12.0,10.0zM12.0,15.7c-0.83,0.0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5c0.83,0.0 1.5,0.67 1.5,1.5S12.83,15.7 12.0,15.7z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_06.xml b/packages/SystemUI/res/drawable/ic_qs_location_06.xml
deleted file mode 100644
index 5642a8a2..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_06.xml
+++ /dev/null
@@ -1,31 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.0,16.0c-1.16,0.0 -2.1,0.94 -2.1,2.1C9.9,19.67 12.0,22.0 12.0,22.0s2.1,-2.33 2.1,-3.9C14.1,16.94 13.16,16.0 12.0,16.0zM12.0,18.85c-0.41,0.0 -0.75,-0.34 -0.75,-0.75s0.34,-0.75 0.75,-0.75c0.41,0.0 0.75,0.34 0.75,0.75S12.41,18.85 12.0,18.85z"/>
-    <path
-        android:pathData="M11.99,15c-1.35,0,-2.45,1.1,-2.45,2.45      c0,1.84,2.45,4.55,2.45,4.55s2.45,-2.71,2.45,-4.55C14.44,16.1,13.34,15,11.99,15z M11.99,18.33c-0.48,0,-0.88,-0.39,-0.88,-0.88      s0.39,-0.88,0.88,-0.88c0.48,0,0.87,0.39,0.87,0.88S12.47,18.33,11.99,18.33z"
-        android:fill="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_07.xml b/packages/SystemUI/res/drawable/ic_qs_location_07.xml
deleted file mode 100644
index 1ad2ebc..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_07.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M12,9c-2.51,0,-4.55,2.04,-4.55,4.55       C7.45,16.96,12,22,12,22s4.55,-5.04,4.55,-8.45C16.55,11.04,14.51,9,12,9z M12,15.18c-0.9,0,-1.63,-0.73,-1.63,-1.62       s0.73,-1.62,1.63,-1.62c0.9,0,1.62,0.73,1.62,1.62S12.9,15.18,12,15.18z"
-        android:fill="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_08.xml b/packages/SystemUI/res/drawable/ic_qs_location_08.xml
deleted file mode 100644
index 179bc66..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_08.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M12,6c-3.09,0,-5.6,2.51,-5.6,5.6       C6.4,15.8,12,22,12,22s5.6,-6.2,5.6,-10.4C17.6,8.51,15.09,6,12,6z M12,13.6c-1.1,0,-2,-0.9,-2,-2s0.9,-2,2,-2c1.1,0,2,0.9,2,2       S13.1,13.6,12,13.6z"
-        android:fill="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_09.xml b/packages/SystemUI/res/drawable/ic_qs_location_09.xml
deleted file mode 100644
index 6169af5..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_09.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M12,4c-3.48,0,-6.3,2.82,-6.3,6.3       C5.7,15.02,12,22,12,22s6.3,-6.98,6.3,-11.7C18.3,6.82,15.48,4,12,4z M12,12.55c-1.24,0,-2.25,-1.01,-2.25,-2.25S10.76,8.05,12,8.05       c1.24,0,2.25,1.01,2.25,2.25S13.24,12.55,12,12.55z"
-        android:fill="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_10.xml b/packages/SystemUI/res/drawable/ic_qs_location_10.xml
deleted file mode 100644
index 93e2eb4..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_10.xml
+++ /dev/null
@@ -1,28 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M12,3C8.33,3,5.35,5.98,5.35,9.65       C5.35,14.64,12,22,12,22s6.65,-7.36,6.65,-12.35C18.65,5.98,15.67,3,12,3z M12,12.02c-1.31,0,-2.38,-1.06,-2.38,-2.38       S10.69,7.28,12,7.28c1.31,0,2.37,1.06,2.37,2.37S13.31,12.02,12,12.02z"
-        android:fill="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_off.xml b/packages/SystemUI/res/drawable/ic_qs_location_off.xml
index d28d347..26ebfbf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_location_off.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2014 The Android Open Source Project
 
@@ -14,18 +13,19 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" />
-</animation-list>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#4DFFFFFF"
+        android:pathData="M24.0,13.0c2.8,0.0 5.0,2.2 5.0,5.0c0.0,1.5 -0.7,2.8 -1.7,3.7l7.3,7.3c2.0,-3.7 3.4,-7.6 3.4,-11.0c0.0,-7.7 -6.3,-14.0 -14.0,-14.0c-4.0,0.0 -7.5,1.6 -10.1,4.3l6.4,6.4C21.2,13.6 22.5,13.0 24.0,13.0zM32.7,32.2l-9.3,-9.3l-0.2,-0.2L6.5,6.0L4.0,8.5l6.4,6.4c-0.2,1.0 -0.4,2.0 -0.4,3.1c0.0,10.5 14.0,26.0 14.0,26.0s3.3,-3.7 6.8,-8.7l6.7,6.7l2.5,-2.5L32.7,32.2z"/>
+    <path
+        android:pathData="M23.5,22.9l0.0,0.0 -0.20000076,-0.19999886z"
+        android:fill="#4DFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_on.xml b/packages/SystemUI/res/drawable/ic_qs_location_on.xml
index 72512ac..bc73005 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_location_on.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2014 The Android Open Source Project
 
@@ -14,18 +13,16 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" />
-</animation-list>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M24.0,4.0c-7.7,0.0 -14.0,6.3 -14.0,14.0c0.0,10.5 14.0,26.0 14.0,26.0s14.0,-15.5 14.0,-26.0C38.0,10.3 31.7,4.0 24.0,4.0zM24.0,23.0c-2.8,0.0 -5.0,-2.2 -5.0,-5.0s2.2,-5.0 5.0,-5.0c2.8,0.0 5.0,2.2 5.0,5.0S26.8,23.0 24.0,23.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_11.xml b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml
similarity index 71%
rename from packages/SystemUI/res/drawable/ic_qs_location_11.xml
rename to packages/SystemUI/res/drawable/stat_sys_no_sim.xml
index 09a3e63..70948b7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_11.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml
@@ -15,14 +15,14 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="64dp"
-        android:height="64dp"/>
+        android:width="18dp"
+        android:height="18dp"/>
 
     <viewport
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"/>
 
     <path
-        android:pathData="M12,2C8.13,2,5,5.13,5,9c0,5.25,7,13,7,13s7,-7.75,7,-13       C19,5.13,15.87,2,12,2z M12,11.5c-1.38,0,-2.5,-1.12,-2.5,-2.5s1.12,-2.5,2.5,-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,11.5,12,11.5z"
-        android:fill="#4DFFFFFF"/>
+        android:fill="#4DFFFFFF"
+        android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/>
 </vector>
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0fb0f8b..192ba57 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -17,13 +17,16 @@
 package com.android.systemui.power;
 
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
 import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
@@ -54,17 +57,22 @@
 
     public void start() {
 
-        mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
-        mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryWarningLevel);
-        mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-
         final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
         mWarnings = new PowerDialogWarnings(mContext);
 
+        ContentObserver obs = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateBatteryWarningLevels();
+            }
+        };
+        final ContentResolver resolver = mContext.getContentResolver();
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                false, obs, UserHandle.USER_ALL);
+        updateBatteryWarningLevels();
+
         // Register for Intent broadcasts for...
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -73,6 +81,29 @@
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
     }
 
+    void updateBatteryWarningLevels() {
+        int critLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+
+        final ContentResolver resolver = mContext.getContentResolver();
+        int defWarnLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        int warnLevel = Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
+        if (warnLevel == 0) {
+            warnLevel = defWarnLevel;
+        }
+        if (warnLevel < critLevel) {
+            warnLevel = critLevel;
+        }
+
+        mLowBatteryReminderLevels[0] = warnLevel;
+        mLowBatteryReminderLevels[1] = critLevel;
+        mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
+                + mContext.getResources().getInteger(
+                        com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
+    }
+
     /**
      * Buckets the battery level.
      *
@@ -87,7 +118,7 @@
         if (level >= mLowBatteryAlertCloseLevel) {
             return 1;
         }
-        if (level >= mLowBatteryReminderLevels[0]) {
+        if (level > mLowBatteryReminderLevels[0]) {
             return 0;
         }
         final int N = mLowBatteryReminderLevels.length;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index c76ee8c..786cd9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -287,6 +287,7 @@
         public boolean activityIn;
         public boolean activityOut;
         public int overlayIconId;
+        public boolean filter;
 
         @Override
         public boolean copyTo(State other) {
@@ -300,6 +301,7 @@
             o.activityIn = activityIn;
             o.activityOut = activityOut;
             o.overlayIconId = overlayIconId;
+            o.filter = filter;
             return super.copyTo(other) || changed;
         }
 
@@ -311,6 +313,7 @@
             rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
             rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
             rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
+            rt.insert(rt.length() - 1, ",filter=" + filter);
             return rt;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index 901cc10..d5fe033 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -93,12 +93,12 @@
         final SignalState s = (SignalState) state;
         mSignal.setImageDrawable(null);  // force refresh
         mSignal.setImageResource(s.iconId);
-        mSignal.setColorFilter(FILTER);
+        mSignal.setColorFilter(s.filter ? FILTER : null);
         if (s.overlayIconId > 0) {
             mOverlay.setVisibility(VISIBLE);
             mOverlay.setImageDrawable(null);  // force refresh
             mOverlay.setImageResource(s.overlayIconId);
-            mOverlay.setColorFilter(FILTER);
+            mOverlay.setColorFilter(s.filter ? FILTER : null);
         } else {
             mOverlay.setVisibility(GONE);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 182a0ce..6d91d33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -72,12 +72,15 @@
         if (cb == null) return;
 
         final Resources r = mContext.getResources();
-        state.iconId = cb.enabled && (cb.mobileSignalIconId > 0)
+        state.iconId = cb.noSim
+                ? R.drawable.stat_sys_no_sim
+                : cb.enabled && (cb.mobileSignalIconId > 0)
                 ? cb.mobileSignalIconId
                 : R.drawable.ic_qs_signal_no_signal;
         state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled
                 ? cb.dataTypeIconId
                 : 0;
+        state.filter = state.iconId != R.drawable.stat_sys_no_sim;
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
 
@@ -117,6 +120,7 @@
         boolean activityIn;
         boolean activityOut;
         String enabledDesc;
+        boolean noSim;
     }
 
     private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
@@ -134,7 +138,7 @@
                 int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description) {
+                String dataTypeContentDescriptionId, String description, boolean noSim) {
             final CallbackInfo info = new CallbackInfo();  // TODO pool?
             info.enabled = enabled;
             info.wifiEnabled = mWifiEnabled;
@@ -145,6 +149,7 @@
             info.activityIn = activityIn;
             info.activityOut = activityOut;
             info.enabledDesc = description;
+            info.noSim = noSim;
             refreshState(info);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 5301362..7c2c7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -71,6 +71,6 @@
         state.visible = mVisible;
         state.value = enabled;
         state.label = mContext.getString(R.string.quick_settings_inversion_label);
-        state.iconId = R.drawable.ic_qs_color_inversion;
+        state.iconId = enabled ? R.drawable.ic_qs_inversion_on : R.drawable.ic_qs_inversion_off;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index db9b054..04f1eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.qs.tiles;
 
-import android.content.res.Resources;
-import android.graphics.drawable.AnimationDrawable;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.LocationController;
@@ -63,28 +60,15 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         final boolean locationEnabled =  mController.isLocationEnabled();
         state.visible = true;
-        if (state.value != locationEnabled) {
-            state.value = locationEnabled;
-            final Resources res = mContext.getResources();
-            final AnimationDrawable d = (AnimationDrawable) res.getDrawable(locationEnabled
-                    ? R.drawable.ic_qs_location_on
-                    : R.drawable.ic_qs_location_off);
-            state.icon = d;
-            mUiHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    d.start();
-                }
-            });
-        }
+        state.value = locationEnabled;
         if (locationEnabled) {
-            if (state.icon == null) state.iconId = R.drawable.ic_qs_location_01;
+            state.iconId = R.drawable.ic_qs_location_on;
             state.label = mContext.getString(R.string.quick_settings_location_label);
             state.contentDescription = mContext.getString(
                     R.string.accessibility_quick_settings_location,
                     mContext.getString(R.string.accessibility_desc_on));
         } else {
-            if (state.icon == null) state.iconId = R.drawable.ic_qs_location_11;
+            state.iconId = R.drawable.ic_qs_location_off;
             state.label = mContext.getString(R.string.quick_settings_location_label);
             state.contentDescription = mContext.getString(
                     R.string.accessibility_quick_settings_location,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 6b73002..a236497 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -87,6 +87,7 @@
         state.connected = wifiConnected;
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
+        state.filter = true;
         final String signalContentDescription;
         final Resources r = mContext.getResources();
         if (wifiConnected) {
@@ -159,7 +160,7 @@
                 int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description) {
+                String dataTypeContentDescriptionId, String description, boolean noSim) {
             // noop
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 508c34e..5a9fe91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,7 +48,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -80,11 +80,10 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
+import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.Locale;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -106,6 +105,7 @@
     protected static final int MSG_SHOW_HEADS_UP = 1026;
     protected static final int MSG_HIDE_HEADS_UP = 1027;
     protected static final int MSG_ESCALATE_HEADS_UP = 1028;
+    protected static final int MSG_DECAY_HEADS_UP = 1029;
 
     protected static final boolean ENABLE_HEADS_UP = true;
     // scores above this threshold should be displayed in heads up mode.
@@ -129,7 +129,9 @@
     protected NotificationData mNotificationData = new NotificationData();
     protected NotificationStackScrollLayout mStackScroller;
 
-    protected NotificationData.Entry mInterruptingNotificationEntry;
+    // for heads up notifications
+    protected HeadsUpNotificationView mHeadsUpNotificationView;
+    protected int mHeadsUpNotificationDecay;
     protected long mInterruptingNotificationTime;
 
     // used to notify status bar for suppressing notification LED
@@ -291,7 +293,7 @@
         public void onListenerConnected() {
             if (DEBUG) Log.d(TAG, "onListenerConnected");
             final StatusBarNotification[] notifications = getActiveNotifications();
-            final Ranking currentRanking = getCurrentRanking();
+            final RankingMap currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -303,41 +305,40 @@
         }
 
         @Override
-        public void onNotificationPosted(final StatusBarNotification sbn) {
+        public void onNotificationPosted(final StatusBarNotification sbn,
+                final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
-            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     if (mNotificationData.findByKey(sbn.getKey()) != null) {
-                        updateNotificationInternal(sbn, currentRanking);
+                        updateNotificationInternal(sbn, rankingMap);
                     } else {
-                        addNotificationInternal(sbn, currentRanking);
+                        addNotificationInternal(sbn, rankingMap);
                     }
                 }
             });
         }
 
         @Override
-        public void onNotificationRemoved(final StatusBarNotification sbn) {
+        public void onNotificationRemoved(final StatusBarNotification sbn,
+                final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
-            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    removeNotificationInternal(sbn.getKey(), currentRanking);
+                    removeNotificationInternal(sbn.getKey(), rankingMap);
                 }
             });
         }
 
         @Override
-        public void onNotificationRankingUpdate() {
+        public void onNotificationRankingUpdate(final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onRankingUpdate");
-            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    updateRankingInternal(currentRanking);
+                    updateRankingInternal(rankingMap);
                 }
             });
         }
@@ -505,8 +506,8 @@
 
     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
         View vetoButton = row.findViewById(R.id.veto);
-        if (n.isClearable() || (mInterruptingNotificationEntry != null
-                && mInterruptingNotificationEntry.row == row)) {
+        if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
+                && mHeadsUpNotificationView.getEntry().row == row)) {
             final String _pkg = n.getPackageName();
             final String _tag = n.getTag();
             final int _id = n.getId();
@@ -765,6 +766,12 @@
 
     public abstract void resetHeadsUpDecayTimer();
 
+    public abstract void scheduleHeadsUpOpen();
+
+    public abstract void scheduleHeadsUpClose();
+
+    public abstract void scheduleHeadsUpEscalation();
+
     /**
      * Save the current "public" (locked and secure) state of the lockscreen.
      */
@@ -796,6 +803,18 @@
         return mUsersAllowingPrivateNotifications.get(userHandle);
     }
 
+    public void onNotificationClear(StatusBarNotification notification) {
+        try {
+            mBarService.onNotificationClear(
+                    notification.getPackageName(),
+                    notification.getTag(),
+                    notification.getId(),
+                    notification.getUserId());
+        } catch (android.os.RemoteException ex) {
+            // oh well
+        }
+    }
+
     protected class H extends Handler {
         public void handleMessage(Message m) {
             Intent intent;
@@ -1101,7 +1120,7 @@
 
                     try {
                         if (mIsHeadsUp) {
-                            mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+                            mHeadsUpNotificationView.clear();
                         }
                         mBarService.onNotificationClick(mNotificationKey);
                     } catch (RemoteException ex) {
@@ -1157,7 +1176,7 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+    protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
@@ -1197,7 +1216,7 @@
         return entry;
     }
 
-    protected void addNotificationViews(Entry entry, Ranking ranking) {
+    protected void addNotificationViews(Entry entry, RankingMap ranking) {
         if (entry == null) {
             return;
         }
@@ -1206,7 +1225,7 @@
         updateNotifications();
     }
 
-    private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+    private void addNotificationViews(StatusBarNotification notification, RankingMap ranking) {
         addNotificationViews(createNotificationViews(notification), ranking);
     }
 
@@ -1292,9 +1311,9 @@
     }
 
     public abstract void addNotificationInternal(StatusBarNotification notification,
-            Ranking ranking);
+            RankingMap ranking);
 
-    protected abstract void updateRankingInternal(Ranking ranking);
+    protected abstract void updateRankingInternal(RankingMap ranking);
 
     @Override
     public void removeNotification(String key) {
@@ -1303,7 +1322,7 @@
         }
     }
 
-    public abstract void removeNotificationInternal(String key, Ranking ranking);
+    public abstract void removeNotificationInternal(String key, RankingMap ranking);
 
     public void updateNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
@@ -1311,7 +1330,7 @@
         }
     }
 
-    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+    public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1394,15 +1413,15 @@
             try {
                 updateNotificationViews(oldEntry, notification);
 
-                if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
-                        && oldNotification == mInterruptingNotificationEntry.notification) {
+                if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
+                        && oldNotification == mHeadsUpNotificationView.getEntry().notification) {
                     if (!shouldInterrupt(notification)) {
                         if (DEBUG) Log.d(TAG, "no longer interrupts!");
-                        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+                        scheduleHeadsUpClose();
                     } else {
                         if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
-                        mInterruptingNotificationEntry.notification = notification;
-                        updateHeadsUpViews(mInterruptingNotificationEntry, notification);
+                        mHeadsUpNotificationView.getEntry().notification = notification;
+                        updateHeadsUpViews(mHeadsUpNotificationView.getEntry(), notification);
                     }
                 }
 
@@ -1511,8 +1530,8 @@
     }
 
     protected void notifyHeadsUpScreenOn(boolean screenOn) {
-        if (!screenOn && mInterruptingNotificationEntry != null) {
-            mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
+        if (!screenOn) {
+            scheduleHeadsUpEscalation();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 4233ab8..bfa74fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -21,6 +21,7 @@
 import android.os.Process;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -58,17 +59,18 @@
         updateSyntheticNotification();
     }
 
-    public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) {
-        if (ranking == null) return false;
+    public boolean tryIntercept(StatusBarNotification notification, RankingMap rankingMap) {
+        if (rankingMap == null) return false;
         if (shouldDisplayIntercepted()) return false;
         if (mReleased.contains(notification.getKey())) return false;
-        if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false;
+        Ranking ranking = rankingMap.getRanking(notification.getKey());
+        if (!ranking.isInterceptedByDoNotDisturb()) return false;
         mIntercepted.put(notification.getKey(), notification);
         updateSyntheticNotification();
         return true;
     }
 
-    public void retryIntercepts(Ranking ranking) {
+    public void retryIntercepts(RankingMap ranking) {
         if (ranking == null) return;
 
         final int N = mIntercepted.size();
@@ -111,7 +113,7 @@
             return;
         }
         final Notification n = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.ic_qs_zen_on)
+                .setSmallIcon(R.drawable.ic_notify_zen)
                 .setContentTitle(mContext.getResources().getQuantityString(
                         R.plurals.zen_mode_notification_title,
                         mIntercepted.size(), mIntercepted.size()))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 631e19c..c313c58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,6 +18,7 @@
 
 import android.app.Notification;
 import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 
@@ -70,12 +71,16 @@
     }
 
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
-    private Ranking mRanking;
+    private RankingMap mRanking;
     private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
         @Override
         public int compare(Entry a, Entry b) {
             if (mRanking != null) {
-                return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+                Ranking aRanking = mRanking.getRanking(a.key);
+                Ranking bRanking = mRanking.getRanking(b.key);
+                int aRank = aRanking != null ? aRanking.getRank() : -1;
+                int bRank = bRanking != null ? bRanking.getRank() : -1;
+                return aRank - bRank;
             }
 
             final StatusBarNotification na = a.notification;
@@ -108,12 +113,12 @@
         return null;
     }
 
-    public void add(Entry entry, Ranking ranking) {
+    public void add(Entry entry, RankingMap ranking) {
         mEntries.add(entry);
         updateRankingAndSort(ranking);
     }
 
-    public Entry remove(String key, Ranking ranking) {
+    public Entry remove(String key, RankingMap ranking) {
         Entry e = findByKey(key);
         if (e == null) {
             return null;
@@ -123,7 +128,7 @@
         return e;
     }
 
-    public void updateRanking(Ranking ranking) {
+    public void updateRanking(RankingMap ranking) {
         updateRankingAndSort(ranking);
     }
 
@@ -137,12 +142,13 @@
                 }
             }
         } else {
-            return mRanking.isAmbient(key);
+            Ranking ranking = mRanking.getRanking(key);
+            return ranking != null && ranking.isAmbient();
         }
         return false;
     }
 
-    private void updateRankingAndSort(Ranking ranking) {
+    private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRanking = ranking;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
index 086a266..e312d58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -423,6 +423,7 @@
             return;
         }
         if (!animate) {
+            view.animate().cancel();
             view.setAlpha(alpha);
             view.setScaleX(scale);
             view.setScaleY(scale);
@@ -465,6 +466,13 @@
     }
 
     public void reset() {
+        if (mSwipeAnimator != null) {
+            mSwipeAnimator.cancel();
+        }
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View view : targetViews) {
+            view.animate().cancel();
+        }
         setTranslation(0.0f, true);
         mSwipingInProgress = false;
     }
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 772d0e7..1f3098d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
@@ -67,6 +68,11 @@
     private VelocityTrackerInterface mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
 
+    /**
+     * Whether an instant expand request is currently pending and we are just waiting for layout.
+     */
+    private boolean mInstantExpanding;
+
     PanelBar mBar;
 
     protected int mMaxPanelHeight = -1;
@@ -128,6 +134,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mInstantExpanding) {
+            return false;
+        }
 
         /*
          * We capture touch events here and update the expand height here in case according to
@@ -263,6 +272,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mInstantExpanding) {
+            return false;
+        }
 
         /*
          * If the user drags anywhere inside the panel we intercept it if he moves his finger
@@ -556,6 +568,41 @@
         }
     }
 
+    public void instantExpand() {
+        mInstantExpanding = true;
+        abortAnimations();
+        if (mTracking) {
+            onTrackingStopped(true /* expands */); // The panel is expanded after this call.
+            onExpandingFinished();
+        }
+        setVisibility(VISIBLE);
+
+        // Wait for window manager to pickup the change, so we know the maximum height of the panel
+        // then.
+        getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (mStatusBar.getStatusBarWindow().getHeight()
+                                != mStatusBar.getStatusBarHeight()) {
+                            getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                            setExpandedFraction(1f);
+                            mInstantExpanding = false;
+                        }
+                    }
+                });
+
+        // Make sure a layout really happens.
+        requestLayout();
+    }
+
+    private void abortAnimations() {
+        cancelPeek();
+        if (mHeightAnimator != null) {
+            mHeightAnimator.cancel();
+        }
+    }
+
     protected void startUnlockHintAnimation() {
 
         // We don't need to hint the user if an animation is already running or the user is changing
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 66770b0..d413f63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,7 +64,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
@@ -279,10 +279,6 @@
     // the date view
     DateView mDateView;
 
-    // for heads up notifications
-    private HeadsUpNotificationView mHeadsUpNotificationView;
-    private int mHeadsUpNotificationDecay;
-
     // on-screen navigation buttons
     private NavigationBarView mNavigationBarView = null;
     private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -368,7 +364,7 @@
                 if (!mUseHeadsUp) {
                     Log.d(TAG, "dismissing any existing heads up notification on disable event");
                     setHeadsUpVisibility(false);
-                    mHeadsUpNotificationView.setNotification(null);
+                    mHeadsUpNotificationView.release();
                     removeHeadsUpView();
                 } else {
                     addHeadsUpView();
@@ -832,6 +828,10 @@
         return mStatusBarView;
     }
 
+    public StatusBarWindowView getStatusBarWindow() {
+        return mStatusBarWindow;
+    }
+
     @Override
     protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
         boolean opaque = false;
@@ -1064,7 +1064,7 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+    public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
         if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
         if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) {
             // Forward the ranking so we can sort the new notification.
@@ -1075,31 +1075,28 @@
         displayNotification(notification, ranking);
     }
 
-    public void displayNotification(StatusBarNotification notification,
-            Ranking ranking) {
-        Entry shadeEntry = createNotificationViews(notification);
-        if (shadeEntry == null) {
-            return;
-        }
+    public void displayNotification(StatusBarNotification notification, RankingMap ranking) {
         if (mUseHeadsUp && shouldInterrupt(notification)) {
             if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
             Entry interruptionCandidate = new Entry(notification, null);
             ViewGroup holder = mHeadsUpNotificationView.getHolder();
             if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
                 mInterruptingNotificationTime = System.currentTimeMillis();
-                mInterruptingNotificationEntry = interruptionCandidate;
-                shadeEntry.setInterruption();
 
                 // 1. Populate mHeadsUpNotificationView
-                mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
+                mHeadsUpNotificationView.showNotification(interruptionCandidate);
 
-                // 2. Animate mHeadsUpNotificationView in
-                mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
-
-                // 3. Set alarm to age the notification off
-                resetHeadsUpDecayTimer();
+                // do not show the notification in the shade, yet.
+                return;
             }
-        } else if (notification.getNotification().fullScreenIntent != null) {
+        }
+
+        Entry shadeEntry = createNotificationViews(notification);
+        if (shadeEntry == null) {
+            return;
+        }
+
+        if (notification.getNotification().fullScreenIntent != null) {
             // Stop screensaver if the notification has a full-screen intent.
             // (like an incoming phone call)
             awakenDreams();
@@ -1114,7 +1111,7 @@
             // usual case: status bar visible & not immersive
 
             // show the ticker if there isn't already a heads up
-            if (mInterruptingNotificationEntry == null) {
+            if (mHeadsUpNotificationView.getEntry() == null) {
                 tick(notification, true);
             }
         }
@@ -1124,31 +1121,64 @@
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
     }
 
+    public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
+        NotificationData.Entry shadeEntry = createNotificationViews(notification);
+        if (shadeEntry == null) {
+            return;
+        }
+        shadeEntry.setInterruption();
+
+        addNotificationViews(shadeEntry, null);
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+    }
+
     @Override
     public void resetHeadsUpDecayTimer() {
-        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
+        mHandler.removeMessages(MSG_DECAY_HEADS_UP);
         if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
                 && mHeadsUpNotificationView.isClearable()) {
-            mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay);
+            mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
         }
     }
 
     @Override
-    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+    public void scheduleHeadsUpOpen() {
+        mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
+    }
+
+    @Override
+    public void scheduleHeadsUpClose() {
+        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+    }
+
+    @Override
+    public void scheduleHeadsUpEscalation() {
+        mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
+    }
+
+    @Override
+    public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
         super.updateNotificationInternal(notification, ranking);
         // if we're here, then the notification is already in the shade
         mIntercepted.remove(notification.getKey());
     }
 
     @Override
-    protected void updateRankingInternal(Ranking ranking) {
+    protected void updateRankingInternal(RankingMap ranking) {
         mNotificationData.updateRanking(ranking);
         mIntercepted.retryIntercepts(ranking);
         updateNotifications();
     }
 
     @Override
-    public void removeNotificationInternal(String key, Ranking ranking) {
+    public void removeNotificationInternal(String key, RankingMap ranking) {
+        if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
+                && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
+            mHeadsUpNotificationView.clear();
+        }
+
         StatusBarNotification old = removeNotificationViews(key, ranking);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
@@ -1161,11 +1191,6 @@
             // Recalculate the position of the sliding windows and the titles.
             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
 
-            if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
-                    && old == mInterruptingNotificationEntry.notification) {
-                mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-            }
-
             if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0
                     && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) {
                 animateCollapsePanels();
@@ -1587,7 +1612,12 @@
                 case MSG_SHOW_HEADS_UP:
                     setHeadsUpVisibility(true);
                     break;
+                case MSG_DECAY_HEADS_UP:
+                    mHeadsUpNotificationView.release();
+                    setHeadsUpVisibility(false);
+                    break;
                 case MSG_HIDE_HEADS_UP:
+                    mHeadsUpNotificationView.release();
                     setHeadsUpVisibility(false);
                     break;
                 case MSG_ESCALATE_HEADS_UP:
@@ -1600,8 +1630,9 @@
 
     /**  if the interrupting notification had a fullscreen intent, fire it now.  */
     private void escalateHeadsUp() {
-        if (mInterruptingNotificationEntry != null) {
-            final StatusBarNotification sbn = mInterruptingNotificationEntry.notification;
+        if (mHeadsUpNotificationView.getEntry() != null) {
+            final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
+            mHeadsUpNotificationView.release();
             final Notification notification = sbn.getNotification();
             if (notification.fullScreenIntent != null) {
                 if (DEBUG)
@@ -2256,7 +2287,7 @@
         pw.print("  mUseHeadsUp=");
         pw.println(mUseHeadsUp);
         pw.print("  interrupting package: ");
-        pw.println(hunStateToString(mInterruptingNotificationEntry));
+        pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
         dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
         if (mNavigationBarView != null) {
             pw.print("  mNavigationBarWindowState=");
@@ -2530,26 +2561,10 @@
         if (!ENABLE_HEADS_UP) return;
         if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
         mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
-        if (!vis) {
-            if (DEBUG) Log.d(TAG, "setting heads up entry to null");
-            mInterruptingNotificationEntry = null;
-        }
     }
 
     public void onHeadsUpDismissed() {
-        if (mInterruptingNotificationEntry == null) return;
-        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-        if (mHeadsUpNotificationView.isClearable()) {
-            try {
-                mBarService.onNotificationClear(
-                        mInterruptingNotificationEntry.notification.getPackageName(),
-                        mInterruptingNotificationEntry.notification.getTag(),
-                        mInterruptingNotificationEntry.notification.getId(),
-                        mInterruptingNotificationEntry.notification.getUserId());
-            } catch (android.os.RemoteException ex) {
-                // oh well
-            }
-        }
+        mHeadsUpNotificationView.dismiss();
     }
 
     /**
@@ -2971,22 +2986,9 @@
 
     private void instantExpandNotificationsPanel() {
 
-        // Make our window larger and the panel visible.
+        // Make our window larger and the panel expanded.
         makeExpandedVisible(true);
-        mNotificationPanel.setVisibility(View.VISIBLE);
-
-        // Wait for window manager to pickup the change, so we know the maximum height of the panel
-        // then.
-        mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
-                new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                if (mStatusBarWindow.getHeight() != getStatusBarHeight()) {
-                    mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                    mNotificationPanel.setExpandedFraction(1);
-                }
-            }
-        });
+        mNotificationPanel.instantExpand();
     }
 
     private void instantCollapseNotificationPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index df01c12..d778ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -33,9 +33,9 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
         ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -51,7 +51,7 @@
     private SwipeHelper mSwipeHelper;
     private EdgeSwipeHelper mEdgeSwipeHelper;
 
-    private BaseStatusBar mBar;
+    private PhoneStatusBar mBar;
     private ExpandHelper mExpandHelper;
 
     private long mStartTouchTime;
@@ -69,7 +69,7 @@
         if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
     }
 
-    public void setBar(BaseStatusBar bar) {
+    public void setBar(PhoneStatusBar bar) {
         mBar = bar;
     }
 
@@ -77,7 +77,10 @@
         return mContentHolder;
     }
 
-    public boolean setNotification(NotificationData.Entry headsUp) {
+    public boolean showNotification(NotificationData.Entry headsUp) {
+        // bump any previous heads up back to the shade
+        release();
+
         mHeadsUp = headsUp;
         if (mContentHolder != null) {
             mContentHolder.removeAllViews();
@@ -97,10 +100,46 @@
 
             mSwipeHelper.snapChild(mContentHolder, 1f);
             mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
+
+            // 2. Animate mHeadsUpNotificationView in
+            mBar.scheduleHeadsUpOpen();
+
+            // 3. Set alarm to age the notification off
+            mBar.resetHeadsUpDecayTimer();
         }
         return true;
     }
 
+    /** Discard the Heads Up notification. */
+    public void clear() {
+        mHeadsUp = null;
+        mBar.scheduleHeadsUpClose();
+    }
+
+    /** Respond to dismissal of the Heads Up window. */
+    public void dismiss() {
+        if (mHeadsUp == null) return;
+        if (mHeadsUp.notification.isClearable()) {
+            mBar.onNotificationClear(mHeadsUp.notification);
+        } else {
+            release();
+        }
+        mHeadsUp = null;
+        mBar.scheduleHeadsUpClose();
+    }
+
+    /** Push any current Heads Up notification down into the shade. */
+    public void release() {
+        if (mHeadsUp != null) {
+            mBar.displayNotificationFromHeadsUp(mHeadsUp.notification);
+        }
+        mHeadsUp = null;
+    }
+
+    public NotificationData.Entry getEntry() {
+        return mHeadsUp;
+    }
+
     public boolean isClearable() {
         return mHeadsUp == null || mHeadsUp.notification.isClearable();
     }
@@ -125,7 +164,7 @@
 
         if (mHeadsUp != null) {
             // whoops, we're on already!
-            setNotification(mHeadsUp);
+            showNotification(mHeadsUp);
         }
 
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -282,6 +321,10 @@
                 mTmpTwoArray[1] + mContentHolder.getHeight());
     }
 
+    public void escalate() {
+        mBar.scheduleHeadsUpEscalation();
+    }
+
     private class EdgeSwipeHelper implements Gefingerpoken {
         private static final boolean DEBUG_EDGE_SWIPE = false;
         private final float mTouchSlop;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index dc8f315..1f68860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -30,7 +30,7 @@
         void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId,
                 String mobileSignalContentDescriptionId, int dataTypeIconId,
                 boolean activityIn, boolean activityOut,
-                String dataTypeContentDescriptionId, String description);
+                String dataTypeContentDescriptionId, String description, boolean noSim);
         void onAirplaneModeChanged(boolean enabled);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 56402a5..254a0e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -87,6 +87,7 @@
     int mQSDataTypeIconId;
     int mAirplaneIconId;
     boolean mDataActive;
+    boolean mNoSim;
     int mLastSignalLevel;
     boolean mShowPhoneRSSIForData = false;
     boolean mShowAtLeastThreeGees = false;
@@ -349,18 +350,18 @@
         if (isEmergencyOnly()) {
             cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId,
                     mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                    mContentDescriptionDataType, null);
+                    mContentDescriptionDataType, null, mNoSim);
         } else {
             if (mIsWimaxEnabled && mWimaxConnected) {
                 // Wimax is special
                 cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId,
                         mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                        mContentDescriptionDataType, mNetworkName);
+                        mContentDescriptionDataType, mNetworkName, mNoSim);
             } else {
                 // Normal mobile data
                 cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId,
                         mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
-                        mContentDescriptionDataType, mNetworkName);
+                        mContentDescriptionDataType, mNetworkName, mNoSim);
             }
         }
         cb.onAirplaneModeChanged(mAirplaneMode);
@@ -736,6 +737,7 @@
             // GSM case, we have to check also the sim state
             if (mSimState == IccCardConstants.State.READY ||
                     mSimState == IccCardConstants.State.UNKNOWN) {
+                mNoSim = false;
                 if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
                     switch (mDataActivity) {
                         case TelephonyManager.DATA_ACTIVITY_IN:
@@ -758,6 +760,7 @@
                 }
             } else {
                 iconId = R.drawable.stat_sys_no_sim;
+                mNoSim = true;
                 visible = false; // no SIM? no data
             }
         } else {
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 fa8ad4c..9260aac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.tv;
 
 import android.os.IBinder;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
@@ -51,11 +51,11 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+    public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
     }
 
     @Override
-    protected void updateRankingInternal(Ranking ranking) {
+    protected void updateRankingInternal(RankingMap ranking) {
     }
 
     @Override
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public void removeNotificationInternal(String key, Ranking ranking) {
+    public void removeNotificationInternal(String key, RankingMap ranking) {
     }
 
     @Override
@@ -147,6 +147,18 @@
     }
 
     @Override
+    public void scheduleHeadsUpOpen() {
+    }
+
+    @Override
+    public void scheduleHeadsUpEscalation() {
+    }
+
+    @Override
+    public void scheduleHeadsUpClose() {
+    }
+
+    @Override
     protected int getMaxKeyguardNotifications() {
         return 0;
     }
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index fe5c2ef..aeb195f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.database.ContentObserver;
 import android.os.BatteryStats;
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
@@ -149,8 +150,8 @@
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
         mLowBatteryWarningLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
-        mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+        mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
         mShutdownBatteryTemperature = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shutdownBatteryTemperature);
 
@@ -173,11 +174,39 @@
     void systemReady() {
         // check our power situation now that it is safe to display the shutdown dialog.
         synchronized (mLock) {
-            shutdownIfNoPowerLocked();
-            shutdownIfOverTempLocked();
+            ContentObserver obs = new ContentObserver(mHandler) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    synchronized (mLock) {
+                        updateBatteryWarningLevelLocked();
+                    }
+                }
+            };
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                    false, obs, UserHandle.USER_ALL);
+            updateBatteryWarningLevelLocked();
         }
     }
 
+    void updateBatteryWarningLevelLocked() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        int defWarnLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
+        if (mLowBatteryWarningLevel == 0) {
+            mLowBatteryWarningLevel = defWarnLevel;
+        }
+        if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
+            mLowBatteryWarningLevel = mCriticalBatteryLevel;
+        }
+        mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
+        processValuesLocked(true);
+    }
+
     /**
      * Returns true if the device is plugged into any of the specified plug types.
      */
@@ -232,7 +261,7 @@
         }
     }
 
-    public boolean isBatteryLowLocked() {
+    public boolean shouldSendBatteryLowLocked() {
         final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
         final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
 
@@ -299,14 +328,14 @@
             if (!mUpdatesStopped) {
                 mBatteryProps = props;
                 // Process the new values.
-                processValuesLocked();
+                processValuesLocked(false);
             } else {
                 mLastBatteryProps.set(props);
             }
         }
     }
 
-    private void processValuesLocked() {
+    private void processValuesLocked(boolean force) {
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
@@ -349,14 +378,14 @@
         shutdownIfNoPowerLocked();
         shutdownIfOverTempLocked();
 
-        if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
+        if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
                 mBatteryProps.batteryHealth != mLastBatteryHealth ||
                 mBatteryProps.batteryPresent != mLastBatteryPresent ||
                 mBatteryProps.batteryLevel != mLastBatteryLevel ||
                 mPlugType != mLastPlugType ||
                 mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
-                mInvalidCharger != mLastInvalidCharger) {
+                mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -400,7 +429,24 @@
                 logOutlier = true;
             }
 
-            mBatteryLevelLow = isBatteryLowLocked();
+            if (!mBatteryLevelLow) {
+                // Should we now switch in to low battery mode?
+                if (mPlugType == BATTERY_PLUGGED_NONE
+                        && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
+                    mBatteryLevelLow = true;
+                }
+            } else {
+                // Should we now switch out of low battery mode?
+                if (mPlugType != BATTERY_PLUGGED_NONE) {
+                    mBatteryLevelLow = false;
+                } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel)  {
+                    mBatteryLevelLow = false;
+                } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
+                    // If being forced, the previous state doesn't matter, we will just
+                    // absolutely check to see if we are now above the warning level.
+                    mBatteryLevelLow = false;
+                }
+            }
 
             sendIntentLocked();
 
@@ -428,7 +474,7 @@
                 });
             }
 
-            if (mBatteryLevelLow) {
+            if (shouldSendBatteryLowLocked()) {
                 mSentLowBatteryBroadcast = true;
                 mHandler.post(new Runnable() {
                     @Override
@@ -650,7 +696,7 @@
                         long ident = Binder.clearCallingIdentity();
                         try {
                             mUpdatesStopped = true;
-                            processValuesLocked();
+                            processValuesLocked(false);
                         } finally {
                             Binder.restoreCallingIdentity(ident);
                         }
@@ -664,7 +710,7 @@
                     if (mUpdatesStopped) {
                         mUpdatesStopped = false;
                         mBatteryProps.set(mLastBatteryProps);
-                        processValuesLocked();
+                        processValuesLocked(false);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b429b93..fe49371 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -906,6 +906,9 @@
             }
             startTime = 0;
             finishLaunchTickingLocked();
+            if (task != null) {
+                task.hasBeenVisible = true;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 35f8f31..196e860 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -45,6 +45,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.IActivityManager.WaitResult;
 import android.app.ResultInfo;
+import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -73,6 +74,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
@@ -88,11 +90,13 @@
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.TransferPipe;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.wm.WindowManagerService;
 
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -128,9 +132,15 @@
     static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
     static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
     static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
+    static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
+    static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
 
     private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
+    /** Status Bar Service **/
+    private IBinder mToken = new Binder();
+    private IStatusBarService mStatusBarService;
+
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
     static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
@@ -253,6 +263,21 @@
         mLaunchingActivity.setReferenceCounted(false);
     }
 
+    // This function returns a IStatusBarService. The value is from ServiceManager.
+    // getService and is cached.
+    private IStatusBarService getStatusBarService() {
+        synchronized (mService) {
+            if (mStatusBarService == null) {
+                mStatusBarService = IStatusBarService.Stub.asInterface(
+                    ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+                if (mStatusBarService == null) {
+                    Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
+                }
+            }
+            return mStatusBarService;
+        }
+    }
+
     void setWindowManager(WindowManagerService wm) {
         synchronized (mService) {
             mWindowManager = wm;
@@ -2953,9 +2978,12 @@
     }
 
     void setLockTaskModeLocked(TaskRecord task) {
+        final Message lockTaskMsg = Message.obtain();
         if (task == null) {
             // Take out of lock task mode.
             mLockTaskModeTask = null;
+            lockTaskMsg.what = LOCK_TASK_END_MSG;
+            mHandler.sendMessage(lockTaskMsg);
             return;
         }
         if (isLockTaskModeViolation(task)) {
@@ -2965,6 +2993,8 @@
         mLockTaskModeTask = task;
         findTaskToMoveToFrontLocked(task, 0, null);
         resumeTopActivitiesLocked();
+        lockTaskMsg.what = LOCK_TASK_START_MSG;
+        mHandler.sendMessage(lockTaskMsg);
     }
 
     boolean isLockTaskModeViolation(TaskRecord task) {
@@ -3061,6 +3091,32 @@
                     } catch (RemoteException e) {
                     }
                 }
+                case LOCK_TASK_START_MSG: {
+                    // When lock task starts, we disable the status bars.
+                    try {
+                        if (getStatusBarService() != null) {
+                            getStatusBarService().disable
+                                (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
+                                mToken, mService.mContext.getPackageName());
+                        }
+                    } catch (RemoteException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                    break;
+                }
+                case LOCK_TASK_END_MSG: {
+                    // When lock task ends, we enable the status bars.
+                    try {
+                       if (getStatusBarService() != null) {
+                           getStatusBarService().disable
+                               (StatusBarManager.DISABLE_NONE,
+                               mToken, mService.mContext.getPackageName());
+                       }
+                    } catch (RemoteException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                    break;
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 79e2d9d..81a0b36 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -73,6 +73,7 @@
     boolean rootWasReset;   // True if the intent at the root of the task had
                             // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
     boolean askedCompatMode;// Have asked the user about compat mode for this task.
+    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
@@ -328,8 +329,12 @@
     }
 
     boolean autoRemoveFromRecents() {
-        return intent != null &&
-                (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
+        // We will automatically remove the task either if it has explicitly asked for
+        // this, or it is empty and has never contained an activity that got shown to
+        // the user.
+        return (intent != null &&
+                (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0) ||
+                (mActivities.isEmpty() && !hasBeenVisible);
     }
 
     /**
@@ -800,7 +805,8 @@
         }
         pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
                 pw.print(" lastDescription="); pw.println(lastDescription);
-        pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+        pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
+                pw.print(" lastActiveTime="); pw.print(lastActiveTime);
                 pw.print(" (inactive for ");
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0c094e8..c7dd849 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4988,6 +4988,7 @@
                     mResolveActivity.packageName = mAndroidApplication.packageName;
                     mResolveActivity.processName = "system:ui";
                     mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
                     mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
                     mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
                     mResolveActivity.exported = true;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d8671d9..fb4b8f0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -409,6 +409,9 @@
     // Current state of the low power mode setting.
     private boolean mLowPowerModeSetting;
 
+    // Current state of whether the settings are allowing auto low power mode.
+    private boolean mAutoLowPowerModeEnabled;
+
     // True if the battery level is currently considered low.
     private boolean mBatteryLevelLow;
 
@@ -558,6 +561,9 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.LOW_POWER_MODE),
                     false, mSettingsObserver, UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -645,8 +651,12 @@
 
         final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
                 Settings.Global.LOW_POWER_MODE, 0) != 0;
-        if (lowPowerModeEnabled != mLowPowerModeSetting) {
+        final boolean autoLowPowerModeEnabled = Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15) != 0;
+        if (lowPowerModeEnabled != mLowPowerModeSetting
+                || autoLowPowerModeEnabled != mAutoLowPowerModeEnabled) {
             mLowPowerModeSetting = lowPowerModeEnabled;
+            mAutoLowPowerModeEnabled = autoLowPowerModeEnabled;
             updateLowPowerModeLocked();
         }
 
@@ -654,7 +664,8 @@
     }
 
     void updateLowPowerModeLocked() {
-        final boolean lowPowerModeEnabled = mLowPowerModeSetting || mBatteryLevelLow;
+        final boolean lowPowerModeEnabled = !mIsPowered
+                && (mLowPowerModeSetting || (mAutoLowPowerModeEnabled && mBatteryLevelLow));
         if (mLowPowerModeEnabled != lowPowerModeEnabled) {
             mLowPowerModeEnabled = lowPowerModeEnabled;
             powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
@@ -1197,7 +1208,7 @@
                 }
             }
 
-            if (oldLevelLow != mBatteryLevelLow) {
+            if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
                 updateLowPowerModeLocked();
             }
         }
@@ -2168,6 +2179,8 @@
             pw.println("  mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
             pw.println("  mSandmanScheduled=" + mSandmanScheduled);
             pw.println("  mSandmanSummoned=" + mSandmanSummoned);
+            pw.println("  mLowPowerModeEnabled=" + mLowPowerModeEnabled);
+            pw.println("  mBatteryLevelLow=" + mBatteryLevelLow);
             pw.println("  mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
             pw.println("  mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
             pw.println("  mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
@@ -2204,6 +2217,8 @@
             pw.println("  mDreamsEnabledSetting=" + mDreamsEnabledSetting);
             pw.println("  mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
             pw.println("  mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
+            pw.println("  mLowPowerModeSetting=" + mLowPowerModeSetting);
+            pw.println("  mAutoLowPowerModeEnabled=" + mAutoLowPowerModeEnabled);
             pw.println("  mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
             pw.println("  mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
             pw.println("  mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);