Merge "Consolidate user actions metrics into one histogram." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 1c4f85b..a5aadfa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8685,6 +8685,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -8696,6 +8697,7 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -34723,7 +34725,9 @@
     method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
     method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
+    field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -34739,6 +34743,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -34769,13 +34774,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -34811,7 +34819,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
@@ -34819,8 +34827,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -51977,7 +51984,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -51991,7 +51997,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
diff --git a/api/system-current.txt b/api/system-current.txt
index 039b9ad..0902b53 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5076,6 +5076,7 @@
     field public static final java.lang.String EXTRA_THREAD_TITLE = "android.threadTitle";
     field public static final java.lang.String EXTRA_TITLE = "android.title";
     field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
     field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
     field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
@@ -9004,6 +9005,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -9017,6 +9019,7 @@
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
     field public static final java.lang.String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
     field public static final java.lang.String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -37117,6 +37120,22 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri);
+    ctor protected Adjustment(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.CharSequence getExplanation();
+    method public int getImportance();
+    method public java.lang.String getKey();
+    method public java.lang.String getPackage();
+    method public android.net.Uri getReference();
+    method public android.os.Bundle getSignals();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+    field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
+    field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
+  }
+
   public class Condition implements android.os.Parcelable {
     ctor public Condition(android.net.Uri, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
@@ -37192,7 +37211,9 @@
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
+    field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -37210,6 +37231,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -37233,11 +37255,12 @@
 
   public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService {
     ctor public NotificationRankerService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationRankerService.Adjustment);
+    method public final void adjustNotification(android.service.notification.Adjustment);
+    method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationRankerService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     field public static final int REASON_APP_CANCEL = 8; // 0x8
@@ -37254,14 +37277,11 @@
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
     field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
     field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
+    field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService";
   }
 
-  public class NotificationRankerService.Adjustment {
-    ctor public NotificationRankerService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -37271,13 +37291,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -37346,7 +37369,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void setStatusIcon(android.graphics.drawable.Icon, java.lang.String);
@@ -37355,8 +37378,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -55074,7 +55096,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -55088,7 +55109,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
diff --git a/api/test-current.txt b/api/test-current.txt
index 3febda1..5ec3a5f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8692,6 +8692,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -8703,6 +8704,7 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -34796,7 +34798,9 @@
     method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
     method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
+    field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -34812,6 +34816,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -34842,13 +34847,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -34884,7 +34892,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
@@ -34892,8 +34900,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -52053,7 +52060,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -52067,7 +52073,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96e1eaa..36e962e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4727,8 +4727,16 @@
         if (callbacks != null) {
             final int N = callbacks.size();
             for (int i=0; i<N; i++) {
-                performConfigurationChanged(callbacks.get(i), null, config, null,
-                        REPORT_TO_ACTIVITY);
+                ComponentCallbacks2 cb = callbacks.get(i);
+                if (cb instanceof Activity) {
+                    // If callback is an Activity - call corresponding method to consider override
+                    // config and avoid onConfigurationChanged if it hasn't changed.
+                    Activity a = (Activity) cb;
+                    performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
+                            config, REPORT_TO_ACTIVITY);
+                } else {
+                    performConfigurationChanged(cb, null, config, null, REPORT_TO_ACTIVITY);
+                }
             }
         }
     }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7a69c62..ee80ec3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -25,6 +25,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.Uri;
 import android.os.Bundle;
+import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
@@ -80,7 +81,8 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter);
 
-    void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
+    void applyAdjustmentFromRankerService(in INotificationListener token, in Adjustment adjustment);
+    void applyAdjustmentsFromRankerService(in INotificationListener token, in List<Adjustment> adjustments);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index faefc9d..4bf1aa3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -496,6 +497,15 @@
      */
     public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
 
+    /**
+     * Bit to be bitswise-ored into the {@link #flags} field that should be
+     * set if this notification is the group summary for an auto-group of notifications.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_AUTOGROUP_SUMMARY  = 0x00000400;
+
     public int flags;
 
     /** @hide */
@@ -1945,13 +1955,9 @@
      * @hide
      */
     public static void addFieldsFromContext(Context context, Notification notification) {
-        if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
-            notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
-                    context.getApplicationInfo());
-        }
-        if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
-            notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
-        }
+        notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
+                context.getApplicationInfo());
+        notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
     }
 
     @Override
@@ -3020,12 +3026,13 @@
         /**
          * @hide
          */
-        public void setFlag(int mask, boolean value) {
+        public Builder setFlag(int mask, boolean value) {
             if (value) {
                 mN.flags |= mask;
             } else {
                 mN.flags &= ~mask;
             }
+            return this;
         }
 
         /**
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 95a8ccf..77307b7 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -27,6 +27,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.ref.WeakReference;
+
 /**
  * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
  * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
@@ -62,15 +64,15 @@
      * Identifier for a message that will result in a call to
      * {@link #onStartJob(android.app.job.JobParameters)}.
      */
-    private final int MSG_EXECUTE_JOB = 0;
+    private static final int MSG_EXECUTE_JOB = 0;
     /**
      * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
      */
-    private final int MSG_STOP_JOB = 1;
+    private static final int MSG_STOP_JOB = 1;
     /**
      * Message that the client has completed execution of this job.
      */
-    private final int MSG_JOB_FINISHED = 2;
+    private static final int MSG_JOB_FINISHED = 2;
 
     /** Lock object for {@link #mHandler}. */
     private final Object mHandlerLock = new Object();
@@ -82,21 +84,36 @@
     @GuardedBy("mHandlerLock")
     JobHandler mHandler;
 
-    /** Binder for this service. */
-    IJobService mBinder = new IJobService.Stub() {
-        @Override
-        public void startJob(JobParameters jobParams) {
-            ensureHandler();
-            Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
-            m.sendToTarget();
+    static final class JobInterface extends IJobService.Stub {
+        final WeakReference<JobService> mService;
+
+        JobInterface(JobService service) {
+            mService = new WeakReference<>(service);
         }
+
         @Override
-        public void stopJob(JobParameters jobParams) {
-            ensureHandler();
-            Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
-            m.sendToTarget();
+        public void startJob(JobParameters jobParams) throws RemoteException {
+            JobService service = mService.get();
+            if (service != null) {
+                service.ensureHandler();
+                Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
+                m.sendToTarget();
+            }
         }
-    };
+
+        @Override
+        public void stopJob(JobParameters jobParams) throws RemoteException {
+            JobService service = mService.get();
+            if (service != null) {
+                service.ensureHandler();
+                Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
+                m.sendToTarget();
+            }
+
+        }
+    }
+
+    IJobService mBinder;
 
     /** @hide */
     void ensureHandler() {
@@ -194,6 +211,9 @@
 
     /** @hide */
     public final IBinder onBind(Intent intent) {
+        if (mBinder == null) {
+            mBinder = new JobInterface(this);
+        }
         return mBinder.asBinder();
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 30f2c94..207b70a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3730,6 +3730,31 @@
     public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
 
     /**
+     * A {@link ComponentName ComponentName[]} describing components that should be filtered out
+     * and omitted from a list of components presented to the user.
+     *
+     * <p>When used with {@link #ACTION_CHOOSER}, the chooser will omit any of the components
+     * in this array if it otherwise would have shown them. Useful for omitting specific targets
+     * from your own package or other apps from your organization if the idea of sending to those
+     * targets would be redundant with other app functionality. Filtered components will not
+     * be able to present targets from an associated <code>ChooserTargetService</code>.</p>
+     */
+    public static final String EXTRA_EXCLUDE_COMPONENTS
+            = "android.intent.extra.EXCLUDE_COMPONENTS";
+
+    /**
+     * A {@link android.service.chooser.ChooserTarget ChooserTarget[]} for {@link #ACTION_CHOOSER}
+     * describing additional high-priority deep-link targets for the chooser to present to the user.
+     *
+     * <p>Targets provided in this way will be presented inline with all other targets provided
+     * by services from other apps. They will be prioritized before other service targets, but
+     * after those targets provided by sources that the user has manually pinned to the front.</p>
+     *
+     * @see #ACTION_CHOOSER
+     */
+    public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
+
+    /**
      * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection
      * from the chooser activity presented by {@link #ACTION_CHOOSER}.
      *
diff --git a/core/java/android/inputmethodservice/CompactExtractEditLayout.java b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
new file mode 100644
index 0000000..f994c65
--- /dev/null
+++ b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
@@ -0,0 +1,103 @@
+package android.inputmethodservice;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.annotation.FractionRes;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A special purpose layout for the editor extract view for tiny (sub 250dp) screens.
+ * The layout is based on sizes proportional to screen pixel size to provide for the
+ * best layout fidelity on varying pixel sizes and densities.
+ *
+ * @hide
+ */
+public class CompactExtractEditLayout extends LinearLayout {
+    private View mInputExtractEditText;
+    private View mInputExtractAccessories;
+    private View mInputExtractAction;
+    private boolean mPerformLayoutChanges;
+
+    public CompactExtractEditLayout(Context context) {
+        super(context);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mInputExtractEditText = findViewById(com.android.internal.R.id.inputExtractEditText);
+        mInputExtractAccessories = findViewById(com.android.internal.R.id.inputExtractAccessories);
+        mInputExtractAction = findViewById(com.android.internal.R.id.inputExtractAction);
+
+        if (mInputExtractEditText != null && mInputExtractAccessories != null
+                && mInputExtractAction != null) {
+            mPerformLayoutChanges = true;
+        }
+    }
+
+    private int applyFractionInt(@FractionRes int fraction, int whole) {
+        return Math.round(getResources().getFraction(fraction, whole, whole));
+    }
+
+    private static void setLayoutHeight(View v, int px) {
+        ViewGroup.LayoutParams lp = v.getLayoutParams();
+        lp.height = px;
+        v.setLayoutParams(lp);
+    }
+
+    private static void setLayoutMarginBottom(View v, int px) {
+        ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+        lp.bottomMargin = px;
+        v.setLayoutParams(lp);
+    }
+
+    private void applyProportionalLayout(int screenWidthPx, int screenHeightPx) {
+        if (getResources().getConfiguration().isScreenRound()) {
+            setGravity(Gravity.BOTTOM);
+        }
+        setLayoutHeight(this, applyFractionInt(
+                com.android.internal.R.fraction.input_extract_layout_height, screenHeightPx));
+
+        setPadding(
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_left,
+                        screenWidthPx),
+                0,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_right,
+                        screenWidthPx),
+                0);
+
+        setLayoutMarginBottom(mInputExtractEditText,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_text_margin_bottom,
+                        screenHeightPx));
+
+        setLayoutMarginBottom(mInputExtractAccessories,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_action_margin_bottom,
+                        screenHeightPx));
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mPerformLayoutChanges) {
+            Resources res = getResources();
+            DisplayMetrics dm = res.getDisplayMetrics();
+            int heightPixels = dm.heightPixels;
+            int widthPixels = dm.widthPixels;
+            applyProportionalLayout(widthPixels, heightPixels);
+        }
+    }
+}
+
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index cc201bc..085b97c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -68,9 +68,10 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.ImageButton;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -302,7 +303,7 @@
     boolean mExtractViewHidden;
     ExtractEditText mExtractEditText;
     ViewGroup mExtractAccessories;
-    Button mExtractAction;
+    View mExtractAction;
     ExtractedText mExtractedText;
     int mExtractedToken;
     
@@ -1344,7 +1345,7 @@
             mExtractEditText = (ExtractEditText)view.findViewById(
                     com.android.internal.R.id.inputExtractEditText);
             mExtractEditText.setIME(this);
-            mExtractAction = (Button)view.findViewById(
+            mExtractAction = view.findViewById(
                     com.android.internal.R.id.inputExtractAction);
             if (mExtractAction != null) {
                 mExtractAccessories = (ViewGroup)view.findViewById(
@@ -2408,7 +2409,35 @@
                 return getText(com.android.internal.R.string.ime_action_default);
         }
     }
-    
+
+    /**
+     * Return a drawable resource id that can be used as a button icon for the given
+     * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @return Returns a drawable resource id to use.
+     */
+    @DrawableRes
+    private int getIconForImeAction(int imeOptions) {
+        switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+            case EditorInfo.IME_ACTION_GO:
+                return com.android.internal.R.drawable.ic_input_extract_action_go;
+            case EditorInfo.IME_ACTION_SEARCH:
+                return com.android.internal.R.drawable.ic_input_extract_action_search;
+            case EditorInfo.IME_ACTION_SEND:
+                return com.android.internal.R.drawable.ic_input_extract_action_send;
+            case EditorInfo.IME_ACTION_NEXT:
+                return com.android.internal.R.drawable.ic_input_extract_action_next;
+            case EditorInfo.IME_ACTION_DONE:
+                return com.android.internal.R.drawable.ic_input_extract_action_done;
+            case EditorInfo.IME_ACTION_PREVIOUS:
+                return com.android.internal.R.drawable.ic_input_extract_action_previous;
+            default:
+                return com.android.internal.R.drawable.ic_input_extract_action_return;
+        }
+    }
+
     /**
      * Called when the fullscreen-mode extracting editor info has changed,
      * to determine whether the extracting (extract text and candidates) portion
@@ -2459,10 +2488,20 @@
         if (hasAction) {
             mExtractAccessories.setVisibility(View.VISIBLE);
             if (mExtractAction != null) {
-                if (ei.actionLabel != null) {
-                    mExtractAction.setText(ei.actionLabel);
+                if (mExtractAction instanceof ImageButton) {
+                    ((ImageButton) mExtractAction)
+                            .setImageResource(getIconForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        mExtractAction.setContentDescription(ei.actionLabel);
+                    } else {
+                        mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
+                    }
                 } else {
-                    mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        ((TextView) mExtractAction).setText(ei.actionLabel);
+                    } else {
+                        ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
+                    }
                 }
                 mExtractAction.setOnClickListener(mActionClickListener);
             }
diff --git a/core/java/android/service/notification/Adjustment.aidl b/core/java/android/service/notification/Adjustment.aidl
new file mode 100644
index 0000000..8bd814a
--- /dev/null
+++ b/core/java/android/service/notification/Adjustment.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable Adjustment;
\ No newline at end of file
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
new file mode 100644
index 0000000..2e4f48d
--- /dev/null
+++ b/core/java/android/service/notification/Adjustment.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Ranking updates from the Ranker.
+ *
+ * @hide
+ */
+@SystemApi
+public final class Adjustment implements Parcelable {
+    private final String mPackage;
+    private final String mKey;
+    private final int mImportance;
+    private final CharSequence mExplanation;
+    private final Uri mReference;
+    private final Bundle mSignals;
+
+    public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
+    public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
+
+    /**
+     * Create a notification adjustment.
+     *
+     * @param pkg The package of the notification.
+     * @param key The notification key.
+     * @param importance The recommended importance of the notification.
+     * @param signals A bundle of signals that should inform notification grouping and ordering.
+     * @param explanation A human-readable justification for the adjustment.
+     * @param reference A reference to an external object that augments the
+     *                  explanation, such as a
+     *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+     *                  or null.
+     */
+    public Adjustment(String pkg, String key, int importance, Bundle signals,
+            CharSequence explanation, Uri reference) {
+        mPackage = pkg;
+        mKey = key;
+        mImportance = importance;
+        mSignals = signals;
+        mExplanation = explanation;
+        mReference = reference;
+    }
+
+    protected Adjustment(Parcel in) {
+        if (in.readInt() == 1) {
+            mPackage = in.readString();
+        } else {
+            mPackage = null;
+        }
+        if (in.readInt() == 1) {
+            mKey = in.readString();
+        } else {
+            mKey = null;
+        }
+        mImportance = in.readInt();
+        if (in.readInt() == 1) {
+            mExplanation = in.readCharSequence();
+        } else {
+            mExplanation = null;
+        }
+        mReference = in.readParcelable(Uri.class.getClassLoader());
+        mSignals = in.readBundle();
+    }
+
+    public static final Creator<Adjustment> CREATOR = new Creator<Adjustment>() {
+        @Override
+        public Adjustment createFromParcel(Parcel in) {
+            return new Adjustment(in);
+        }
+
+        @Override
+        public Adjustment[] newArray(int size) {
+            return new Adjustment[size];
+        }
+    };
+
+    public String getPackage() {
+        return mPackage;
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    public int getImportance() {
+        return mImportance;
+    }
+
+    public CharSequence getExplanation() {
+        return mExplanation;
+    }
+
+    public Uri getReference() {
+        return mReference;
+    }
+
+    public Bundle getSignals() {
+        return mSignals;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mPackage != null) {
+            dest.writeInt(1);
+            dest.writeString(mPackage);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mKey != null) {
+            dest.writeInt(1);
+            dest.writeString(mKey);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mImportance);
+        if (mExplanation != null) {
+            dest.writeInt(1);
+            dest.writeCharSequence(mExplanation);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeParcelable(mReference, flags);
+        dest.writeBundle(mSignals);
+    }
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 7325aef..e708b0a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -123,6 +123,16 @@
      * This does not change the interruption filter, only the effects. **/
     public static final int HINT_HOST_DISABLE_EFFECTS = 1;
 
+    /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
+     * should disable notification sound, but not phone calls.
+     * This does not change the interruption filter, only the effects. **/
+    public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 1 << 1;
+
+    /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
+     * should disable phone call sounds, buyt not notification sound.
+     * This does not change the interruption filter, only the effects. **/
+    public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 1 << 2;
+
     /**
      * Whether notification suppressed by DND should not interruption visually when the screen is
      * off.
@@ -1052,6 +1062,8 @@
         private int mSuppressedVisualEffects;
         private @Importance int mImportance;
         private CharSequence mImportanceExplanation;
+        // System specified group key.
+        private String mOverrideGroupKey;
 
         public Ranking() {}
 
@@ -1130,9 +1142,17 @@
             return mImportanceExplanation;
         }
 
+        /**
+         * If the system has overriden the group key, then this will be non-null, and this
+         * key should be used to bundle notifications.
+         */
+        public String getOverrideGroupKey() {
+            return mOverrideGroupKey;
+        }
+
         private void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
-                CharSequence explanation) {
+                CharSequence explanation, String overrideGroupKey) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < IMPORTANCE_LOW;
@@ -1141,6 +1161,7 @@
             mSuppressedVisualEffects = suppressedVisualEffects;
             mImportance = importance;
             mImportanceExplanation = explanation;
+            mOverrideGroupKey = overrideGroupKey;
         }
 
         /**
@@ -1184,6 +1205,7 @@
         private ArrayMap<String, Integer> mSuppressedVisualEffects;
         private ArrayMap<String, Integer> mImportance;
         private ArrayMap<String, String> mImportanceExplanation;
+        private ArrayMap<String, String> mOverrideGroupKeys;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1210,7 +1232,7 @@
             int rank = getRank(key);
             outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
-                    getImportance(key), getImportanceExplanation(key));
+                    getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key));
             return rank >= 0;
         }
 
@@ -1281,6 +1303,15 @@
             return mImportanceExplanation.get(key);
         }
 
+        private String getOverrideGroupKey(String key) {
+            synchronized (this) {
+                if (mOverrideGroupKeys == null) {
+                    buildOverrideGroupKeys();
+                }
+            }
+            return mOverrideGroupKeys.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1335,6 +1366,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildOverrideGroupKeys() {
+            Bundle overrideGroupKeys = mRankingUpdate.getOverrideGroupKeys();
+            mOverrideGroupKeys = new ArrayMap<>(overrideGroupKeys.size());
+            for (String key: overrideGroupKeys.keySet()) {
+                mOverrideGroupKeys.put(key, overrideGroupKeys.getString(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java
index 47fdac6..ee5361a 100644
--- a/core/java/android/service/notification/NotificationRankerService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -22,14 +22,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Log;
 import com.android.internal.os.SomeArgs;
 
+import java.util.List;
+
 /**
  * A service that helps the user manage notifications. This class is only used to
  * extend the framework service and may not be implemented by non-framework components.
@@ -91,27 +96,8 @@
     /** Notification was canceled by the owning managed profile being turned off. */
     public static final int REASON_PROFILE_TURNED_OFF = 15;
 
-    public class Adjustment {
-        int mImportance;
-        CharSequence mExplanation;
-        Uri mReference;
-
-        /**
-         * Create a notification importance adjustment.
-         *
-         * @param importance The final importance of the notification.
-         * @param explanation A human-readable justification for the adjustment.
-         * @param reference A reference to an external object that augments the
-         *                  explanation, such as a
-         *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
-         *                  or null.
-         */
-        public Adjustment(int importance, CharSequence explanation, Uri reference) {
-            mImportance = importance;
-            mExplanation = explanation;
-            mReference = reference;
-        }
-    }
+    /** Autobundled summary notification was canceled because its group was unbundled */
+    public static final int REASON_UNAUTOBUNDLED = 16;
 
     private Handler mHandler;
 
@@ -200,18 +186,32 @@
     }
 
     /**
-     * Change the importance of an existing notification.  N.B. this won’t cause
+     * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
      *
-     * @param key the notification key
-     * @param adjustment the new importance with an explanation
+     * @param adjustment the adjustment with an explanation
      */
-    public final void adjustImportance(String key, Adjustment adjustment) {
+    public final void adjustNotification(Adjustment adjustment) {
         if (!isBound()) return;
         try {
-            getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
-                    adjustment.mImportance, adjustment.mExplanation);
+            getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
+     * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
+     * N.B. this won’t cause an existing notification to alert, but might allow a future update to
+     * these notifications to alert.
+     *
+     * @param adjustments a list of adjustments with explanations
+     */
+    public final void adjustNotifications(List<Adjustment> adjustments) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
@@ -299,7 +299,7 @@
                     args.recycle();
                     Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
                     if (adjustment != null) {
-                        adjustImportance(sbn.getKey(), adjustment);
+                        adjustNotification(adjustment);
                     }
                 } break;
 
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 79f6fc4..788b5c0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -30,16 +30,18 @@
     private final Bundle mSuppressedVisualEffects;
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
+    private final Bundle mOverrideGroupKeys;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
-            int[] importance, Bundle explanation) {
+            int[] importance, Bundle explanation, Bundle overrideGroupKeys) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
         mSuppressedVisualEffects = suppressedVisualEffects;
         mImportance = importance;
         mImportanceExplanation = explanation;
+        mOverrideGroupKeys = overrideGroupKeys;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -50,6 +52,7 @@
         mImportance = new int[mKeys.length];
         in.readIntArray(mImportance);
         mImportanceExplanation = in.readBundle();
+        mOverrideGroupKeys = in.readBundle();
     }
 
     @Override
@@ -65,6 +68,7 @@
         out.writeBundle(mSuppressedVisualEffects);
         out.writeIntArray(mImportance);
         out.writeBundle(mImportanceExplanation);
+        out.writeBundle(mOverrideGroupKeys);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -101,4 +105,8 @@
     public Bundle getImportanceExplanation() {
         return mImportanceExplanation;
     }
+
+    public Bundle getOverrideGroupKeys() {
+        return mOverrideGroupKeys;
+    }
 }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 198e43d..0221b66 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -33,7 +33,8 @@
     private final int id;
     private final String tag;
     private final String key;
-    private final String groupKey;
+    private String groupKey;
+    private String overrideGroupKey;
 
     private final int uid;
     private final String opPkg;
@@ -51,6 +52,27 @@
                 System.currentTimeMillis());
     }
 
+    /** @hide */
+    public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
+            int initialPid, Notification notification, UserHandle user, String overrideGroupKey,
+            long postTime) {
+        if (pkg == null) throw new NullPointerException();
+        if (notification == null) throw new NullPointerException();
+
+        this.pkg = pkg;
+        this.opPkg = opPkg;
+        this.id = id;
+        this.tag = tag;
+        this.uid = uid;
+        this.initialPid = initialPid;
+        this.notification = notification;
+        this.user = user;
+        this.postTime = postTime;
+        this.overrideGroupKey = overrideGroupKey;
+        this.key = key();
+        this.groupKey = groupKey();
+    }
+
     public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
             int initialPid, int score, Notification notification, UserHandle user,
             long postTime) {
@@ -84,15 +106,27 @@
         this.notification = new Notification(in);
         this.user = UserHandle.readFromParcel(in);
         this.postTime = in.readLong();
+        if (in.readInt() != 0) {
+            this.overrideGroupKey = in.readString();
+        } else {
+            this.overrideGroupKey = null;
+        }
         this.key = key();
         this.groupKey = groupKey();
     }
 
     private String key() {
-        return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
+        String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
+        if (overrideGroupKey != null && getNotification().isGroupSummary()) {
+            sbnKey = sbnKey + "|" + overrideGroupKey;
+        }
+        return sbnKey;
     }
 
     private String groupKey() {
+        if (overrideGroupKey != null) {
+            return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;
+        }
         final String group = getNotification().getGroup();
         final String sortKey = getNotification().getSortKey();
         if (group == null && sortKey == null) {
@@ -105,6 +139,17 @@
                         : "g:" + group);
     }
 
+    /**
+     * Returns true if this notification is part of a group.
+     */
+    public boolean isGroup() {
+        if (overrideGroupKey != null || getNotification().getGroup() != null
+                || getNotification().getSortKey() != null) {
+            return true;
+        }
+        return false;
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(this.pkg);
         out.writeString(this.opPkg);
@@ -121,6 +166,12 @@
         user.writeToParcel(out, flags);
 
         out.writeLong(this.postTime);
+        if (this.overrideGroupKey != null) {
+            out.writeInt(1);
+            out.writeString(this.overrideGroupKey);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     public int describeContents() {
@@ -149,22 +200,22 @@
         this.notification.cloneInto(no, false); // light copy
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                0, no, this.user, this.postTime);
+                no, this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public StatusBarNotification clone() {
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                0, this.notification.clone(), this.user, this.postTime);
+                this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public String toString() {
         return String.format(
-                "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+                "StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
                 this.pkg, this.user, this.id, this.tag,
-                0, this.key, this.notification);
+                this.key, this.notification);
     }
 
     /** Convenience method to check the notification's flags for
@@ -258,6 +309,21 @@
     }
 
     /**
+     * Sets the override group key.
+     */
+    public void setOverrideGroupKey(String overrideGroupKey) {
+        this.overrideGroupKey = overrideGroupKey;
+        groupKey = groupKey();
+    }
+
+    /**
+     * Returns the override group key.
+     */
+    public String getOverrideGroupKey() {
+        return overrideGroupKey;
+    }
+
+    /**
      * @hide
      */
     public Context getPackageContext(Context context) {
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index 5434e2e..747f185 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -28,7 +28,6 @@
             String contentDescription);
     void onShowDialog(in Tile tile);
     void onStartActivity(in Tile tile);
-    void setTileMode(in ComponentName component, int mode);
     boolean isLocked();
     boolean isSecure();
     void startUnlockAndRun(in Tile tile);
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 553d539..4e9a075 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -89,28 +89,24 @@
     public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
 
     /**
-     * The tile mode hasn't been set yet.
-     * @hide
-     */
-    public static final int TILE_MODE_UNSET = 0;
-
-    /**
-     * Constant to be returned by {@link #onTileAdded}.
-     * <p>
-     * Passive mode is the default mode for tiles.  The System will tell the tile
-     * when it is most important to update by putting it in the listening state.
-     */
-    public static final int TILE_MODE_PASSIVE = 1;
-
-    /**
-     * Constant to be returned by {@link #onTileAdded}.
+     * Meta-data for tile definition to set a tile into active mode.
      * <p>
      * Active mode is for tiles which already listen and keep track of their state in their
      * own process.  These tiles may request to send an update to the System while their process
      * is alive using {@link #requestListeningState}.  The System will only bind these tiles
      * on its own when a click needs to occur.
+     *
+     * To make a TileService an active tile, set this meta-data to true on the TileService's
+     * manifest declaration.
+     * <pre class="prettyprint">
+     * {@literal
+     * <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
+     *      android:value="true" />
+     * }
+     * </pre>
      */
-    public static final int TILE_MODE_ACTIVE = 2;
+    public static final String META_DATA_ACTIVE_TILE
+            = "android.service.quicksettings.ACTIVE_TILE";
 
     /**
      * Used to notify SysUI that Listening has be requested.
@@ -147,12 +143,8 @@
      * Note that this is not guaranteed to be called between {@link #onCreate()}
      * and {@link #onStartListening()}, it will only be called when the tile is added
      * and not on subsequent binds.
-     *
-     * @see #TILE_MODE_PASSIVE
-     * @see #TILE_MODE_ACTIVE
      */
-    public int onTileAdded() {
-        return TILE_MODE_PASSIVE;
+    public void onTileAdded() {
     }
 
     /**
@@ -386,15 +378,7 @@
                     }
                     break;
                 case MSG_TILE_ADDED:
-                    int mode = TileService.this.onTileAdded();
-                    if (mService == null) {
-                        return;
-                    }
-                    try {
-                        mService.setTileMode(new ComponentName(TileService.this,
-                                TileService.this.getClass()), mode);
-                    } catch (RemoteException e) {
-                    }
+                    TileService.this.onTileAdded();
                     break;
                 case MSG_TILE_REMOVED:
                     if (mListening) {
@@ -431,8 +415,8 @@
     /**
      * Requests that a tile be put in the listening state so it can send an update.
      *
-     * This method is only applicable to tiles that return {@link #TILE_MODE_ACTIVE} from
-     * {@link #onTileAdded()}, and will do nothing otherwise.
+     * This method is only applicable to tiles that have {@link #META_DATA_ACTIVE_TILE} defined
+     * as true on their TileService Manifest declaration, and will do nothing otherwise.
      */
     public static final void requestListeningState(Context context, ComponentName component) {
         Intent intent = new Intent(ACTION_REQUEST_LISTENING);
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 29a72fd..f1c8c7d 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -31,12 +31,7 @@
             throw new IllegalArgumentException("Path string can not be null.");
         }
         Path path = new Path();
-        boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
-                pathString.length());
-        if (!hasValidPathData) {
-            throw new IllegalArgumentException("Path string: " + pathString +
-                    " does not contain valid path data");
-        }
+        nParseStringForPath(path.mNativePath, pathString, pathString.length());
         return path;
     }
 
@@ -104,7 +99,6 @@
             }
             super.finalize();
         }
-
     }
 
     /**
@@ -123,7 +117,7 @@
     }
 
     // Native functions are defined below.
-    private static native boolean nParseStringForPath(long pathPtr, String pathString,
+    private static native void nParseStringForPath(long pathPtr, String pathString,
             int stringLength);
     private static native void nCreatePathFromPathData(long outPathPtr, long pathData);
     private static native long nCreateEmptyPathData();
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c972476..f44d4c1a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -485,15 +485,25 @@
     }
 
     /**
-     * Stops any rendering into the surface. Use this if it is unclear whether
+     * Halts any current rendering into the surface. Use this if it is unclear whether
      * or not the surface used by the HardwareRenderer will be changing. It
-     * Suspends any rendering into the surface, but will not do any destruction
+     * Suspends any rendering into the surface, but will not do any destruction.
+     *
+     * Any subsequent draws will override the pause, resuming normal operation.
      */
     boolean pauseSurface(Surface surface) {
         return nPauseSurface(mNativeProxy, surface);
     }
 
     /**
+     * Hard stops or resumes rendering into the surface. This flag is used to
+     * determine whether or not it is safe to use the given surface *at all*
+     */
+    void setStopped(boolean stopped) {
+        nSetStopped(mNativeProxy, stopped);
+    }
+
+    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
@@ -988,6 +998,7 @@
     private static native void nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native boolean nPauseSurface(long nativeProxy, Surface window);
+    private static native void nSetStopped(long nativeProxy, boolean stopped);
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
     private static native void nSetLightCenter(long nativeProxy,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b0aa5c3..7e51096 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8877,7 +8877,7 @@
      * @hide
      */
     public void clearAccessibilityFocus() {
-        clearAccessibilityFocusNoCallbacks();
+        clearAccessibilityFocusNoCallbacks(0);
 
         // Clear the global reference of accessibility focus if this view or
         // any of its descendants had accessibility focus. This will NOT send
@@ -8920,14 +8920,27 @@
     /**
      * Clears accessibility focus without calling any callback methods
      * normally invoked in {@link #clearAccessibilityFocus()}. This method
-     * is used for clearing accessibility focus when giving this focus to
-     * another view.
+     * is used separately from that one for clearing accessibility focus when
+     * giving this focus to another view.
+     *
+     * @param action The action, if any, that led to focus being cleared. Set to
+     * AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS to specify that focus is moving within
+     * the window.
      */
-    void clearAccessibilityFocusNoCallbacks() {
+    void clearAccessibilityFocusNoCallbacks(int action) {
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+                event.setAction(action);
+                if (mAccessibilityDelegate != null) {
+                    mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
+                } else {
+                    sendAccessibilityEventUnchecked(event);
+                }
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 4c75935..3e277eb 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -41,6 +41,22 @@
      * As a result {@link AnimatorListener#onAnimationEnd(Animator)}
      * will occur after the animation has ended, but it may be delayed depending
      * on thread responsiveness.
+     * <p>
+     * Note that if any start delay is set on the reveal animator, the start radius
+     * will not be applied to the reveal circle until the start delay has passed.
+     * If it's desired to set a start radius on the reveal circle during the start
+     * delay, one workaround could be adding an animator with the same start and
+     * end radius. For example:
+     * <pre><code>
+     * public static Animator createRevealWithDelay(View view, int centerX, int centerY, float startRadius, float endRadius) {
+     *     Animator delayAnimator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, startRadius);
+     *     delayAnimator.setDuration(delayTimeMS);
+     *     Animator revealAnimator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, endRadius);
+     *     AnimatorSet set = new AnimatorSet();
+     *     set.playSequentially(delayAnimator, revealAnimator);
+     *     return set;
+     * }
+     * </code></pre>
      *
      * @param view The View will be clipped to the animating circle.
      * @param centerX The x coordinate of the center of the animating circle, relative to
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5b2877f..94c4cef 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1079,13 +1079,16 @@
     void setWindowStopped(boolean stopped) {
         if (mStopped != stopped) {
             mStopped = stopped;
+            final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer;
+            if (renderer != null) {
+                if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+                renderer.setStopped(mStopped);
+            }
             if (!mStopped) {
                 scheduleTraversals();
             } else {
-                if (mAttachInfo.mHardwareRenderer != null) {
-                    if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
-                    mAttachInfo.mHardwareRenderer.updateSurface(null);
-                    mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
+                if (renderer != null) {
+                    renderer.destroyHardwareResources(mView);
                 }
             }
         }
@@ -3071,7 +3074,8 @@
 
             // Clear accessibility focus on the host after clearing state since
             // this method may be reentrant.
-            focusHost.clearAccessibilityFocusNoCallbacks();
+            focusHost.clearAccessibilityFocusNoCallbacks(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
 
             AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
             if (provider != null) {
@@ -3088,7 +3092,8 @@
         }
         if (mAccessibilityFocusedHost != null) {
             // Clear accessibility focus in the view.
-            mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
+            mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
         }
 
         // Set the new focus host and node.
@@ -6623,7 +6628,7 @@
             // Error state: virtual view with no provider. Clear focus.
             mAccessibilityFocusedHost = null;
             mAccessibilityFocusedVirtualView = null;
-            focusedHost.clearAccessibilityFocusNoCallbacks();
+            focusedHost.clearAccessibilityFocusNoCallbacks(0);
             return;
         }
 
@@ -6673,7 +6678,7 @@
         if (mAccessibilityFocusedVirtualView == null) {
             // Error state: The node no longer exists. Clear focus.
             mAccessibilityFocusedHost = null;
-            focusedHost.clearAccessibilityFocusNoCallbacks();
+            focusedHost.clearAccessibilityFocusNoCallbacks(0);
 
             // This will probably fail, but try to keep the provider's internal
             // state consistent by clearing focus.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c372c30..ddad23a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1701,6 +1701,14 @@
          */
         public int accessibilityIdOfAnchor = -1;
 
+        /**
+         * The window title isn't kept in sync with what is displayed in the title bar, so we
+         * separately track the currently shown title to provide to accessibility.
+         *
+         * @hide
+         */
+        public CharSequence accessibilityTitle;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1749,6 +1757,7 @@
                 title = "";
 
             mTitle = TextUtils.stringOrSpannedString(title);
+            accessibilityTitle = mTitle;
         }
 
         public final CharSequence getTitle() {
@@ -1808,6 +1817,7 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
+            TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1859,6 +1869,7 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
+            accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1900,6 +1911,8 @@
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
         /** {@hide} */
+        public static final int ACCESSIBILITY_TITLE_CHANGED = 1 << 25;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -2065,6 +2078,13 @@
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
             }
 
+            if (!Objects.equals(accessibilityTitle, o.accessibilityTitle)
+                    && o.accessibilityTitle != null) {
+                // NOTE: accessibilityTitle only copied if the originator set one.
+                accessibilityTitle = o.accessibilityTitle;
+                changes |= ACCESSIBILITY_TITLE_CHANGED;
+            }
+
             return changes;
         }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a4e489c..ed6ab56 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -41,12 +41,12 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
-import android.provider.DocumentsContract;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -70,6 +70,7 @@
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.google.android.collect.Lists;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -89,6 +90,7 @@
     private IntentSender mChosenComponentSender;
     private IntentSender mRefinementIntentSender;
     private RefinementResultReceiver mRefinementResultReceiver;
+    private ChooserTarget[] mCallerChooserTargets;
 
     private Intent mReferrerFillInIntent;
 
@@ -97,6 +99,7 @@
 
     private SharedPreferences mPinnedSharedPrefs;
     private static final float PINNED_TARGET_SCORE_BOOST = 1000.f;
+    private static final float CALLER_TARGET_SCORE_BOOST = 900.f;
     private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings";
     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
 
@@ -219,6 +222,34 @@
                 Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
         setSafeForwardingMode(true);
 
+        pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
+        if (pa != null) {
+            ComponentName[] names = new ComponentName[pa.length];
+            for (int i = 0; i < pa.length; i++) {
+                if (!(pa[i] instanceof ComponentName)) {
+                    Log.w(TAG, "Filtered component #" + i + " not a ComponentName: " + pa[i]);
+                    names = null;
+                    break;
+                }
+                names[i] = (ComponentName) pa[i];
+            }
+            setFilteredComponents(names);
+        }
+
+        pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
+        if (pa != null) {
+            ChooserTarget[] targets = new ChooserTarget[pa.length];
+            for (int i = 0; i < pa.length; i++) {
+                if (!(pa[i] instanceof ChooserTarget)) {
+                    Log.w(TAG, "Chooser target #" + i + " not a ChooserTarget: " + pa[i]);
+                    targets = null;
+                    break;
+                }
+                targets[i] = (ChooserTarget) pa[i];
+            }
+            mCallerChooserTargets = targets;
+        }
+
         mPinnedSharedPrefs = getPinnedSharedPrefs(this);
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
@@ -292,6 +323,9 @@
             boolean alwaysUseOption) {
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
         mChooserListAdapter = (ChooserListAdapter) adapter;
+        if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
+            mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets));
+        }
         mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
         mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
         adapterView.setAdapter(mChooserRowAdapter);
@@ -427,13 +461,19 @@
                         continue;
                     }
                 } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Could not look up service " + serviceComponent, e);
+                    Log.e(TAG, "Could not look up service " + serviceComponent
+                            + "; component name not found");
                     continue;
                 }
 
                 final ChooserTargetServiceConnection conn =
                         new ChooserTargetServiceConnection(this, dri);
-                if (bindService(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND)) {
+
+                // Explicitly specify Process.myUserHandle instead of calling bindService
+                // to avoid the warning from calling from the system process without an explicit
+                // user handle
+                if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
+                        Process.myUserHandle())) {
                     if (DEBUG) {
                         Log.d(TAG, "Binding service connection for target " + dri
                                 + " intent " + serviceIntent);
@@ -635,7 +675,11 @@
             if (mSourceInfo != null) {
                 return mSourceInfo.getResolvedIntent();
             }
-            return getTargetIntent();
+
+            final Intent targetIntent = new Intent(getTargetIntent());
+            targetIntent.setComponent(mChooserTarget.getComponentName());
+            targetIntent.putExtras(mChooserTarget.getIntentExtras());
+            return targetIntent;
         }
 
         @Override
@@ -650,8 +694,7 @@
         }
 
         private Intent getBaseIntentToSend() {
-            Intent result = mSourceInfo != null
-                    ? mSourceInfo.getResolvedIntent() : getTargetIntent();
+            Intent result = getResolvedIntent();
             if (result == null) {
                 Log.e(TAG, "ChooserTargetInfo: no base intent available to send");
             } else {
@@ -677,7 +720,19 @@
             }
             intent.setComponent(mChooserTarget.getComponentName());
             intent.putExtras(mChooserTarget.getIntentExtras());
-            activity.startActivityAsCaller(intent, options, true, userId);
+
+            // Important: we will ignore the target security checks in ActivityManager
+            // if and only if the ChooserTarget's target package is the same package
+            // where we got the ChooserTargetService that provided it. This lets a
+            // ChooserTargetService provide a non-exported or permission-guarded target
+            // to the chooser for the user to pick.
+            //
+            // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere
+            // so we'll obey the caller's normal security checks.
+            final boolean ignoreTargetSecurity = mSourceInfo != null
+                    && mSourceInfo.getResolvedComponentName().getPackageName()
+                    .equals(mChooserTarget.getComponentName().getPackageName());
+            activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId);
             return true;
         }
 
@@ -810,6 +865,9 @@
 
         @Override
         public float getScore(DisplayResolveInfo target) {
+            if (target == null) {
+                return CALLER_TARGET_SCORE_BOOST;
+            }
             float score = super.getScore(target);
             if (target.isPinned()) {
                 score += PINNED_TARGET_SCORE_BOOST;
@@ -1281,7 +1339,7 @@
     }
 
     static class ChooserTargetServiceConnection implements ServiceConnection {
-        private final DisplayResolveInfo mOriginalTarget;
+        private DisplayResolveInfo mOriginalTarget;
         private ComponentName mConnectedComponent;
         private ChooserActivity mChooserActivity;
         private final Object mLock = new Object();
@@ -1359,6 +1417,7 @@
         public void destroy() {
             synchronized (mLock) {
                 mChooserActivity = null;
+                mOriginalTarget = null;
             }
         }
 
@@ -1366,7 +1425,9 @@
         public String toString() {
             return "ChooserTargetServiceConnection{service="
                     + mConnectedComponent + ", activity="
-                    + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}";
+                    + (mOriginalTarget != null
+                    ? mOriginalTarget.getResolveInfo().activityInfo.toString()
+                    : "<connection destroyed>") + "}";
         }
     }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ff680e2..f2bf9e1 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -105,6 +105,7 @@
     private final ArrayList<Intent> mIntents = new ArrayList<>();
     private ResolverComparator mResolverComparator;
     private PickTargetOptionRequest mPickOptionRequest;
+    private ComponentName[] mFilteredComponents;
 
     protected ResolverDrawerLayout mResolverDrawerLayout;
 
@@ -332,6 +333,24 @@
         }
     }
 
+    public final void setFilteredComponents(ComponentName[] components) {
+        mFilteredComponents = components;
+    }
+
+    public final boolean isComponentFiltered(ComponentInfo component) {
+        if (mFilteredComponents == null) {
+            return false;
+        }
+
+        final ComponentName checkName = component.getComponentName();
+        for (ComponentName name : mFilteredComponents) {
+            if (name.equals(checkName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Perform any initialization needed for voice interaction.
      */
@@ -1269,7 +1288,8 @@
                                 ai.applicationInfo.uid, ai.exported);
                         boolean suspended = (ai.applicationInfo.flags
                                 & ApplicationInfo.FLAG_SUSPENDED) != 0;
-                        if (granted != PackageManager.PERMISSION_GRANTED || suspended) {
+                        if (granted != PackageManager.PERMISSION_GRANTED || suspended
+                                || isComponentFiltered(ai)) {
                             // Access not allowed!
                             if (mOrigResolveList == currentResolveList) {
                                 mOrigResolveList = new ArrayList<>(mOrigResolveList);
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 7252276..cd4f7b6 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -372,29 +372,35 @@
          * Throw an exception if one of a variety of internal consistency checks fails.
          */
         private void assertConsistency() {
-            // Assert that our sequewnce number has been initialized. If it hasn't
-            // that means someone tried to read or write data without allocating it
-            // since we were created or reset.
-            if (mSequence == UNINITIALIZED_SEQUENCE) {
-                logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
-                        + " SparseMappingTable.Table.  -- "
-                        + dumpInternalState());
-                return;
-            }
+            // Something with this checking isn't working and is triggering
+            // more problems than it's helping to debug.
+            //   Original bug: b/27045736
+            //   New bug: b/27960286
+            if (false) {
+                // Assert that our sequence number has been initialized. If it hasn't
+                // that means someone tried to read or write data without allocating it
+                // since we were created or reset.
+                if (mSequence == UNINITIALIZED_SEQUENCE) {
+                    logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
+                            + " SparseMappingTable.Table.  -- "
+                            + dumpInternalState());
+                    return;
+                }
 
-            // Assert that our sequence number matches mParent's.  If it isn't that means
-            // we have been reset and our
-            if (mSequence != mParent.mSequence) {
-                if (mSequence < mParent.mSequence) {
-                    logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
-                            + " called but not Table.resetTable() -- "
-                            + dumpInternalState());
-                    return;
-                } else if (mSequence > mParent.mSequence) {
-                    logOrThrow("Sequence mismatch. Table.resetTable()"
-                            + " called but not SparseMappingTable.resetTable() -- "
-                            + dumpInternalState());
-                    return;
+                // Assert that our sequence number matches mParent's.  If it isn't that means
+                // we have been reset and our
+                if (mSequence != mParent.mSequence) {
+                    if (mSequence < mParent.mSequence) {
+                        logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
+                                + " called but not Table.resetTable() -- "
+                                + dumpInternalState());
+                        return;
+                    } else if (mSequence > mParent.mSequence) {
+                        logOrThrow("Sequence mismatch. Table.resetTable()"
+                                + " called but not SparseMappingTable.resetTable() -- "
+                                + dumpInternalState());
+                        return;
+                    }
                 }
             }
         }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0f257d7..c64328b 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -516,8 +516,8 @@
         }
         mTitle = title;
         WindowManager.LayoutParams params = getAttributes();
-        if (!TextUtils.equals(title, params.getTitle())) {
-            params.setTitle(title);
+        if (!TextUtils.equals(title, params.accessibilityTitle)) {
+            params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
             dispatchWindowAttributesChanged(getAttributes());
         }
     }
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 6dc251c..1c2d13d 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -53,36 +53,14 @@
     fontFamily->Unref();
 }
 
-static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData,
+        size_t fontSize, int ttcIndex) {
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex);
     bool result = family->addFont(minikinFont);
     minikinFont->Unref();
     return result;
 }
 
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
-        jint ttcIndex) {
-    NPE_CHECK_RETURN_ZERO(env, path);
-    ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
-    if (face == NULL) {
-        ALOGE("addFont failed to create font %s", str.c_str());
-        return false;
-    }
-    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
-}
-
-static struct {
-    jmethodID mGet;
-    jmethodID mSize;
-} gListClassInfo;
-
-static struct {
-    jfieldID mTag;
-    jfieldID mStyleValue;
-} gAxisClassInfo;
-
 static void release_global_ref(const void* /*data*/, void* context) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     bool needToAttach = (env == NULL);
@@ -106,6 +84,47 @@
     }
 }
 
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf,
+        jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, bytebuf);
+    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
+    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+    SkFontMgr::FontParameters params;
+    params.setCollectionIndex(ttcIndex);
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    SkTypeface* face = fm->createFromStream(fontData.release(), params);
+    if (face == NULL) {
+        ALOGE("addFont failed to create font");
+        return false;
+    }
+    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
+    return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex);
+}
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
         jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
@@ -133,7 +152,7 @@
         }
     }
 
-    void* fontPtr = env->GetDirectBufferAddress(font);
+    const void* fontPtr = env->GetDirectBufferAddress(font);
     if (fontPtr == NULL) {
         ALOGE("addFont failed to create font, buffer invalid");
         return false;
@@ -159,7 +178,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex);
     fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
     minikinFont->Unref();
     return true;
@@ -191,6 +210,7 @@
         return false;
     }
 
+    size_t bufSize = asset->getLength();
     SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset));
     SkMemoryStream* stream = new SkMemoryStream(data);
     // CreateFromStream takes ownership of stream.
@@ -200,7 +220,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
+    return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -208,7 +228,7 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
-    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+    { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index b04293e..e5c4a2d 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -176,6 +176,9 @@
     PathParser::ParseResult result;
     PathData data;
     PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
     path->mutateStagingProperties()->setData(data);
     env->ReleaseStringUTFChars(inputStr, pathString);
 }
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0927120..0c867f1 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "jni.h"
+#include "GraphicsJNI.h"
 
 #include <PathParser.h>
 #include <SkPath.h>
@@ -27,7 +28,7 @@
 
 using namespace uirenderer;
 
-static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
         jint strLength) {
     const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
     SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
@@ -36,9 +37,8 @@
     PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputPathStr, pathString);
     if (result.failureOccurred) {
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
     }
-    return !result.failureOccurred;
 }
 
 static long createEmptyPathData(JNIEnv*, jobject) {
@@ -62,7 +62,7 @@
         return reinterpret_cast<jlong>(pathData);
     } else {
         delete pathData;
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
         return NULL;
     }
 }
@@ -100,7 +100,7 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-    {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath},
+    {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath},
     {"nCreateEmptyPathData", "!()J", (void*)createEmptyPathData},
     {"nCreatePathData", "!(J)J", (void*)createPathData},
     {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d65209..ef45c87 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -479,6 +479,12 @@
     return proxy->pauseSurface(surface);
 }
 
+static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean stopped) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setStopped(stopped);
+}
+
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
         jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -732,6 +738,7 @@
     { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
+    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
     { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
new file mode 100644
index 0000000..23809d5
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 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"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_done.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
new file mode 100644
index 0000000..a0ebf92
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M18,32.34L9.66,24l-2.83,2.83L18,38l24,-24 -2.83,-2.83z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_go.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
new file mode 100644
index 0000000..c24f5a0
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M6,22h28.34l-7.17,-7.17L30,12l12,12 -12,12 -2.83,-2.83L34.34,26H6z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_next.xml b/core/res/res/drawable/ic_input_extract_action_next.xml
new file mode 100644
index 0000000..fa0b178
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_next.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M23.17,14.83L30.34,22H2v4h28.34l-7.17,7.17L26,36l12,-12 -12,-12 -2.83,2.83zM40,12v24h4V12h-4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_previous.xml b/core/res/res/drawable/ic_input_extract_action_previous.xml
new file mode 100644
index 0000000..5e1823c
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_previous.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M22.83,14.83L15.66,22H44v4H15.66l7.17,7.17L20,36 8,24l12,-12 2.83,2.83zM6,12v24H2V12h4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_return.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
new file mode 100644
index 0000000..c46a4a2
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M38,14v8H11.66l7.17,-7.17L16,12 4,24l12,12 2.83,-2.83L11.66,26H42V14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_search.xml b/core/res/res/drawable/ic_input_extract_action_search.xml
new file mode 100644
index 0000000..fd1dcea
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_search.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M31,28h-1.59l-0.55,-0.55C30.82,25.18 32,22.23 32,19c0,-7.18 -5.82,-13 -13,-13S6,11.82 6,19s5.82,13 13,13c3.23,0 6.18,-1.18 8.45,-3.13l0.55,0.55L28,31l10,9.98L40.98,38 31,28zM19,28c-4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9 9,4.03 9,9 -4.03,9 -9,9z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_send.xml b/core/res/res/drawable/ic_input_extract_action_send.xml
new file mode 100644
index 0000000..0f3754b
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_send.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M4.02,42L46,24 4.02,6 4,20l30,4 -30,4z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/input_extract_action_bg_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
new file mode 100644
index 0000000..2457bb9
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/input_extract_action_bg_pressed_material_dark"
+        android:state_pressed="true"/>
+    <item android:drawable="@drawable/input_extract_action_bg_normal_material_dark"/>
+</selector>
diff --git a/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
new file mode 100644
index 0000000..9e36253
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_200"/>
+</shape>
diff --git a/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
new file mode 100644
index 0000000..2328ce3
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_100"/>
+</shape>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
new file mode 100644
index 0000000..cd921f1
--- /dev/null
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<android.inputmethodservice.CompactExtractEditLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:baselineAligned="false">
+
+    <android.inputmethodservice.ExtractEditText
+        android:id="@id/inputExtractEditText"
+        android:layout_width="0dp"
+        android:layout_height="24dp"
+        android:background="@null"
+        android:singleLine="true"
+        android:inputType="text"
+        android:layout_weight="1"
+        android:fontFamily="sans-serif-condensed-light"
+        android:textColor="@color/primary_text_default_material_dark"
+        android:textColorHighlight="@color/accent_material_dark"
+        android:textSize="18dp"
+        android:cursorVisible="false"
+        android:gravity="bottom|right"
+        />
+
+    <FrameLayout
+        android:id="@id/inputExtractAccessories"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="8dp"
+        android:visibility="visible">
+        <ImageButton
+            android:id="@id/inputExtractAction"
+            android:layout_width="@dimen/input_extract_action_button_width"
+            android:layout_height="@dimen/input_extract_action_button_width"
+            android:background="@drawable/input_extract_action_bg_material_dark"
+            android:padding="4dp"
+            android:scaleType="centerInside" />
+    </FrameLayout>
+</android.inputmethodservice.CompactExtractEditLayout>
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..f4b250c
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">25.2%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">7.5%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">21.4%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">5.5%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">2.1%</item>
+    <item name="input_extract_action_button_width" type="dimen">32dp</item>
+    <item name="input_extract_action_button_height" type="dimen">32dp</item>
+</resources>
diff --git a/core/res/res/values-w170dp-notround-watch/dimens.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..9f30ac1
--- /dev/null
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_padding_right" type="fraction">7.5%</item>
+</resources>
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-watch/dimens.xml
new file mode 100644
index 0000000..f79a0a5
--- /dev/null
+++ b/core/res/res/values-watch/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">17.5%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">3.6%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">2.5%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_button_width" type="dimen">24dp</item>
+    <item name="input_extract_action_button_height" type="dimen">24dp</item>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 756a94b..6d6065f 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -18,6 +18,7 @@
     <style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
 </resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 61753b1..66509fb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -19,14 +19,16 @@
     <style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod"  />
+    <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel"  />
     <style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel"  />
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
     <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-
 </resources>
 
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7399fa9..c8ca116 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -75,7 +75,9 @@
     <color name="material_grey_100">#fff5f5f5</color>
     <color name="material_grey_50">#fffafafa</color>
 
+    <color name="material_deep_teal_100">#ffb2dfdb</color>
     <color name="material_deep_teal_200">#ff80cbc4</color>
+    <color name="material_deep_teal_300">#ff4db6ac</color>
     <color name="material_deep_teal_500">#ff009688</color>
 
     <color name="material_blue_grey_800">#ff37474f</color>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d0414c3..9342eb1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1265,6 +1265,7 @@
   <java-symbol type="drawable" name="ic_corp_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_off" />
   <java-symbol type="drawable" name="ic_corp_icon_badge" />
+  <java-symbol type="drawable" name="ic_corp_user_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_no_background" />
   <java-symbol type="drawable" name="ic_corp_icon" />
   <java-symbol type="drawable" name="ic_corp_statusbar_icon" />
@@ -2541,4 +2542,23 @@
   <java-symbol type="id" name="titleDividerNoCustom" />
 
   <java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
+
+  <!-- Wearable input extract edit view -->
+  <java-symbol type="drawable" name="ic_input_extract_action_go" />
+  <java-symbol type="drawable" name="ic_input_extract_action_search" />
+  <java-symbol type="drawable" name="ic_input_extract_action_send" />
+  <java-symbol type="drawable" name="ic_input_extract_action_next" />
+  <java-symbol type="drawable" name="ic_input_extract_action_done" />
+  <java-symbol type="drawable" name="ic_input_extract_action_previous" />
+  <java-symbol type="drawable" name="ic_input_extract_action_return" />
+
+  <java-symbol type="fraction" name="input_extract_layout_height" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left_no_action" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_right" />
+  <java-symbol type="fraction" name="input_extract_text_margin_bottom" />
+  <java-symbol type="fraction" name="input_extract_action_margin_bottom" />
+
+  <java-symbol type="dimen" name="input_extract_action_button_width" />
+  <java-symbol type="dimen" name="input_extract_action_button_height" />
 </resources>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 478d66c..25a6e00 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -83,4 +83,18 @@
         <item name="fontFamily">sans-serif-condensed-light</item>
         <item name="textColor">@color/micro_text_light</item>
     </style>
+
+   <style name="Theme.Micro.Panel" parent="Theme.Material.Panel"  />
+   <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel"  />
+
+    <!-- Default theme for material style input methods, which is used by the
+         {@link android.inputmethodservice.InputMethodService} class.
+         This inherits from Theme.Panel, but sets up IME appropriate animations
+         and a few custom attributes. -->
+    <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
+        <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+        <item name="imeFullscreenBackground">#1e282c</item>
+        <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+        <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+    </style>
 </resources>
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index be2e6bf..702218c 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -11,6 +11,9 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := NotificationStressTests
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ub-uiautomator
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
index 4cb617e..6e86c37 100644
--- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java
+++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
@@ -16,15 +16,19 @@
 
 package android.app;
 
-
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.RepetitiveTest;
-import android.test.TimedTest;
+import android.util.Log;
 
+import java.lang.InterruptedException;
+import java.lang.reflect.Method;
 import java.util.Random;
 
 /**
@@ -34,51 +38,78 @@
 public class NotificationStressTest extends InstrumentationTestCase {
 
     private static final int NUM_ITERATIONS = 200;
+    private static final int NUM_ITERATIONS_2 = 30;
+    private static final int LONG_TIMEOUT = 2000;
+    // 50 notifications per app: defined as Variable MAX_PACKAGE_NOTIFICATIONS in
+    // NotificationManagerService.java
+    private static final int MAX_NOTIFCATIONS = 50;
     private static final int[] ICONS = new int[] {
-        android.R.drawable.stat_notify_call_mute,
-        android.R.drawable.stat_notify_chat,
-        android.R.drawable.stat_notify_error,
-        android.R.drawable.stat_notify_missed_call,
-        android.R.drawable.stat_notify_more,
-        android.R.drawable.stat_notify_sdcard,
-        android.R.drawable.stat_notify_sdcard_prepare,
-        android.R.drawable.stat_notify_sdcard_usb,
-        android.R.drawable.stat_notify_sync,
-        android.R.drawable.stat_notify_sync_noanim,
-        android.R.drawable.stat_notify_voicemail,
+            android.R.drawable.stat_notify_call_mute,
+            android.R.drawable.stat_notify_chat,
+            android.R.drawable.stat_notify_error,
+            android.R.drawable.stat_notify_missed_call,
+            android.R.drawable.stat_notify_more,
+            android.R.drawable.stat_notify_sdcard,
+            android.R.drawable.stat_notify_sdcard_prepare,
+            android.R.drawable.stat_notify_sdcard_usb,
+            android.R.drawable.stat_notify_sync,
+            android.R.drawable.stat_notify_sync_noanim,
+            android.R.drawable.stat_notify_voicemail,
     };
 
     private final Random mRandom = new Random();
     private Context mContext;
     private NotificationManager mNotificationManager;
-    private int notifyId = 0;
+    private UiDevice mDevice = null;
+    private int mNotifyId = 0;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
         mContext = getInstrumentation().getContext();
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
+        mDevice.setOrientationNatural();
+        mNotificationManager.cancelAll();
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
+        mDevice.unfreezeRotation();
         mNotificationManager.cancelAll();
     }
 
-    @RepetitiveTest(numIterations=NUM_ITERATIONS)
+    @RepetitiveTest(numIterations = NUM_ITERATIONS)
     public void testNotificationStress() {
         // Cancel one of every five notifications to vary load on notification manager
-        if (notifyId % 5 == 4) {
-            mNotificationManager.cancel(notifyId - 4);
+        if (mNotifyId % 5 == 4) {
+            mNotificationManager.cancel(mNotifyId - 4);
         }
-        sendNotification(notifyId++, "testNotificationStressNotify");
+        sendNotification(mNotifyId++, "testNotificationStressNotify");
+    }
+
+    @RepetitiveTest(numIterations = NUM_ITERATIONS_2)
+    public void testNotificationsWithShadeStress() throws Exception {
+        mDevice.openNotification();
+        Thread.sleep(LONG_TIMEOUT);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            sendNotification(mNotifyId++, "testNotificationStressNotify");
+        }
+        Thread.sleep(500);
+        assertTrue(mNotificationManager.getActiveNotifications().length == MAX_NOTIFCATIONS);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            mNotificationManager.cancel(--mNotifyId);
+        }
+        if (isLockScreen()) {
+            fail("Notification stress test failed, back to lockscreen");
+        }
     }
 
     private void sendNotification(int id, CharSequence text) {
         // Fill in arbitrary content
-        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
+        Intent intent = new Intent(Intent.ACTION_VIEW);
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
         CharSequence title = text + " " + id;
         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
@@ -90,8 +121,19 @@
                 .setContentTitle(title)
                 .setContentText(subtitle)
                 .setContentIntent(pendingIntent)
+                .setPriority(Notification.PRIORITY_HIGH)
                 .build();
         mNotificationManager.notify(id, notification);
         SystemClock.sleep(10);
     }
+
+    private boolean isLockScreen() {
+        KeyguardManager myKM = (KeyguardManager) mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        if (myKM.inKeyguardRestrictedInputMode()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index f741e3c..e48bf79 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,8 +17,12 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
+import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.List;
 
 /**
@@ -27,6 +31,9 @@
  * @hide
  */
 public class FontFamily {
+
+    private static String TAG = "FontFamily";
+
     /**
      * @hide
      */
@@ -62,7 +69,15 @@
     }
 
     public boolean addFont(String path, int ttcIndex) {
-        return nAddFont(mNativePtr, path, ttcIndex);
+        try (FileInputStream file = new FileInputStream(path)) {
+            FileChannel fileChannel = file.getChannel();
+            long fontSize = fileChannel.size();
+            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+            return nAddFont(mNativePtr, fontBuffer, ttcIndex);
+        } catch (IOException e) {
+            Log.e(TAG, "Error mapping font file " + path);
+            return false;
+        }
     }
 
     public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
@@ -76,7 +91,7 @@
 
     private static native long nCreateFamily(String lang, int variant);
     private static native void nUnrefFamily(long nativePtr);
-    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
+    private static native boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 4e9ac9c..7e85333 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -156,6 +156,12 @@
     return;
 }
 
+bool PathParser::isVerbValid(char verb) {
+    verb = tolower(verb);
+    return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q'
+            || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
+}
+
 void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
         const char* pathStr, size_t strLen) {
     if (pathStr == NULL) {
@@ -171,6 +177,12 @@
         end = nextStart(pathStr, strLen, end);
         std::vector<float> points;
         getFloats(&points, result, pathStr, start, end);
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+        }
+        // If either verb or points is not valid, return immediately.
         if (result->failureOccurred) {
             return;
         }
@@ -182,10 +194,15 @@
     }
 
     if ((end - start) == 1 && start < strLen) {
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+            return;
+        }
         data->verbs.push_back(pathStr[start]);
         data->verbSizes.push_back(0);
     }
-    return;
 }
 
 void PathParser::dump(const PathData& data) {
@@ -218,7 +235,8 @@
     // Check if there is valid data coming out of parsing the string.
     if (pathData.verbs.size() == 0) {
         result->failureOccurred = true;
-        result->failureMessage = "No verbs found in the string for pathData";
+        result->failureMessage = "No verbs found in the string for pathData: ";
+        result->failureMessage += pathStr;
         return;
     }
     VectorDrawableUtils::verbsToPath(skPath, pathData);
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index c4bbb74..180a7a3 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -44,6 +44,7 @@
     ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
             const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
+    static bool isVerbValid(char verb);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b9e3358..b39de26 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,8 +22,9 @@
 
 namespace android {
 
-MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
-    mTypeface(typeface) {
+MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
+        int ttcIndex) :
+    mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
 }
 
 MinikinFontSkia::~MinikinFontSkia() {
@@ -66,22 +67,38 @@
     bounds->mBottom = skBounds.fBottom;
 }
 
-bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
-    if (buf == NULL) {
-        const size_t tableSize = mTypeface->getTableSize(tag);
-        *size = tableSize;
-        return tableSize != 0;
-    } else {
-        const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
-        *size = actualSize;
-        return actualSize != 0;
+const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
+    // we don't have a buffer to the font data, copy to own buffer
+    const size_t tableSize = mTypeface->getTableSize(tag);
+    *size = tableSize;
+    if (tableSize == 0) {
+        return nullptr;
     }
+    void* buf = malloc(tableSize);
+    if (buf == nullptr) {
+        return nullptr;
+    }
+    mTypeface->getTableData(tag, 0, tableSize, buf);
+    *destroy = free;
+    return buf;
 }
 
 SkTypeface *MinikinFontSkia::GetSkTypeface() const {
     return mTypeface;
 }
 
+const void* MinikinFontSkia::GetFontData() const {
+    return mFontData;
+}
+
+size_t MinikinFontSkia::GetFontSize() const {
+    return mFontSize;
+}
+
+int MinikinFontSkia::GetFontIndex() const {
+    return mTtcIndex;
+}
+
 int32_t MinikinFontSkia::GetUniqueId() const {
     return mTypeface->uniqueID();
 }
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 1d50168..dbc65f9 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,7 +28,8 @@
 class ANDROID_API MinikinFontSkia : public MinikinFont {
 public:
     // Note: this takes ownership of the reference (will unref on dtor)
-    explicit MinikinFontSkia(SkTypeface *typeface);
+    explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize,
+        int ttcIndex);
 
     ~MinikinFontSkia();
 
@@ -38,20 +39,30 @@
     void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
         const MinikinPaint &paint) const;
 
-    // If buf is NULL, just update size
-    bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
+    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
 
     int32_t GetUniqueId() const;
 
     SkTypeface* GetSkTypeface() const;
 
+    // Access to underlying raw font bytes
+    const void* GetFontData() const;
+    size_t GetFontSize() const;
+    int GetFontIndex() const;
+
     static uint32_t packPaintFlags(const SkPaint* paint);
     static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
 
     // set typeface and fake bold/italic parameters
     static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
 private:
-    SkTypeface *mTypeface;
+    SkTypeface* mTypeface;
+
+    // A raw pointer to the font data - it should be owned by some other object with
+    // lifetime at least as long as this object.
+    const void* mFontData;
+    size_t mFontSize;
+    int mTtcIndex;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index fa8ad5d..c583988 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -66,7 +66,10 @@
         ALOGD("makeFontCollection adding %s", fn);
         SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
         if (skFace != NULL) {
-            MinikinFont *font = new MinikinFontSkia(skFace);
+            // TODO: might be a nice optimization to get access to the underlying font
+            // data, but would require us opening the file ourselves and passing that
+            // to the appropriate Create method of SkTypeface.
+            MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0);
             family->addFont(font);
             font->Unref();
         } else {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ab66b2a..890d4a1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -113,18 +113,11 @@
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
         mHaveNewSurface = true;
         mSwapHistory.clear();
-        makeCurrent();
     } else {
         mRenderThread.removeFrameCallback(this);
     }
 }
 
-void CanvasContext::requireSurface() {
-    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-            "requireSurface() called but no surface set!");
-    makeCurrent();
-}
-
 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
     mSwapBehavior = swapBehavior;
 }
@@ -146,6 +139,18 @@
     return mRenderThread.removeFrameCallback(this);
 }
 
+void CanvasContext::setStopped(bool stopped) {
+    if (mStopped != stopped) {
+        mStopped = stopped;
+        if (mStopped) {
+            mRenderThread.removeFrameCallback(this);
+            if (mEglManager.isCurrent(mEglSurface)) {
+                mEglManager.makeCurrent(EGL_NO_SURFACE);
+            }
+        }
+    }
+}
+
 // TODO: don't pass viewport size, it's automatic via EGL
 void CanvasContext::setup(int width, int height, float lightRadius,
         uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
@@ -172,7 +177,9 @@
     mOpaque = opaque;
 }
 
-void CanvasContext::makeCurrent() {
+bool CanvasContext::makeCurrent() {
+    if (mStopped) return false;
+
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
     EGLint error = 0;
@@ -180,6 +187,7 @@
     if (error) {
         setSurface(nullptr);
     }
+    return !error;
 }
 
 static bool wasSkipped(FrameInfo* info) {
@@ -671,7 +679,7 @@
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    requireSurface();
+    mEglManager.initialize();
     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 9350114..52df3abe 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -82,13 +82,14 @@
     void initialize(Surface* surface);
     void updateSurface(Surface* surface);
     bool pauseSurface(Surface* surface);
+    void setStopped(bool stopped);
     bool hasSurface() { return mNativeSurface.get(); }
 
     void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightCenter(const Vector3& lightCenter);
     void setOpaque(bool opaque);
-    void makeCurrent();
+    bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
             int64_t syncQueued, RenderNode* target);
     void draw();
@@ -172,7 +173,6 @@
     friend class android::uirenderer::RenderState;
 
     void setSurface(Surface* window);
-    void requireSurface();
 
     void freePrefetchedLayers(TreeObserver* observer);
 
@@ -185,6 +185,7 @@
     EglManager& mEglManager;
     sp<Surface> mNativeSurface;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mStopped = false;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
     struct SwapHistory {
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 651aaa2..ed472ac 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -115,7 +115,7 @@
     ATRACE_CALL();
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
-    mContext->makeCurrent();
+    bool canDraw = mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse(mContext);
 
     for (size_t i = 0; i < mLayers.size(); i++) {
@@ -126,8 +126,9 @@
 
     // This is after the prepareTree so that any pending operations
     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
-    if (CC_UNLIKELY(!mContext->hasSurface())) {
+    if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
         mSyncResult |= kSync_LostSurfaceRewardIfFound;
+        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8def7ad..ac6a28f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -270,12 +270,6 @@
         // Ensure we always have a valid surface & context
         surface = mPBufferSurface;
     }
-    // TODO: Temporary to help diagnose b/27286867
-    if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) {
-        ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface,
-                mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "",
-                        surface, surface == mPBufferSurface ? " (pbuffer)" : "");
-    }
     if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
         if (errOut) {
             *errOut = eglGetError();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 2e99d0b..c5a2dc7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,14 +52,6 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-namespace DumpFlags {
-    enum {
-        FrameStats = 1 << 0,
-        Reset      = 1 << 1,
-        JankStats  = 1 << 2,
-    };
-};
-
 CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
         RenderNode* rootRenderNode, IContextFactory* contextFactory) {
     return new CanvasContext(*args->thread, args->translucent,
@@ -175,6 +167,18 @@
     return (bool) postAndWait(task);
 }
 
+CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
+    args->context->setStopped(args->stopped);
+    return nullptr;
+}
+
+void RenderProxy::setStopped(bool stopped) {
+    SETUP_TASK(setStopped);
+    args->context = mContext;
+    args->stopped = stopped;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
         float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
     args->context->setup(args->width, args->height, args->lightRadius,
@@ -425,6 +429,9 @@
     if (args->dumpFlags & DumpFlags::Reset) {
         args->context->resetFrameStats();
     }
+    if (args->dumpFlags & DumpFlags::JankStats) {
+        args->thread->jankTracker().dump(args->fd);
+    }
     return nullptr;
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 97194fe..32d3283 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -50,6 +50,14 @@
 class RenderThread;
 class RenderProxyBridge;
 
+namespace DumpFlags {
+    enum {
+        FrameStats = 1 << 0,
+        Reset      = 1 << 1,
+        JankStats  = 1 << 2,
+    };
+};
+
 /*
  * RenderProxy is strictly single threaded. All methods must be invoked on the owning
  * thread. It is important to note that RenderProxy may be deleted while it has
@@ -71,6 +79,7 @@
     ANDROID_API void initialize(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
+    ANDROID_API void setStopped(bool stopped);
     ANDROID_API void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index cc0fdd5..c5af061 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -122,5 +122,5 @@
         }
     }
 
-    proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+    proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
 }
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 7208547..796169e 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -231,11 +231,12 @@
 };
 
 const StringPath sStringPaths[] = {
-    {"3e...3", false},
-    {"L.M.F.A.O", false},
-    {"m 1 1", true},
-    {"z", true},
-    {"1-2e34567", false}
+    {"3e...3", false}, // Not starting with a verb and ill-formatted float
+    {"L.M.F.A.O", false}, // No floats following verbs
+    {"m 1 1", true}, // Valid path data
+    {"z", true}, // Valid path data
+    {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+    {"f 4 5", false} // Invalid verb
 };
 
 
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 0763403..b77ff10 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -17,4 +17,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name">Android Services Library</string>
     <string name="notification_ranker">Android Notification Ranking Service</string>
+    <string name="notification_ranker_autobundle_explanation">Auto-grouping updated by Ranking Service</string>
 </resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
index 0b2b1a4..3ef2aea 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Ranker.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
@@ -16,16 +16,36 @@
 
 package android.ext.services.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+
+import android.os.Bundle;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationRankerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import android.ext.services.R;
 
 /**
  * Class that provides an updatable ranker module for the notification manager..
  */
 public final class Ranker extends NotificationRankerService {
     private static final String TAG = "RocketRanker";
-    private static final boolean DEBUG =  Log.isLoggable(TAG, Log.DEBUG);;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final int AUTOBUNDLE_AT_COUNT = 4;
+    private static final String AUTOBUNDLE_KEY = "ranker_bundle";
+
+    // Map of package : notification keys. Only contains notifications that are not bundled
+    // by the app (aka no group or sort key).
+    Map<String, LinkedHashSet<String>> mUnbundledNotifications;
 
     @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance,
@@ -37,10 +57,146 @@
     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
         if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+        try {
+            List<String> notificationsToBundle = new ArrayList<>();
+            if (!sbn.isGroup()) {
+                // Not grouped by the app, add to the list of notifications for the app;
+                // send bundling update if app exceeds the autobundling limit.
+                synchronized (mUnbundledNotifications) {
+                    LinkedHashSet<String> notificationsForPackage
+                            = mUnbundledNotifications.get(sbn.getPackageName());
+                    if (notificationsForPackage == null) {
+                        notificationsForPackage = new LinkedHashSet<>();
+                    }
+                    if (notificationsForPackage.contains(sbn.getKey())) {
+                        return;
+                    }
+                    notificationsForPackage.add(sbn.getKey());
+                    mUnbundledNotifications.put(sbn.getPackageName(), notificationsForPackage);
+
+                    if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) {
+                        // Autobundle all but the most recently posted (not updated) notification.
+                        int count = 0;
+                        for (String key : notificationsForPackage) {
+                            if (count < notificationsForPackage.size() - 1) {
+                                notificationsToBundle.add(key);
+                            }
+                            count++;
+                        }
+                    }
+                }
+                if (notificationsToBundle.size() > 0) {
+                    adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0),
+                            true);
+                    adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true);
+                }
+            } else {
+                // Grouped, but not by us. Send updates to unautobundle, if we bundled it.
+                maybeUnbundle(sbn, false);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Failure processing new notification", e);
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn) {
+        try {
+            maybeUnbundle(sbn, true);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error processing canceled notification", e);
+        }
+    }
+
+    /**
+     * Un-autobundles notifications that are now grouped by the app. Additionally cancels
+     * autobundling if the status change of this notification resulted in the loose notification
+     * count being under the limit.
+     */
+    private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone) {
+        List<String> notificationsToUnAutobundle = new ArrayList<>();
+        boolean removeSummary = false;
+        synchronized (mUnbundledNotifications) {
+            LinkedHashSet<String> notificationsForPackage
+                    = mUnbundledNotifications.get(sbn.getPackageName());
+            if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
+                return;
+            }
+            if (notificationsForPackage.remove(sbn.getKey())) {
+                if (!notificationGone) {
+                    // Add the current notification to the unbundling list if it still exists.
+                    notificationsToUnAutobundle.add(sbn.getKey());
+                }
+                // If the status change of this notification has brought the number of loose
+                // notifications back below the limit, remove the summary and un-autobundle.
+                if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) {
+                    removeSummary = true;
+                    for (String key : notificationsForPackage) {
+                        notificationsToUnAutobundle.add(key);
+                    }
+                }
+            }
+        }
+        if (notificationsToUnAutobundle.size() > 0) {
+            if (removeSummary) {
+                adjustAutobundlingSummary(sbn.getPackageName(), null, false);
+            }
+            adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false);
+        }
     }
 
     @Override
     public void onListenerConnected() {
         if (DEBUG) Log.i(TAG, "CONNECTED");
+        mUnbundledNotifications = new HashMap<>();
+        for (StatusBarNotification sbn : getActiveNotifications()) {
+            onNotificationPosted(sbn);
+        }
     }
+
+    private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded) {
+        Bundle signals = new Bundle();
+        if (summaryNeeded) {
+            signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true);
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
+        } else {
+            signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false);
+        }
+        Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
+                getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
+        if (DEBUG) {
+            Log.i(TAG, "Summary update for: " + packageName + " "
+                    + (summaryNeeded ? "adding" : "removing"));
+        }
+        try {
+            adjustNotification(adjustment);
+        } catch (Exception e) {
+            Slog.e(TAG, "Adjustment failed", e);
+        }
+
+    }
+    private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle) {
+        List<Adjustment> adjustments = new ArrayList<>();
+        for (String key : keys) {
+            adjustments.add(createBundlingAdjustment(packageName, key, bundle));
+            if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key);
+        }
+        try {
+            adjustNotifications(adjustments);
+        } catch (Exception e) {
+            Slog.e(TAG, "Adjustments failed", e);
+        }
+    }
+
+    private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle) {
+        Bundle signals = new Bundle();
+        if (bundle) {
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
+        } else {
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
+        }
+        return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
+                getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a560e3c..084acac 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -850,5 +850,7 @@
     <!-- Description for a custom screen zoom level. This shows the requested display
          density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
     <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
+    <!-- Label for Help and feedback menu item -->
+    <string name="help_feedback_label">Help &amp; feedback</string>
 
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
new file mode 100644
index 0000000..320cd58
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources.Theme;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+
+import java.net.URISyntaxException;
+import java.util.Locale;
+
+/**
+ * Functions to easily prepare contextual help menu option items with an intent that opens up the
+ * browser to a particular URL, while taking into account the preferred language and app version.
+ */
+public class HelpUtils {
+    private final static String TAG = HelpUtils.class.getSimpleName();
+
+    private static final int MENU_HELP = Menu.FIRST + 100;
+
+    /**
+     * Help URL query parameter key for the preferred language.
+     */
+    private final static String PARAM_LANGUAGE_CODE = "hl";
+
+    /**
+     * Help URL query parameter key for the app version.
+     */
+    private final static String PARAM_VERSION = "version";
+
+    // Constants for help intents.
+    private static final String EXTRA_CONTEXT = "EXTRA_CONTEXT";
+    private static final String EXTRA_THEME = "EXTRA_THEME";
+    private static final String EXTRA_PRIMARY_COLOR = "EXTRA_PRIMARY_COLOR";
+    private static final String EXTRA_BACKUP_URI = "EXTRA_BACKUP_URI";
+
+    /**
+     * Cached version code to prevent repeated calls to the package manager.
+     */
+    private static String sCachedVersionCode = null;
+
+    /** Static helper that is not instantiable*/
+    private HelpUtils() { }
+
+    public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
+            String backupContext) {
+        MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
+        return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
+    }
+
+    public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
+            String backupContext) {
+        MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
+        return prepareHelpMenuItem(activity, helpItem, activity.getString(helpUriResource),
+                backupContext);
+    }
+
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     *   item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    public static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
+            String helpUriString, String backupContext) {
+        if (TextUtils.isEmpty(helpUriString)) {
+            // The help url string is empty or null, so set the help menu item to be invisible.
+            helpMenuItem.setVisible(false);
+
+            // return that the help menu item is not visible (i.e. false)
+            return false;
+        } else {
+            final Intent intent = getHelpIntent(activity, helpUriString, backupContext);
+
+            // Set the intent to the help menu item, show the help menu item in the overflow
+            // menu, and make it visible.
+            if (intent != null) {
+                helpMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                    @Override
+                    public boolean onMenuItemClick(MenuItem item) {
+                        try {
+                            activity.startActivityForResult(intent, 0);
+                        } catch (ActivityNotFoundException exc) {
+                            Log.e(TAG, "No activity found for intent: " + intent);
+                        }
+                        return true;
+                    }
+                });
+                helpMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+                helpMenuItem.setVisible(true);
+            } else {
+                helpMenuItem.setVisible(false);
+                return false;
+            }
+
+            // return that the help menu item is visible (i.e., true)
+            return true;
+        }
+    }
+
+    public static Intent getHelpIntent(Context context, String helpUriString,
+            String backupContext) {
+        // Try to handle as Intent Uri, otherwise just treat as Uri.
+        try {
+            Intent intent = Intent.parseUri(helpUriString,
+                    Intent.URI_ANDROID_APP_SCHEME | Intent.URI_INTENT_SCHEME);
+            addIntentParameters(context, intent, backupContext);
+            ComponentName component = intent.resolveActivity(context.getPackageManager());
+            if (component != null) {
+                return intent;
+            } else if (intent.hasExtra(EXTRA_BACKUP_URI)) {
+                // This extra contains a backup URI for when the intent isn't available.
+                return getHelpIntent(context, intent.getStringExtra(EXTRA_BACKUP_URI),
+                        backupContext);
+            } else {
+                return null;
+            }
+        } catch (URISyntaxException e) {
+        }
+        // The help url string exists, so first add in some extra query parameters.
+        final Uri fullUri = uriWithAddedParameters(context, Uri.parse(helpUriString));
+
+        // Then, create an intent that will be fired when the user
+        // selects this help menu item.
+        Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        return intent;
+    }
+
+    private static void addIntentParameters(Context context, Intent intent, String backupContext) {
+        if (!intent.hasExtra(EXTRA_CONTEXT)) {
+            // Insert some context if none exists.
+            intent.putExtra(EXTRA_CONTEXT, backupContext);
+        }
+        intent.putExtra(EXTRA_THEME, 1 /* Light, dark action bar */);
+        Theme theme = context.getTheme();
+        TypedValue typedValue = new TypedValue();
+        theme.resolveAttribute(android.R.attr.colorPrimary, typedValue, true);
+        intent.putExtra(EXTRA_PRIMARY_COLOR, context.getColor(typedValue.resourceId));
+    }
+
+    /**
+     * Adds two query parameters into the Uri, namely the language code and the version code
+     * of the app's package as gotten via the context.
+     * @return the uri with added query parameters
+     */
+    public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+        Uri.Builder builder = baseUri.buildUpon();
+
+        // Add in the preferred language
+        builder.appendQueryParameter(PARAM_LANGUAGE_CODE, Locale.getDefault().toString());
+
+        // Add in the package version code
+        if (sCachedVersionCode == null) {
+            // There is no cached version code, so try to get it from the package manager.
+            try {
+                // cache the version code
+                PackageInfo info = context.getPackageManager().getPackageInfo(
+                        context.getPackageName(), 0);
+                sCachedVersionCode = Integer.toString(info.versionCode);
+
+                // append the version code to the uri
+                builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
+            } catch (NameNotFoundException e) {
+                // Cannot find the package name, so don't add in the version parameter
+                // This shouldn't happen.
+                Log.wtf(TAG, "Invalid package name for context", e);
+            }
+        } else {
+            builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
+        }
+
+        // Build the full uri and return it
+        return builder.build();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 586f269..ca0b86a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,7 +15,7 @@
 import android.os.BatteryManager;
 import android.os.UserManager;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import java.text.NumberFormat;
 
@@ -73,21 +73,22 @@
     /**
      * Returns a circular icon for a user.
      */
-    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+    public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
             // We use predefined values for managed profiles
             Bitmap b = BitmapFactory.decodeResource(context.getResources(),
                     com.android.internal.R.drawable.ic_corp_icon);
-            return CircleFramedDrawable.getInstance(context, b);
+            return new UserIconDrawable(iconSize).setIcon(b).bake();
         }
         if (user.iconPath != null) {
             Bitmap icon = um.getUserIcon(user.id);
             if (icon != null) {
-                return CircleFramedDrawable.getInstance(context, icon);
+                return new UserIconDrawable(iconSize).setIcon(icon).bake();
             }
         }
-        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+        return new UserIconDrawable(iconSize).setIconDrawable(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
     }
 
     /** Formats the ratio of amount/total as a percentage. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
new file mode 100644
index 0000000..32478a7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.drawable;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
+ */
+public class UserIconDrawable extends Drawable implements Drawable.Callback {
+
+    private Drawable mUserDrawable;
+    private Bitmap mUserIcon;
+    private Bitmap mBitmap; // baked representation. Required for transparent border around badge
+    private final Paint mIconPaint = new Paint();
+    private final Paint mPaint = new Paint();
+    private final Matrix mIconMatrix = new Matrix();
+    private float mIntrinsicRadius;
+    private float mDisplayRadius;
+    private float mPadding = 0;
+    private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero
+    private boolean mInvalidated = true;
+    private ColorStateList mTintColor = null;
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP;
+
+    private float mFrameWidth;
+    private float mFramePadding;
+    private ColorStateList mFrameColor = null;
+    private Paint mFramePaint;
+
+    private Drawable mBadge;
+    private Paint mClearPaint;
+    private float mBadgeRadius;
+    private float mBadgeMargin;
+
+    /**
+     * Gets the system default managed-user badge as a drawable
+     * @param context
+     * @return drawable containing just the badge
+     */
+    public static Drawable getManagedUserBadgeDrawable(Context context) {
+        int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
+        return context.getResources().getDrawableForDensity(
+                com.android.internal.R.drawable.ic_corp_user_badge,
+                displayDensity, context.getTheme());
+    }
+
+    /**
+     * Gets the preferred list-item size of this drawable.
+     * @param context
+     * @return size in pixels
+     */
+    public static int getSizeForList(Context context) {
+        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    }
+
+    public UserIconDrawable() {
+        this(0);
+    }
+
+    /**
+     * Use this constructor if the drawable is intended to be placed in listviews
+     * @param intrinsicSize if 0, the intrinsic size will come from the icon itself
+     */
+    public UserIconDrawable(int intrinsicSize) {
+        super();
+        mIconPaint.setAntiAlias(true);
+        mIconPaint.setFilterBitmap(true);
+        mPaint.setFilterBitmap(true);
+        mPaint.setAntiAlias(true);
+        if (intrinsicSize > 0) {
+            setBounds(0, 0, intrinsicSize, intrinsicSize);
+            setIntrinsicSize(intrinsicSize);
+        }
+        setIcon(null);
+    }
+
+    public UserIconDrawable setIcon(Bitmap icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        }
+        mUserIcon = icon;
+        if (mUserIcon == null) {
+            mIconPaint.setShader(null);
+            mBitmap = null;
+        } else {
+            mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP,
+                    Shader.TileMode.CLAMP));
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setIconDrawable(Drawable icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+        }
+        mUserIcon = null;
+        mUserDrawable = icon;
+        if (mUserDrawable == null) {
+            mBitmap = null;
+        } else {
+            mUserDrawable.setCallback(this);
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setBadge(Drawable badge) {
+        mBadge = badge;
+        if (mBadge != null) {
+            if (mClearPaint == null) {
+                mClearPaint = new Paint();
+                mClearPaint.setAntiAlias(true);
+                mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                mClearPaint.setStyle(Paint.Style.FILL);
+            }
+            // update metrics
+            onBoundsChange(getBounds());
+        } else {
+            invalidateSelf();
+        }
+        return this;
+    }
+
+    public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
+        Drawable badge = null;
+        boolean isManaged = context.getSystemService(DevicePolicyManager.class)
+                .getProfileOwnerAsUser(userId) != null;
+        if (isManaged) {
+            badge = getManagedUserBadgeDrawable(context);
+        }
+        return setBadge(badge);
+    }
+
+    public void setBadgeRadius(float radius) {
+        mBadgeRadius = radius;
+        onBoundsChange(getBounds());
+    }
+
+    public void setBadgeMargin(float margin) {
+        mBadgeMargin = margin;
+        onBoundsChange(getBounds());
+    }
+
+    /**
+     * Sets global padding of icon/frame. Doesn't effect the badge.
+     * @param padding
+     */
+    public void setPadding(float padding) {
+        mPadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    private void initFramePaint() {
+        if (mFramePaint == null) {
+            mFramePaint = new Paint();
+            mFramePaint.setStyle(Paint.Style.STROKE);
+            mFramePaint.setAntiAlias(true);
+        }
+    }
+
+    public void setFrameWidth(float width) {
+        initFramePaint();
+        mFrameWidth = width;
+        mFramePaint.setStrokeWidth(width);
+        onBoundsChange(getBounds());
+    }
+
+    public void setFramePadding(float padding) {
+        initFramePaint();
+        mFramePadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    public void setFrameColor(int color) {
+        initFramePaint();
+        mFramePaint.setColor(color);
+        invalidateSelf();
+    }
+
+    public void setFrameColor(ColorStateList colorList) {
+        initFramePaint();
+        mFrameColor = colorList;
+        invalidateSelf();
+    }
+
+    /**
+     * This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's
+     * intrinsic size for layout. It is independent of the bounds.
+     * @param size if 0, the intrinsic size will be set to the displayed icon's size
+     */
+    public void setIntrinsicSize(int size) {
+        mSize = size;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mInvalidated) {
+            rebake();
+        }
+        if (mBitmap != null) {
+            if (mTintColor == null) {
+                mPaint.setColorFilter(null);
+            } else {
+                int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
+                if (mPaint.getColorFilter() == null) {
+                    mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
+                } else {
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
+                }
+            }
+
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+    }
+
+    @Override
+    public void setTintList(ColorStateList tintList) {
+        mTintColor = tintList;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setTintMode(@NonNull PorterDuff.Mode mode) {
+        mTintMode = mode;
+        super.invalidateSelf();
+    }
+
+    /**
+     * This 'bakes' the current state of this icon into a bitmap and removes/recycles the source
+     * bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set.
+     * This effectively turns this into a static drawable.
+     */
+    public UserIconDrawable bake() {
+        if (mSize <= 0) {
+            throw new IllegalStateException("Baking requires an explicit intrinsic size");
+        }
+        onBoundsChange(new Rect(0, 0, mSize, mSize));
+        rebake();
+        mFrameColor = null;
+        mFramePaint = null;
+        mClearPaint = null;
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        } else if (mUserIcon != null) {
+            mUserIcon.recycle();
+            mUserIcon = null;
+        }
+        return this;
+    }
+
+    private void rebake() {
+        mInvalidated = false;
+
+        if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) {
+            return;
+        }
+
+        final Canvas canvas = new Canvas(mBitmap);
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        if(mUserDrawable != null) {
+            mUserDrawable.draw(canvas);
+        } else if (mUserIcon != null) {
+            int saveId = canvas.save();
+            canvas.concat(mIconMatrix);
+            canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f,
+                    mIntrinsicRadius, mIconPaint);
+            canvas.restoreToCount(saveId);
+        }
+
+        if (mFrameColor != null) {
+            mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
+        }
+        if ((mFrameWidth + mFramePadding) > 0.001f) {
+            float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f;
+            canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(),
+                    radius, mFramePaint);
+        }
+
+        if ((mBadge != null) && (mBadgeRadius > 0.001f)) {
+            final float badgeDiameter = mBadgeRadius * 2f;
+            final float badgeTop = mBitmap.getHeight() - badgeDiameter;
+            float badgeLeft = mBitmap.getWidth() - badgeDiameter;
+
+            mBadge.setBounds((int) badgeLeft, (int) badgeTop,
+                    (int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter));
+
+            final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
+            canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
+                    borderRadius, mClearPaint);
+
+            mBadge.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) {
+            return;
+        }
+
+        // re-create bitmap if applicable
+        float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        int size = (int) (newDisplayRadius * 2);
+        if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) {
+            mDisplayRadius = newDisplayRadius;
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        }
+
+        // update metrics
+        mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding;
+        RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius,
+                                  bounds.exactCenterY() - iconRadius,
+                                  bounds.exactCenterX() + iconRadius,
+                                  bounds.exactCenterY() + iconRadius);
+        if (mUserDrawable != null) {
+            Rect rounded = new Rect();
+            dstRect.round(rounded);
+            mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(),
+                                        mUserDrawable.getIntrinsicHeight()) * 0.5f;
+            mUserDrawable.setBounds(rounded);
+        } else if (mUserIcon != null) {
+            // Build square-to-square transformation matrix
+            final float iconCX = mUserIcon.getWidth() * 0.5f;
+            final float iconCY = mUserIcon.getHeight() * 0.5f;
+            mIntrinsicRadius = Math.min(iconCX, iconCY);
+            RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius,
+                                      iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius);
+            mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
+        }
+
+        invalidateSelf();
+    }
+
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+        mInvalidated = true;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mFrameColor != null && mFrameColor.isStateful();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return getIntrinsicWidth();
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        invalidateSelf();
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index f9fa805f..750601d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -33,7 +33,7 @@
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import com.android.settingslib.R;
 
@@ -71,7 +71,8 @@
         }
 
         private static Drawable encircle(Context context, Drawable icon) {
-            return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon));
+            return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
+                    .setIconDrawable(icon).bake();
         }
     }
     private ArrayList<UserDetails> data;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 61c9f2b..5621642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1942,7 +1942,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 126;
+            private static final int SETTINGS_VERSION = 127;
 
             private final int mUserId;
 
@@ -2167,6 +2167,36 @@
                     currentVersion = 126;
                 }
 
+                if (currentVersion == 126) {
+                    // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and
+                    // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState systemSecureSettings =
+                                getSecureSettingsLocked(UserHandle.USER_SYSTEM);
+
+                        final Setting showNotifications = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        if (showNotifications != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                                    showNotifications.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+
+                        final Setting allowPrivate = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+                        if (allowPrivate != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                                    allowPrivate.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 127;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
new file mode 100644
index 0000000..20251c2
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="@color/current_user_border_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
new file mode 100644
index 0000000..2b75c36
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index 8e7feec94..1f24ab0 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -26,7 +26,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingStart="72dp"
-        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?android:attr/colorAccent" />
 
     <com.android.systemui.ResizingSpace
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index c6e453a..d685528 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -33,14 +33,18 @@
     <TextView android:id="@+id/user_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="16dp"
+            android:layout_marginEnd="13dp"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
             android:contentDescription="@null"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
-            sysui:framePadding="6dp"
-            sysui:activeFrameColor="@color/current_user_border_color" />
+            sysui:framePadding="2.5dp"
+            sysui:badgeDiameter="18dp"
+            sysui:badgeMargin="1dp"
+            sysui:frameColor="@color/qs_user_detail_avatar_frame" />
 </com.android.systemui.qs.tiles.UserDetailItemView>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 661d74a..58fc069 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -33,12 +33,16 @@
 
     <com.android.systemui.statusbar.phone.UserAvatarView
             android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
-            android:layout_marginBottom="10dp"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
+            android:layout_marginBottom="7dp"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             systemui:frameWidth="2dp"
-            systemui:framePadding="6dp"
-            systemui:activeFrameColor="@color/current_user_border_color"/>
+            systemui:framePadding="2.5dp"
+            systemui:badgeDiameter="18dp"
+            systemui:badgeMargin="1dp"
+            systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 3dca77d..7c4ce15 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -55,6 +55,7 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/heads_up_scrim_height"
         android:background="@drawable/heads_up_scrim"
+        sysui:ignoreRightInset="true"
         android:importantForAccessibility="no"/>
 
     <include layout="@layout/status_bar"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6dd8c52..1543360 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -53,10 +53,14 @@
         <enum name="vertical" value="1" />
     </attr>
     <declare-styleable name="UserAvatarView">
+        <attr name="avatarPadding" format="dimension" />
         <attr name="frameWidth" format="dimension" />
         <attr name="framePadding" format="dimension" />
+        <!-- {@deprecated Use a statelist in frameColor instead.} -->
         <attr name="activeFrameColor" format="color" />
-        <attr name="frameColor" />
+        <attr name="frameColor" format="color" />
+        <attr name="badgeDiameter" format="dimension" />
+        <attr name="badgeMargin" format="dimension" />
     </declare-styleable>
     <declare-styleable name="UserDetailItemView">
         <attr name="regularFontFamily" format="string" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a4eadbf..cf2e338 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,6 +388,9 @@
          quick settings header -->
     <dimen name="max_avatar_size">48dp</dimen>
 
+    <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
+    <dimen name="framed_avatar_size">54dp</dimen>
+
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">16dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c536c1a..a33b7a3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1082,6 +1082,10 @@
     <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string>
     <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string>
 
+    <string name="volume_stream_content_description_unmute">%1$s. Tap to unmute.</string>
+    <string name="volume_stream_content_description_vibrate">%1$s. Tap to set to vibrate. Accessibility services may be muted.</string>
+    <string name="volume_stream_content_description_mute">%1$s. Tap to mute. Accessibility services may be muted.</string>
+
     <!-- Name of special SystemUI debug settings -->
     <string name="system_ui_tuner">System UI Tuner</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
deleted file mode 100644
index 1933bbc..0000000
--- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Shader;
-
-public class BitmapHelper {
-    /**
-     * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
-     * to fit and clipped to an inscribed circle.
-     * @param input Bitmap to resize and clip
-     * @param width Width of output bitmap (and diameter of circle)
-     * @param height Height of output bitmap
-     * @return A shiny new bitmap for you to use
-     */
-    public static Bitmap createCircularClip(Bitmap input, int width, int height) {
-        if (input == null) return null;
-
-        final int inWidth = input.getWidth();
-        final int inHeight = input.getHeight();
-        final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        paint.setAntiAlias(true);
-        final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
-        final RectF dstRect = new RectF(0, 0, width, height);
-        final Matrix m = new Matrix();
-        m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
-        canvas.setMatrix(m);
-        canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
-        return output;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 0d822cb..0798590 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -250,7 +250,6 @@
             FalsingLog.i("onSucccessfulUnlock", "");
         }
         mDataCollector.onSucccessfulUnlock();
-        sessionExitpoint(true /* force */);
     }
 
     public void onBouncerShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 6114573..6b20681 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -138,7 +138,7 @@
         mListening = listening;
         try {
             if (listening) {
-                if (mServiceManager.getType() == TileService.TILE_MODE_PASSIVE) {
+                if (!mServiceManager.isActiveTile()) {
                     mServiceManager.setBindRequested(true);
                     mService.onStartListening();
                 }
@@ -209,7 +209,7 @@
         } catch (RemoteException e) {
         }
         try {
-            if (mServiceManager.getType() == TileService.TILE_MODE_ACTIVE) {
+            if (mServiceManager.isActiveTile()) {
                 mServiceManager.setBindRequested(true);
                 mService.onStartListening();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 8910d44..5a26a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.qs.external;
 
-import libcore.util.Objects;
-
 import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -25,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Handler;
@@ -34,9 +33,11 @@
 import android.service.quicksettings.IQSService;
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.ArraySet;
 import android.util.Log;
+import libcore.util.Objects;
 
 import java.util.Set;
 
@@ -98,6 +99,17 @@
         }
     }
 
+    public boolean isActiveTile() {
+        try {
+            ServiceInfo info = mContext.getPackageManager().getServiceInfo(mIntent.getComponent(),
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+            return info.metaData != null
+                    && info.metaData.getBoolean(TileService.META_DATA_ACTIVE_TILE, false);
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Binds just long enough to send any queued messages, then unbinds.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 664ddd6..ab21532 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.TileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
@@ -51,7 +50,6 @@
     private int mPriority;
     private boolean mJustBound;
     private long mLastUpdate;
-    private int mType;
     private boolean mShowingDialog;
     // Whether we have a pending bind going out to the service without a response yet.
     // This defaults to true to ensure tiles start out unavailable.
@@ -69,25 +67,11 @@
         mServices = tileServices;
         mHandler = handler;
         mStateManager = tileLifecycleManager;
-        mType = tileServices.getContext().getSharedPreferences(PREFS_FILE, 0)
-                .getInt(tileLifecycleManager.getComponent().flattenToString(),
-                        TileService.TILE_MODE_UNSET);
         mStateManager.setQSService(tileServices);
-        if (mType == TileService.TILE_MODE_UNSET) {
-            bindService();
-            mStateManager.onTileAdded();
-        }
     }
 
-    public int getType() {
-        return mType;
-    }
-
-    public void setType(int type) {
-        mServices.getContext().getSharedPreferences(PREFS_FILE, 0).edit()
-                .putInt(mStateManager.getComponent().flattenToString(), type).commit();
-        mType = type;
-        mServices.recalculateBindAllowance();
+    public boolean isActiveTile() {
+        return mStateManager.isActiveTile();
     }
 
     public void setShowingDialog(boolean dialog) {
@@ -114,7 +98,7 @@
 
     public void setLastUpdate(long lastUpdate) {
         mLastUpdate = lastUpdate;
-        if (mBound && mType == TileService.TILE_MODE_ACTIVE) {
+        if (mBound && isActiveTile()) {
             mStateManager.onStopListening();
             setBindRequested(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 5bb2a35..f36d013 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -153,7 +153,7 @@
                 return;
             }
             TileServiceManager service = mServices.get(customTile);
-            if (service.getType() != TileService.TILE_MODE_ACTIVE) {
+            if (!service.isActiveTile()) {
                 return;
             }
             service.setBindRequested(true);
@@ -165,17 +165,6 @@
     }
 
     @Override
-    public void setTileMode(ComponentName component, int mode) {
-        verifyCaller(component.getPackageName());
-        CustomTile customTile = getTileForComponent(component);
-        if (customTile != null) {
-            synchronized (mServices) {
-                mServices.get(customTile).setType(mode);
-            }
-        }
-    }
-
-    @Override
     public void updateQsTile(Tile tile) {
         ComponentName componentName = tile.getComponentName();
         verifyCaller(componentName.getPackageName());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 2032783..e494fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -25,6 +25,7 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.RelativeSizeSpan;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -207,18 +208,21 @@
                     }
                 }
             });
+            final TextView batterySaverTitle =
+                    (TextView) mCurrentView.findViewById(android.R.id.title);
+            final TextView batterySaverSummary =
+                    (TextView) mCurrentView.findViewById(android.R.id.summary);
             if (mCharging) {
-                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
-                        R.string.battery_detail_charging_summary);
-                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.INVISIBLE);
+                mCurrentView.findViewById(R.id.switch_container).setAlpha(.7f);
+                batterySaverTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+                batterySaverTitle.setText(R.string.battery_detail_charging_summary);
                 mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.GONE);
                 mCurrentView.findViewById(R.id.switch_container).setClickable(false);
             } else {
-                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
-                        R.string.battery_detail_switch_title);
-                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
-                        R.string.battery_detail_switch_summary);
-                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.VISIBLE);
+                mCurrentView.findViewById(R.id.switch_container).setAlpha(1);
+                batterySaverTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
+                batterySaverTitle.setText(R.string.battery_detail_switch_title);
+                batterySaverSummary.setText(R.string.battery_detail_switch_summary);
                 mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.VISIBLE);
                 mCurrentView.findViewById(R.id.switch_container).setClickable(true);
                 mCurrentView.findViewById(R.id.switch_container).setOnClickListener(this);
@@ -227,7 +231,7 @@
 
         private void bindBatteryInfo(BatteryInfo info) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
-            builder.append(info.batteryPercentString, new RelativeSizeSpan(2),
+            builder.append(info.batteryPercentString, new RelativeSizeSpan(2.6f),
                     Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
             if (info.remainingLabel != null) {
                 if (mContext.getResources().getBoolean(R.bool.quick_settings_wide)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index fcf758b..99eae02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -87,25 +88,29 @@
         return (UserDetailItemView) convertView;
     }
 
-    public void bind(String name, Bitmap picture) {
+    public void bind(String name, Bitmap picture, int userId) {
         mName.setText(name);
-        mAvatar.setBitmap(picture);
+        mAvatar.setAvatarWithBadge(picture, userId);
     }
 
-    public void bind(String name, Drawable picture) {
+    public void bind(String name, Drawable picture, int userId) {
         mName.setText(name);
-        mAvatar.setDrawable(picture);
+        mAvatar.setDrawableWithBadge(picture, userId);
+    }
+
+    public void setAvatarEnabled(boolean enabled) {
+        mAvatar.setEnabled(enabled);
     }
 
     public void setDisabledByAdmin(boolean disabled) {
         mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
         mName.setEnabled(!disabled);
-        mAvatar.setDisabled(disabled);
+        mAvatar.setEnabled(!disabled);
     }
 
     public void setEnabled(boolean enabled) {
         mName.setEnabled(enabled);
-        mAvatar.setDisabled(!enabled);
+        mAvatar.setEnabled(enabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index da98762..d4fa765 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -76,9 +76,9 @@
             }
             String name = getName(mContext, item);
             if (item.picture == null) {
-                v.bind(name, getDrawable(mContext, item));
+                v.bind(name, getDrawable(mContext, item), item.resolveId());
             } else {
-                v.bind(name, item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             v.setActivated(item.isCurrent);
             v.setDisabledByAdmin(item.isDisabledByAdmin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c9fe2bd..6570221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -34,6 +34,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -257,16 +258,21 @@
     }
 
     public void add(Entry entry, RankingMap ranking) {
-        mEntries.put(entry.notification.getKey(), entry);
-        updateRankingAndSort(ranking);
+        synchronized (mEntries) {
+            mEntries.put(entry.notification.getKey(), entry);
+        }
         mGroupManager.onEntryAdded(entry);
+        updateRankingAndSort(ranking);
     }
 
     public Entry remove(String key, RankingMap ranking) {
-        Entry removed = mEntries.remove(key);
+        Entry removed = null;
+        synchronized (mEntries) {
+            removed = mEntries.remove(key);
+        }
         if (removed == null) return null;
-        updateRankingAndSort(ranking);
         mGroupManager.onEntryRemoved(removed);
+        updateRankingAndSort(ranking);
         return removed;
     }
 
@@ -316,9 +322,30 @@
         return Ranking.IMPORTANCE_UNSPECIFIED;
     }
 
+    public String getOverrideGroupKey(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return mTmpRanking.getOverrideGroupKey();
+        }
+         return null;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
+            synchronized (mEntries) {
+                final int N = mEntries.size();
+                for (int i = 0; i < N; i++) {
+                    Entry entry = mEntries.valueAt(i);
+                    final StatusBarNotification oldSbn = entry.notification.clone();
+                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
+                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+                        entry.notification.setOverrideGroupKey(overrideGroupKey);
+                        mGroupManager.onEntryUpdated(entry, oldSbn);
+                    }
+                    //mGroupManager.onEntryBundlingUpdated(entry, getOverrideGroupKey(entry.key));
+                }
+            }
         }
         filterAndSort();
     }
@@ -328,16 +355,18 @@
     public void filterAndSort() {
         mSortedAndFiltered.clear();
 
-        final int N = mEntries.size();
-        for (int i = 0; i < N; i++) {
-            Entry entry = mEntries.valueAt(i);
-            StatusBarNotification sbn = entry.notification;
+        synchronized (mEntries) {
+            final int N = mEntries.size();
+            for (int i = 0; i < N; i++) {
+                Entry entry = mEntries.valueAt(i);
+                StatusBarNotification sbn = entry.notification;
 
-            if (shouldFilterOut(sbn)) {
-                continue;
+                if (shouldFilterOut(sbn)) {
+                    continue;
+                }
+
+                mSortedAndFiltered.add(entry);
             }
-
-            mSortedAndFiltered.add(entry);
         }
 
         Collections.sort(mSortedAndFiltered, mRankingComparator);
@@ -398,16 +427,17 @@
             NotificationData.Entry e = mSortedAndFiltered.get(active);
             dumpEntry(pw, indent, active, e);
         }
-
-        int M = mEntries.size();
-        pw.print(indent);
-        pw.println("inactive notifications: " + (M - active));
-        int inactiveCount = 0;
-        for (int i = 0; i < M; i++) {
-            Entry entry = mEntries.valueAt(i);
-            if (!mSortedAndFiltered.contains(entry)) {
-                dumpEntry(pw, indent, inactiveCount, entry);
-                inactiveCount++;
+        synchronized (mEntries) {
+            int M = mEntries.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (M - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < M; i++) {
+                Entry entry = mEntries.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index f7a6b271..a27ec28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -26,6 +26,7 @@
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Objects;
 
 /**
  * A class to handle notifications and their corresponding groups.
@@ -121,6 +122,15 @@
         }
     }
 
+    public void onEntryBundlingUpdated(final NotificationData.Entry updated,
+            final String overrideGroupKey) {
+        final StatusBarNotification oldSbn = updated.notification.clone();
+        if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+            updated.notification.setOverrideGroupKey(overrideGroupKey);
+            onEntryUpdated(updated, oldSbn);
+        }
+    }
+
     private void updateSuppression(NotificationGroup group) {
         if (group == null) {
             return;
@@ -129,7 +139,7 @@
         group.suppressed = group.summary != null && !group.expanded
                 && (group.children.size() == 1
                 || (group.children.size() == 0
-                        && !group.summary.notification.getNotification().isGroupChild()
+                        && group.summary.notification.getNotification().isGroupSummary()
                         && hasIsolatedChildren(group)));
         if (prevSuppressed != group.suppressed) {
             mListener.onGroupsChanged();
@@ -173,7 +183,7 @@
 
     public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
         return isGroupSuppressed(sbn.getGroupKey())
-                && sbn.getNotification().isGroupChild()
+                && !sbn.getNotification().isGroupSummary()
                 && getTotalNumberOfChildren(sbn) == 1;
     }
 
@@ -278,11 +288,12 @@
         }
         return sbn.getNotification().isGroupSummary();
     }
+
     private boolean isGroupChild(StatusBarNotification sbn) {
         if (isIsolated(sbn)) {
             return false;
         }
-        return sbn.getNotification().isGroupChild();
+        return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
     }
 
     private String getGroupKey(StatusBarNotification sbn) {
@@ -335,7 +346,7 @@
 
     private boolean shouldIsolate(StatusBarNotification sbn) {
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
-        return sbn.getNotification().isGroupChild()
+        return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
                 && (sbn.getNotification().fullScreenIntent != null
                         || notificationGroup == null
                         || !notificationGroup.expanded
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 093a827..dc1b35d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -17,69 +17,57 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 /**
- * A view that displays a user image cropped to a circle with a frame.
+ * A view that displays a user image cropped to a circle with an optional frame.
  */
 public class UserAvatarView extends View {
 
-    private int mActiveFrameColor;
-    private int mFrameColor;
-    private float mFrameWidth;
-    private float mFramePadding;
-    private Bitmap mBitmap;
-    private Drawable mDrawable;
-    private boolean mIsDisabled;
-
-    private final Paint mFramePaint = new Paint();
-    private final Paint mBitmapPaint = new Paint();
-    private final Matrix mDrawMatrix = new Matrix();
-
-    private float mScale = 1;
+    private final UserIconDrawable mDrawable = new UserIconDrawable();
 
     public UserAvatarView(Context context, AttributeSet attrs,
             int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
             switch (attr) {
+                case R.styleable.UserAvatarView_avatarPadding:
+                    setAvatarPadding(a.getDimension(attr, 0));
+                    break;
                 case R.styleable.UserAvatarView_frameWidth:
                     setFrameWidth(a.getDimension(attr, 0));
                     break;
                 case R.styleable.UserAvatarView_framePadding:
                     setFramePadding(a.getDimension(attr, 0));
                     break;
-                case R.styleable.UserAvatarView_activeFrameColor:
-                    setActiveFrameColor(a.getColor(attr, 0));
-                    break;
                 case R.styleable.UserAvatarView_frameColor:
-                    setFrameColor(a.getColor(attr, 0));
+                    setFrameColor(a.getColorStateList(attr));
+                    break;
+                case R.styleable.UserAvatarView_badgeDiameter:
+                    setBadgeDiameter(a.getDimension(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_badgeMargin:
+                    setBadgeMargin(a.getDimension(attr, 0));
                     break;
             }
         }
         a.recycle();
-
-        mFramePaint.setAntiAlias(true);
-        mFramePaint.setStyle(Paint.Style.STROKE);
-        mBitmapPaint.setAntiAlias(true);
+        setBackground(mDrawable);
     }
 
     public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -94,180 +82,61 @@
         this(context, null);
     }
 
+    /**
+     * @deprecated use {@link #setAvatar(Bitmap)} instead.
+     */
+    @Deprecated
     public void setBitmap(Bitmap bitmap) {
-        setDrawable(null);
-        mBitmap = bitmap;
-        if (mBitmap != null) {
-            mBitmapPaint.setShader(new BitmapShader(
-                    bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        } else {
-            mBitmapPaint.setShader(null);
-        }
-        configureBounds();
-        invalidate();
+        setAvatar(bitmap);
     }
 
-    public void setFrameColor(int frameColor) {
-        mFrameColor = frameColor;
-        invalidate();
-    }
-
-    public void setActiveFrameColor(int activeFrameColor) {
-        mActiveFrameColor = activeFrameColor;
-        invalidate();
+    public void setFrameColor(ColorStateList color) {
+        mDrawable.setFrameColor(color);
     }
 
     public void setFrameWidth(float frameWidth) {
-        mFrameWidth = frameWidth;
-        invalidate();
+        mDrawable.setFrameWidth(frameWidth);
     }
 
     public void setFramePadding(float framePadding) {
-        mFramePadding = framePadding;
-        invalidate();
+        mDrawable.setFramePadding(framePadding);
     }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        configureBounds();
+    public void setAvatarPadding(float avatarPadding) {
+        mDrawable.setPadding(avatarPadding);
     }
 
-    public void configureBounds() {
-        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
-        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
-
-        int dwidth;
-        int dheight;
-        if (mBitmap != null) {
-            dwidth = mBitmap.getWidth();
-            dheight = mBitmap.getHeight();
-        } else if (mDrawable != null) {
-            vwidth -= 2 * (mFrameWidth - 1);
-            vheight -= 2 * (mFrameWidth - 1);
-            dwidth = vwidth;
-            dheight = vheight;
-            mDrawable.setBounds(0, 0, dwidth, dheight);
-        } else {
-            return;
-        }
-
-        float scale;
-        float dx;
-        float dy;
-
-        scale = Math.min((float) vwidth / (float) dwidth,
-                (float) vheight / (float) dheight);
-
-        dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
-        dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
-
-        mDrawMatrix.setScale(scale, scale);
-        mDrawMatrix.postTranslate(dx, dy);
-        mScale = scale;
+    public void setBadgeDiameter(float diameter) {
+        mDrawable.setBadgeRadius(diameter * 0.5f);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
-        float halfW = getWidth() / 2f;
-        float halfH = getHeight() / 2f;
-        float halfSW = Math.min(halfH, halfW);
-        updateDrawableIfDisabled();
-        if (mBitmap != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.concat(mDrawMatrix);
-            float halfBW = mBitmap.getWidth() / 2f;
-            float halfBH = mBitmap.getHeight() / 2f;
-            float halfBSW = Math.min(halfBH, halfBW);
-            canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
-            canvas.restoreToCount(saveCount);
-        } else if (mDrawable != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
-            canvas.concat(mDrawMatrix);
-            mDrawable.draw(canvas);
-            canvas.restoreToCount(saveCount);
-        }
-        if (frameColor != 0) {
-            mFramePaint.setColor(frameColor);
-            mFramePaint.setStrokeWidth(mFrameWidth);
-            canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f,
-                    mFramePaint);
-        }
+    public void setBadgeMargin(float margin) {
+        mDrawable.setBadgeMargin(margin);
+    }
+
+    public void setAvatar(Bitmap avatar) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadge(null);
+    }
+
+    public void setAvatarWithBadge(Bitmap avatar, int userId) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 
     public void setDrawable(Drawable d) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-        mDrawable = d;
-        if (d != null) {
-            d.setCallback(this);
-            if (d.isStateful()) {
-                d.setState(getDrawableState());
-            }
-            d.setLayoutDirection(getLayoutDirection());
-            configureBounds();
-        }
-        if (d != null) {
-            mBitmap = null;
-        }
-        configureBounds();
-        invalidate();
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadge(null);
     }
 
-    @Override
-    public void invalidateDrawable(Drawable dr) {
-        if (dr == mDrawable) {
-            invalidate();
-        } else {
-            super.invalidateDrawable(dr);
+    public void setDrawableWithBadge(Drawable d, int userId) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mDrawable || super.verifyDrawable(who);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mDrawable != null && mDrawable.isStateful()) {
-            mDrawable.setState(getDrawableState());
-        }
-    }
-
-    public void setDisabled(boolean disabled) {
-        if (mIsDisabled == disabled) {
-            return;
-        }
-        mIsDisabled = disabled;
-        invalidate();
-    }
-
-    private void updateDrawableIfDisabled() {
-        int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
-        PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
-                PorterDuff.Mode.SRC_ATOP);
-        if (mBitmap != null) {
-            if (mIsDisabled) {
-                mBitmapPaint.setColorFilter(filter);
-            } else {
-                mBitmapPaint.setColorFilter(null);
-            }
-        } else if (mDrawable != null) {
-            if (mIsDisabled) {
-                mDrawable.setColorFilter(filter);
-            } else {
-                mDrawable.setColorFilter(null);
-            }
-        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index fb310a6..c39d718 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -253,17 +253,13 @@
             UserDetailItemView v = (UserDetailItemView) convertView;
 
             String name = getName(mContext, item);
-            Drawable drawable;
             if (item.picture == null) {
-                drawable = getDrawable(mContext, item).mutate();
+                v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                drawable = new BitmapDrawable(mContext.getResources(), item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             // Disable the icon if switching is disabled
-            if (!item.isSwitchToEnabled) {
-                drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color));
-            }
-            v.bind(name, drawable);
+            v.setAvatarEnabled(item.isSwitchToEnabled);
             convertView.setActivated(item.isCurrent);
             convertView.setTag(item);
             return convertView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 85ac755..bae5bda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -26,7 +26,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.RemoteException;
@@ -37,7 +36,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.UserIcons;
-import com.android.systemui.BitmapHelper;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -155,8 +154,8 @@
                 Drawable avatar = null;
                 Bitmap rawAvatar = um.getUserIcon(userId);
                 if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(),
-                            BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize));
+                    avatar = new UserIconDrawable(avatarSize)
+                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
                 } else {
                     avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
                             /* light= */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ea0bdf2..c82ba3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -50,7 +50,6 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.SystemUISecondaryUserService;
@@ -197,8 +196,6 @@
                 boolean canSwitchUsers = mUserManager.canSwitchUsers();
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
-                int avatarSize = mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.max_avatar_size);
 
                 for (UserInfo info : infos) {
                     boolean isCurrent = currentId == info.id;
@@ -219,8 +216,10 @@
                                 picture = mUserManager.getUserIcon(info.id);
 
                                 if (picture != null) {
-                                    picture = BitmapHelper.createCircularClip(
-                                            picture, avatarSize, avatarSize);
+                                    int avatarSize = mContext.getResources()
+                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
+                                    picture = Bitmap.createScaledBitmap(
+                                            picture, avatarSize, avatarSize, true);
                                 }
                             }
                             int index = isCurrent ? 0 : records.size();
@@ -664,8 +663,7 @@
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
             }
-            return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
-                    /* light= */ true);
+            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
         }
 
         public void refresh() {
@@ -718,6 +716,13 @@
                     isSwitchToEnabled);
         }
 
+        public int resolveId() {
+            if (isGuest || info == null) {
+                return UserHandle.USER_NULL;
+            }
+            return info.id;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append("UserRecord(");
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 1d5ca04..91a8493 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -761,7 +761,37 @@
                 : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
-        row.icon.setContentDescription(ss.name);
+        if (iconEnabled) {
+            if (isRingStream) {
+                if (isRingVibrate) {
+                    row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_unmute,
+                            ss.name));
+                } else {
+                    if (mController.hasVibrator()) {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_vibrate,
+                                ss.name));
+                    } else {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_mute,
+                                ss.name));
+                    }
+                }
+            } else {
+                if (ss.muted || mAutomute && ss.level == 0) {
+                   row.icon.setContentDescription(mContext.getString(
+                           R.string.volume_stream_content_description_unmute,
+                           ss.name));
+                } else {
+                    row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_mute,
+                            ss.name));
+                }
+            }
+        } else {
+            row.icon.setContentDescription(ss.name);
+        }
 
         // update slider
         final boolean enableSlider = !zenMuted;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
index f24b541..1e27603 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.service.quicksettings.TileService;
 import android.test.suitebuilder.annotation.SmallTest;
 import com.android.systemui.SysuiTestCase;
 import org.mockito.ArgumentCaptor;
@@ -42,11 +41,10 @@
         mTileServices = Mockito.mock(TileServices.class);
         Mockito.when(mTileServices.getContext()).thenReturn(mContext);
         mTileLifecycle = Mockito.mock(TileLifecycleManager.class);
+        Mockito.when(mTileLifecycle.isActiveTile()).thenReturn(false);
         ComponentName componentName = new ComponentName(mContext,
                 TileServiceManagerTests.class);
         Mockito.when(mTileLifecycle.getComponent()).thenReturn(componentName);
-        mContext.getSharedPreferences(TileServiceManager.PREFS_FILE, 0).edit()
-                .putInt(componentName.flattenToString(), TileService.TILE_MODE_PASSIVE).commit();
         mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mTileLifecycle);
     }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b3613df..ea3cffe 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2135,6 +2135,9 @@
     // Suggestion -> Overflow -> Remove.
     ACTION_SETTINGS_DISMISS_SUGGESTION = 387;
 
+    // Settings > Apps > Gear > Special Access > Premium SMS access
+    PREMIUM_SMS_ACCESS = 388;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2741733..ca17c43 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -439,7 +439,7 @@
             }
             if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
                 mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(),
-                        event.getSourceNodeId(), event.getEventType());
+                        event.getSourceNodeId(), event.getEventType(), event.getAction());
                 mSecurityPolicy.updateEventSourceLocked(event);
                 notifyAccessibilityServicesDelayedLocked(event, false);
                 notifyAccessibilityServicesDelayedLocked(event, true);
@@ -1795,9 +1795,8 @@
                 }
                 userState.mSoftKeyboardShowMode = 0;
                 userState.mServiceChangingSoftKeyboardMode = null;
+                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
             }
-
-            notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
         }
     }
 
@@ -3829,7 +3828,7 @@
         }
 
         public void updateActiveAndAccessibilityFocusedWindowLocked(int windowId, long nodeId,
-                int eventType) {
+                int eventType, int eventAction) {
             // The active window is either the window that has input focus or
             // the window that the user is currently touching. If the user is
             // touching a window that does not have input focus as soon as the
@@ -3882,8 +3881,12 @@
                         if (mAccessibilityFocusNodeId == nodeId) {
                             mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                         }
-                        if (mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
-                                && mAccessibilityFocusedWindowId == windowId) {
+                        // Clear the window with focus if it no longer has focus and we aren't
+                        // just moving focus from one view to the other in the same window
+                        if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
+                                && (mAccessibilityFocusedWindowId == windowId)
+                                && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)
+                                ) {
                             mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
                         }
                     }
@@ -4355,6 +4358,7 @@
                     }
                 } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
                     if (readSoftKeyboardShowModeChangedLocked(userState)) {
+                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
                 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index ccb4647..6a08191 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -39,7 +39,9 @@
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
+import android.net.ConnectivityManager;
 import android.net.INetworkPolicyManager;
+import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -114,6 +116,7 @@
     private IBatteryStats mBatteryStats;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
+    private ConnectivityService mConnectivityService;
     private AlarmManagerService.LocalService mLocalAlarmManager;
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
@@ -128,6 +131,7 @@
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
     private boolean mForceIdle;
+    private boolean mNetworkConnected;
     private boolean mScreenOn;
     private boolean mCharging;
     private boolean mNotMoving;
@@ -173,16 +177,20 @@
     private static final int LIGHT_STATE_PRE_IDLE = 3;
     /** Device is in the light idle state, trying to stay asleep as much as possible. */
     private static final int LIGHT_STATE_IDLE = 4;
+    /** Device is in the light idle state, we want to go in to idle maintenance but are
+     * waiting for network connectivity before doing so. */
+    private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
     /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
-    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 5;
+    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
     /** Device light idle state is overriden, now applying deep doze state. */
-    private static final int LIGHT_STATE_OVERRIDE = 6;
+    private static final int LIGHT_STATE_OVERRIDE = 7;
     private static String lightStateToString(int state) {
         switch (state) {
             case LIGHT_STATE_ACTIVE: return "ACTIVE";
             case LIGHT_STATE_INACTIVE: return "INACTIVE";
             case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE";
             case LIGHT_STATE_IDLE: return "IDLE";
+            case LIGHT_STATE_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
             case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
             case LIGHT_STATE_OVERRIDE: return "OVERRIDE";
             default: return Integer.toString(state);
@@ -315,17 +323,27 @@
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
-                int plugged = intent.getIntExtra("plugged", 0);
-                updateChargingLocked(plugged != 0);
-            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    Uri data = intent.getData();
-                    String ssp;
-                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
-                        removePowerSaveWhitelistAppInternal(ssp);
+            switch (intent.getAction()) {
+                case ConnectivityManager.CONNECTIVITY_ACTION: {
+                    synchronized (DeviceIdleController.this) {
+                        updateConnectivityStateLocked(intent);
                     }
-                }
+                } break;
+                case Intent.ACTION_BATTERY_CHANGED: {
+                    synchronized (DeviceIdleController.this) {
+                        int plugged = intent.getIntExtra("plugged", 0);
+                        updateChargingLocked(plugged != 0);
+                    }
+                } break;
+                case Intent.ACTION_PACKAGE_REMOVED: {
+                    if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                        Uri data = intent.getData();
+                        String ssp;
+                        if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                            removePowerSaveWhitelistAppInternal(ssp);
+                        }
+                    }
+                } break;
             }
         }
     };
@@ -1318,6 +1336,7 @@
             readConfigFileLocked();
             updateWhitelistAppIdsLocked();
 
+            mNetworkConnected = true;
             mScreenOn = true;
             // Start out assuming we are charging.  If we aren't, we will at least get
             // a battery update the next time the level drops.
@@ -1343,6 +1362,8 @@
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_maint");
                 mActiveIdleWakeLock.setReferenceCounted(false);
+                mConnectivityService = (ConnectivityService)ServiceManager.getService(
+                        Context.CONNECTIVITY_SERVICE);
                 mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
@@ -1395,11 +1416,14 @@
                 filter = new IntentFilter();
                 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                 filter.addDataScheme("package");
+                filter = new IntentFilter();
+                filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
                 getContext().registerReceiver(mReceiver, filter);
 
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
                 mDisplayManager.registerDisplayListener(mDisplayListener, null);
+                updateConnectivityStateLocked(null);
                 updateDisplayLocked();
             }
         }
@@ -1680,6 +1704,35 @@
         }
     }
 
+    void updateConnectivityStateLocked(Intent connIntent) {
+        if (mConnectivityService != null) {
+            NetworkInfo ni = mConnectivityService.getActiveNetworkInfo();
+            boolean conn;
+            if (ni == null) {
+                conn = false;
+            } else {
+                if (connIntent == null) {
+                    conn = ni.isConnected();
+                } else {
+                    final int networkType =
+                            connIntent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+                                    ConnectivityManager.TYPE_NONE);
+                    if (ni.getType() != networkType) {
+                        return;
+                    }
+                    conn = !connIntent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+                            false);
+                }
+            }
+            if (conn != mNetworkConnected) {
+                mNetworkConnected = conn;
+                if (conn && mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+                    stepLightIdleStateLocked("network");
+                }
+            }
+        }
+    }
+
     void updateDisplayLocked() {
         mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         // We consider any situation where the display is showing something to be it on,
@@ -1778,7 +1831,7 @@
         if (mForceIdle) {
             mForceIdle = false;
             if (mScreenOn || mCharging) {
-                becomeActiveLocked("exit-force-idle", Process.myUid());
+                becomeActiveLocked("exit-force", Process.myUid());
             }
         }
     }
@@ -1834,22 +1887,33 @@
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
                 break;
             case LIGHT_STATE_IDLE:
-                // We have been idling long enough, now it is time to do some work.
-                mActiveIdleOpCount = 1;
-                mActiveIdleWakeLock.acquire();
-                mMaintenanceStartTime = SystemClock.elapsedRealtime();
-                if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
-                    mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
-                } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
-                    mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+            case LIGHT_STATE_WAITING_FOR_NETWORK:
+                if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+                    // We have been idling long enough, now it is time to do some work.
+                    mActiveIdleOpCount = 1;
+                    mActiveIdleWakeLock.acquire();
+                    mMaintenanceStartTime = SystemClock.elapsedRealtime();
+                    if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+                    } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
+                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+                    }
+                    scheduleLightAlarmLocked(mCurIdleBudget);
+                    if (DEBUG) Slog.d(TAG,
+                            "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
+                    mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
+                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
+                    addEvent(EVENT_LIGHT_MAINTENANCE);
+                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
+                } else {
+                    // We'd like to do maintenance, but currently don't have network
+                    // connectivity...  let's try to wait until the network comes back.
+                    // We'll only wait for another full idle period, however, and then give up.
+                    scheduleLightAlarmLocked(mNextLightIdleDelay);
+                    if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
+                    mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
+                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                 }
-                scheduleLightAlarmLocked(mCurIdleBudget);
-                if (DEBUG) Slog.d(TAG,
-                        "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
-                mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
-                EventLogTags.writeDeviceIdleLight(mLightState, reason);
-                addEvent(EVENT_LIGHT_MAINTENANCE);
-                mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                 break;
         }
     }
@@ -2209,13 +2273,6 @@
 
     void scheduleLightAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
-        if (mMotionSensor == null) {
-            // If there is no motion sensor on this device, then we won't schedule
-            // alarms, because we can't determine if the device is not moving.  This effectively
-            // turns off normal execution of device idling, although it is still possible to
-            // manually poke it by pretending like the alarm is going off.
-            return;
-        }
         mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                 mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
@@ -2430,9 +2487,14 @@
         pw.println("    Print this help text.");
         pw.println("  step [light|deep]");
         pw.println("    Immediately step to next state, without waiting for alarm.");
-        pw.println("  force-idle");
+        pw.println("  force-idle [light|deep]");
         pw.println("    Force directly into idle mode, regardless of other device state.");
-        pw.println("    Use \"step\" to get out.");
+        pw.println("  force-inactive");
+        pw.println("    Force to be inactive, ready to freely step idle states.");
+        pw.println("  unforce");
+        pw.println("    Resume normal functioning after force-idle or force-inactive.");
+        pw.println("  get [light|deep|force|screen|charging|network]");
+        pw.println("    Retrieve the current given state.");
         pw.println("  disable [light|deep|all]");
         pw.println("    Completely disable device idle mode.");
         pw.println("  enable [light|deep|all]");
@@ -2472,12 +2534,10 @@
                 String arg = shell.getNextArg();
                 try {
                     if (arg == null || "deep".equals(arg)) {
-                        exitForceIdleLocked();
                         stepIdleStateLocked("s:shell");
                         pw.print("Stepped to deep: ");
                         pw.println(stateToString(mState));
                     } else if ("light".equals(arg)) {
-                        exitForceIdleLocked();
                         stepLightIdleStateLocked("s:shell");
                         pw.print("Stepped to light: "); pw.println(lightStateToString(mLightState));
                     } else {
@@ -2492,29 +2552,104 @@
                     null);
             synchronized (this) {
                 long token = Binder.clearCallingIdentity();
+                String arg = shell.getNextArg();
                 try {
-                    if (!mDeepEnabled) {
-                        pw.println("Unable to go idle; not enabled");
-                        return -1;
-                    }
-                    mForceIdle = true;
-                    becomeInactiveIfAppropriateLocked();
-                    int curState = mState;
-                    while (curState != STATE_IDLE) {
-                        stepIdleStateLocked("s:shell");
-                        if (curState == mState) {
-                            pw.print("Unable to go idle; stopped at ");
-                            pw.println(stateToString(mState));
-                            exitForceIdleLocked();
+                    if (arg == null || "deep".equals(arg)) {
+                        if (!mDeepEnabled) {
+                            pw.println("Unable to go deep idle; not enabled");
                             return -1;
                         }
-                        curState = mState;
+                        mForceIdle = true;
+                        becomeInactiveIfAppropriateLocked();
+                        int curState = mState;
+                        while (curState != STATE_IDLE) {
+                            stepIdleStateLocked("s:shell");
+                            if (curState == mState) {
+                                pw.print("Unable to go deep idle; stopped at ");
+                                pw.println(stateToString(mState));
+                                exitForceIdleLocked();
+                                return -1;
+                            }
+                            curState = mState;
+                        }
+                        pw.println("Now forced in to deep idle mode");
+                    } else if ("light".equals(arg)) {
+                        mForceIdle = true;
+                        becomeInactiveIfAppropriateLocked();
+                        int curLightState = mLightState;
+                        while (curLightState != LIGHT_STATE_IDLE) {
+                            stepIdleStateLocked("s:shell");
+                            if (curLightState == mLightState) {
+                                pw.print("Unable to go light idle; stopped at ");
+                                pw.println(lightStateToString(mLightState));
+                                exitForceIdleLocked();
+                                return -1;
+                            }
+                            curLightState = mLightState;
+                        }
+                        pw.println("Now forced in to light idle mode");
+                    } else {
+                        pw.println("Unknown idle mode: " + arg);
                     }
-                    pw.println("Now forced in to idle mode");
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
             }
+        } else if ("force-inactive".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mForceIdle = true;
+                    becomeInactiveIfAppropriateLocked();
+                    pw.print("Light state: ");
+                    pw.print(lightStateToString(mLightState));
+                    pw.print(", deep state: ");
+                    pw.println(stateToString(mState));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        } else if ("unforce".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    exitForceIdleLocked();
+                    pw.print("Light state: ");
+                    pw.print(lightStateToString(mLightState));
+                    pw.print(", deep state: ");
+                    pw.println(stateToString(mState));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        } else if ("get".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                String arg = shell.getNextArg();
+                if (arg != null) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        switch (arg) {
+                            case "light": pw.println(lightStateToString(mLightState)); break;
+                            case "deep": pw.println(stateToString(mState)); break;
+                            case "force": pw.println(mForceIdle); break;
+                            case "screen": pw.println(mScreenOn); break;
+                            case "charging": pw.println(mCharging); break;
+                            case "network": pw.println(mNetworkConnected); break;
+                            default: pw.println("Unknown get option: " + arg); break;
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                } else {
+                    pw.println("Argument required");
+                }
+            }
         } else if ("disable".equals(cmd)) {
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
@@ -2829,6 +2964,7 @@
             pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
             pw.print("  mMotionActive="); pw.println(mMotionListener.active);
             pw.print("  mNotMoving="); pw.println(mNotMoving);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4ec1f61..fdcc242 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1650,6 +1650,13 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
+                    // Normally the screenshot will be taken in makeInvisible(). When an activity
+                    // is finishing, we no longer change its visibility, but we still need to take
+                    // the screenshots if startPausingLocked decided it should be taken.
+                    if (r.mUpdateTaskThumbnailWhenHidden) {
+                        r.updateThumbnailLocked(screenshotActivitiesLocked(r), null);
+                        r.mUpdateTaskThumbnailWhenHidden = false;
+                    }
                     continue;
                 }
                 final boolean isTop = r == top;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f471af6..58db985 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3954,25 +3954,16 @@
 
         public void applyAllVolumes() {
             synchronized (VolumeStreamState.class) {
-                // apply default volume first: by convention this will reset all
-                // devices volumes in audio policy manager to the supplied value
+                // apply device specific volumes first
                 int index;
-                if (mIsMuted) {
-                    index = 0;
-                } else {
-                    index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
-                }
-                AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
-                // then apply device specific volumes
                 for (int i = 0; i < mIndexMap.size(); i++) {
-                    int device = mIndexMap.keyAt(i);
+                    final int device = mIndexMap.keyAt(i);
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         if (mIsMuted) {
                             index = 0;
                         } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                                 mAvrcpAbsVolSupported)
-                                    || ((device & mFullVolumeDevices) != 0))
-                        {
+                                    || ((device & mFullVolumeDevices) != 0)) {
                             index = (mIndexMax + 5)/10;
                         } else {
                             index = (mIndexMap.valueAt(i) + 5)/10;
@@ -3980,6 +3971,15 @@
                         AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
                     }
                 }
+                // apply default volume last: by convention , default device volume will be used
+                // by audio policy manager if no explicit volume is present for a given device type
+                if (mIsMuted) {
+                    index = 0;
+                } else {
+                    index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
+                }
+                AudioSystem.setStreamVolumeIndex(
+                        mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
             }
         }
 
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 5ad8189..be9d800 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -77,8 +77,10 @@
         if (cs != null) {
             if (cs.getActiveNetworkInfo() != null) {
                 mNetworkConnected = cs.getActiveNetworkInfo().isConnected();
+                mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
+            } else {
+                mNetworkConnected = mNetworkUnmetered = false;
             }
-            mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 99c41ea..f20d0a1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -30,8 +30,11 @@
 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
 import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
 import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
+import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
@@ -39,8 +42,6 @@
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.annotation.Nullable;
@@ -97,6 +98,7 @@
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
@@ -114,6 +116,7 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -136,7 +139,6 @@
 
 import libcore.io.IoUtils;
 
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
@@ -158,7 +160,6 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -248,8 +249,9 @@
     private String mSoundNotificationKey;
     private String mVibrateNotificationKey;
 
-    private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
-    private ComponentName mEffectsSuppressor;
+    private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
+            new SparseArray<ArraySet<ManagedServiceInfo>>();
+    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
     private int mListenerHints;  // right now, all hints are global
     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
 
@@ -263,6 +265,7 @@
             new ArrayList<NotificationRecord>();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
+    final ArrayMap<String, String> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
     final PolicyAccess mPolicyAccess = new PolicyAccess();
@@ -283,11 +286,6 @@
     private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
     private static final String ATTR_VERSION = "version";
 
-    // Obsolete:  converted if present, but not resaved to disk.
-    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
-    private static final String TAG_PACKAGE = "package";
-    private static final String ATTR_NAME = "name";
-
     private RankingHelper mRankingHelper;
 
     private final UserProfiles mUserProfiles = new UserProfiles();
@@ -1118,23 +1116,112 @@
     }
 
     private void updateListenerHintsLocked() {
-        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
+        final int hints = calculateHints();
         if (hints == mListenerHints) return;
-        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
+        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
         mListenerHints = hints;
         scheduleListenerHintsChanged(hints);
     }
 
     private void updateEffectsSuppressorLocked() {
-        final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
-                ? mListenersDisablingEffects.valueAt(0).component : null;
-        if (Objects.equals(suppressor, mEffectsSuppressor)) return;
-        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
-        mEffectsSuppressor = suppressor;
-        mZenModeHelper.setEffectsSuppressed(suppressor != null);
+        final long updatedSuppressedEffects = calculateSuppressedEffects();
+        if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+        final List<ComponentName> suppressors = getSuppressors();
+        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
+        mEffectsSuppressors = suppressors;
+        mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private ArrayList<ComponentName> getSuppressors() {
+        ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+
+            for (ManagedServiceInfo info : serviceInfoList) {
+                names.add(info.component);
+            }
+        }
+
+        return names;
+    }
+
+    private boolean removeDisabledHints(ManagedServiceInfo info) {
+        return removeDisabledHints(info, 0);
+    }
+
+    private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
+        boolean removed = false;
+
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            final int hint = mListenersDisablingEffects.keyAt(i);
+            final ArraySet<ManagedServiceInfo> listeners =
+                    mListenersDisablingEffects.valueAt(i);
+
+            if (hints == 0 || (hint & hints) == hint) {
+                removed = removed || listeners.remove(info);
+            }
+        }
+
+        return removed;
+    }
+
+    private void addDisabledHints(ManagedServiceInfo info, int hints) {
+        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
+        }
+
+        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+        }
+
+        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
+        }
+    }
+
+    private void addDisabledHint(ManagedServiceInfo info, int hint) {
+        if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
+            mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
+        }
+
+        ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
+        hintListeners.add(info);
+    }
+
+    private int calculateHints() {
+        int hints = 0;
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            int hint = mListenersDisablingEffects.keyAt(i);
+            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+
+            if (!serviceInfoList.isEmpty()) {
+                hints |= hint;
+            }
+        }
+
+        return hints;
+    }
+
+    private long calculateSuppressedEffects() {
+        int hints = calculateHints();
+        long suppressedEffects = 0;
+
+        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
+        }
+
+        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
+        }
+
+        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
+        }
+
+        return suppressedEffects;
+    }
+
     private void updateInterruptionFilterLocked() {
         int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
         if (interruptionFilter == mInterruptionFilter) return;
@@ -1259,10 +1346,13 @@
             checkCallerIsSystemOrSameApp(pkg);
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
-            // Don't allow client applications to cancel foreground service notis.
+            // Don't allow client applications to cancel foreground service notis or autobundled
+            // summaries.
             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
-                    Binder.getCallingUid() == Process.SYSTEM_UID
-                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
+                    (Binder.getCallingUid() == Process.SYSTEM_UID
+                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
+                            | (Binder.getCallingUid() == Process.SYSTEM_UID
+                            ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
                     REASON_APP_CANCEL, null);
         }
 
@@ -1404,7 +1494,9 @@
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
                     final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
+                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
+                            && (sbn.getNotification().flags
+                            & Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
                         // We could pass back a cloneLight() but clients might get confused and
                         // try to send this thing back to notify() again, which would not work
                         // very well.
@@ -1519,7 +1611,8 @@
             checkCallerIsSystemOrSameApp(component.getPackageName());
             long identity = Binder.clearCallingIdentity();
             try {
-                ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
+                ManagedServices manager =
+                        mRankerServices.isComponentEnabledForCurrentProfiles(component)
                         ? mRankerServices
                         : mListeners;
                 manager.setComponentState(component, true);
@@ -1651,11 +1744,14 @@
             try {
                 synchronized (mNotificationList) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                    final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
+                    final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
+                            | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+                            | HINT_HOST_DISABLE_CALL_EFFECTS;
+                    final boolean disableEffects = (hints & disableEffectsMask) != 0;
                     if (disableEffects) {
-                        mListenersDisablingEffects.add(info);
+                        addDisabledHints(info, hints);
                     } else {
-                        mListenersDisablingEffects.remove(info);
+                        removeDisabledHints(info, hints);
                     }
                     updateListenerHintsLocked();
                     updateEffectsSuppressorLocked();
@@ -1913,7 +2009,7 @@
         @Override
         public ComponentName getEffectsSuppressor() {
             enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
-            return mEffectsSuppressor;
+            return mEffectsSuppressors.get(0);
         }
 
         @Override
@@ -2035,25 +2131,123 @@
         }
 
         @Override
-        public void setImportanceFromRankerService(INotificationListener token, String key,
-                int importance, CharSequence explanation) throws RemoteException {
-            if (importance == IMPORTANCE_NONE) {
-                throw new IllegalArgumentException("blocking not allowed: key=" + key);
-            }
+        public void applyAdjustmentFromRankerService(INotificationListener token,
+                Adjustment adjustment) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
                     mRankerServices.checkServiceTokenLocked(token);
-                    NotificationRecord n = mNotificationsByKey.get(key);
-                    n.setImportance(importance, explanation);
-                    mRankingHandler.requestSort();
+                    applyAdjustmentLocked(adjustment);
                 }
+                maybeAddAutobundleSummary(adjustment);
+                mRankingHandler.requestSort();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void applyAdjustmentsFromRankerService(INotificationListener token,
+                List<Adjustment> adjustments) throws RemoteException {
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    mRankerServices.checkServiceTokenLocked(token);
+                    for (Adjustment adjustment : adjustments) {
+                        applyAdjustmentLocked(adjustment);
+                    }
+                }
+                for (Adjustment adjustment : adjustments) {
+                    maybeAddAutobundleSummary(adjustment);
+                }
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
     };
 
+    private void applyAdjustmentLocked(Adjustment adjustment) {
+        maybeClearAutobundleSummaryLocked(adjustment);
+        NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
+        if (n == null) {
+            return;
+        }
+        if (adjustment.getImportance() != IMPORTANCE_NONE) {
+            n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
+        }
+        if (adjustment.getSignals() != null) {
+            Bundle.setDefusable(adjustment.getSignals(), true);
+            n.sbn.setOverrideGroupKey(adjustment.getSignals().getString(
+                    Adjustment.GROUP_KEY_OVERRIDE_KEY, null));
+        }
+    }
+
+    // Clears the 'fake' auto-bunding summary.
+    private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
+        if (adjustment.getSignals() != null
+                && adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
+                && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
+            if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
+                // Clear summary.
+                final NotificationRecord removed = mNotificationsByKey.get(
+                        mAutobundledSummaries.remove(adjustment.getPackage()));
+                if (removed != null) {
+                    mNotificationList.remove(removed);
+                    cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+                }
+            }
+        }
+    }
+
+    // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
+    private void maybeAddAutobundleSummary(Adjustment adjustment) {
+        if (adjustment.getSignals() != null
+                && adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
+            final String newAutoBundleKey =
+                    adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
+            int userId = -1;
+            NotificationRecord summaryRecord = null;
+            synchronized (mNotificationList) {
+                if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
+                        && newAutoBundleKey != null) {
+                    // Add summary
+                    final StatusBarNotification adjustedSbn
+                            = mNotificationsByKey.get(adjustment.getKey()).sbn;
+
+                    final ApplicationInfo appInfo =
+                            adjustedSbn.getNotification().extras.getParcelable(
+                                    Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                    final Bundle extras = new Bundle();
+                    extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
+                    final Notification summaryNotification =
+                            new Notification.Builder(getContext()).setSmallIcon(
+                                    adjustedSbn.getNotification().getSmallIcon())
+                                    .setGroupSummary(true)
+                                    .setGroup(newAutoBundleKey)
+                                    .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
+                                    .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+                                    .build();
+                    summaryNotification.extras.putAll(extras);
+                    final StatusBarNotification summarySbn =
+                            new StatusBarNotification(adjustedSbn.getPackageName(),
+                                    adjustedSbn.getOpPkg(),
+                                    Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
+                                    adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
+                                    summaryNotification, adjustedSbn.getUser(), newAutoBundleKey,
+                                    System.currentTimeMillis());
+                    summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                    mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
+                    userId = adjustedSbn.getUser().getIdentifier();
+                }
+            }
+            if (summaryRecord != null) {
+                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
+            }
+        }
+    }
+
     private String disableNotificationEffects(NotificationRecord record) {
         if (mDisableNotificationEffects) {
             return "booleanState";
@@ -2175,9 +2369,19 @@
                 pw.print("    mListenersDisablingEffects: (");
                 N = mListenersDisablingEffects.size();
                 for (int i = 0; i < N; i++) {
-                    final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
-                    if (i > 0) pw.print(',');
-                    pw.print(listener.component);
+                    final int hint = mListenersDisablingEffects.keyAt(i);
+                    if (i > 0) pw.print(';');
+                    pw.print("hint[" + hint + "]:");
+
+                    final ArraySet<ManagedServiceInfo> listeners =
+                            mListenersDisablingEffects.valueAt(i);
+                    final int listenerSize = listeners.size();
+
+                    for (int j = 0; j < listenerSize; j++) {
+                        if (i > 0) pw.print(',');
+                        final ManagedServiceInfo listener = listeners.valueAt(i);
+                        pw.print(listener.component);
+                    }
                 }
                 pw.println(')');
                 pw.println("\n  mRankerServicePackageName: " + mRankerServicePackageName);
@@ -2253,6 +2457,17 @@
                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
         final UserHandle user = new UserHandle(userId);
 
+        // Fix the notification as best we can.
+        try {
+            Notification.addFieldsFromContext(getContext().createApplicationContext(
+                    getContext().getPackageManager().getApplicationInfoAsUser(
+                            pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId),
+                    Context.CONTEXT_RESTRICTED), notification);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Cannot create a context for sending app", e);
+            return;
+        }
+
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
@@ -2492,7 +2707,7 @@
             StatusBarNotification sbn = r.sbn;
             String group = sbn.getGroupKey();
             boolean isSummary = sbn.getNotification().isGroupSummary();
-            boolean isChild = sbn.getNotification().isGroupChild();
+            boolean isChild = !isSummary && sbn.isGroup();
 
             NotificationRecord summary = mSummaryByGroupKey.get(group);
             if (isChild && summary != null) {
@@ -2857,11 +3072,13 @@
         synchronized (mNotificationList) {
             final int N = mNotificationList.size();
             ArrayList<String> orderBefore = new ArrayList<String>(N);
+            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
-            int [] importances = new int[N];
+            int[] importances = new int[N];
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
+                groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
                 importances[i] = r.getImportance();
                 mRankingHelper.extractSignals(r);
@@ -2871,7 +3088,8 @@
                 final NotificationRecord r = mNotificationList.get(i);
                 if (!orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || importances[i] != r.getImportance()) {
+                        || importances[i] != r.getImportance()
+                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
                     scheduleSendRankingUpdate();
                     return;
                 }
@@ -3070,6 +3288,7 @@
         mLights.remove(canceledKey);
 
         // Record usage stats
+        // TODO: add unbundling stats?
         switch (reason) {
             case REASON_DELEGATE_CANCEL:
             case REASON_DELEGATE_CANCEL_ALL:
@@ -3089,6 +3308,9 @@
         if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
             mSummaryByGroupKey.remove(groupKey);
         }
+        if (r.sbn.getKey().equals(mAutobundledSummaries.get(r.sbn.getPackageName()))) {
+            mAutobundledSummaries.remove(r.sbn.getPackageName());
+        }
 
         // Save it for users of getHistoricalNotifications()
         mArchive.record(r.sbn);
@@ -3287,7 +3509,7 @@
         for (int i = N - 1; i >= 0; i--) {
             NotificationRecord childR = mNotificationList.get(i);
             StatusBarNotification childSbn = childR.sbn;
-            if (childR.getNotification().isGroupChild() &&
+            if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
                     childR.getGroupKey().equals(r.getGroupKey())) {
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
@@ -3438,6 +3660,7 @@
         ArrayList<String> keys = new ArrayList<String>(N);
         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
         ArrayList<Integer> importance = new ArrayList<>(N);
+        Bundle overrideGroupKeys = new Bundle();
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
@@ -3461,6 +3684,7 @@
                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
+            overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -3470,7 +3694,7 @@
             importanceAr[i] = importance.get(i);
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
-                suppressedVisualEffects, importanceAr, explanation);
+                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
@@ -3690,7 +3914,7 @@
 
         @Override
         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
-            if (mListenersDisablingEffects.remove(removed)) {
+            if (removeDisabledHints(removed)) {
                 updateListenerHintsLocked();
                 updateEffectsSuppressorLocked();
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index fd893fa..a89a422 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -24,18 +24,15 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
-import android.os.Build;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.EventLogTags;
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c45071b..207bdba 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -31,6 +31,7 @@
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 
 public class ZenLog {
     private static final String TAG = "ZenLog";
@@ -126,10 +127,11 @@
         append(TYPE_DISABLE_EFFECTS, record.getKey() + "," + reason);
     }
 
-    public static void traceEffectsSuppressorChanged(ComponentName oldSuppressor,
-            ComponentName newSuppressor) {
-        append(TYPE_SUPPRESSOR_CHANGED, componentToString(oldSuppressor) + "->"
-            + componentToString(newSuppressor));
+    public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
+            List<ComponentName> newSuppressors, long suppressedEffects) {
+        append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+                + componentListToString(oldSuppressors) + "->"
+                + componentListToString(newSuppressors));
     }
 
     public static void traceListenerHintsChanged(int oldHints, int newHints, int listenerCount) {
@@ -193,6 +195,19 @@
         return component != null ? component.toShortString() : null;
     }
 
+    private static String componentListToString(List<ComponentName> components) {
+        StringBuilder stringBuilder = new StringBuilder();
+
+        for (int i = 0; i < components.size(); ++i) {
+            if (i > 0) {
+                stringBuilder.append(", ");
+            }
+            stringBuilder.append(componentToString(components.get(i)));
+        }
+
+        return stringBuilder.toString();
+    }
+
     private static void append(int type, String msg) {
         synchronized(MSGS) {
             TIMES[sNext] = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5c5c8f8..eb49e9f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -102,7 +102,12 @@
     private ZenModeConfig mConfig;
     private AudioManagerInternal mAudioManager;
     private PackageManager mPm;
-    private boolean mEffectsSuppressed;
+    private long mSuppressedEffects;
+
+    public static final long SUPPRESSED_EFFECT_NOTIFICATIONS = 1;
+    public static final long SUPPRESSED_EFFECT_CALLS = 1 << 1;
+    public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
+            | SUPPRESSED_EFFECT_NOTIFICATIONS;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
@@ -228,12 +233,16 @@
         }
     }
 
-    public void setEffectsSuppressed(boolean effectsSuppressed) {
-        if (mEffectsSuppressed == effectsSuppressed) return;
-        mEffectsSuppressed = effectsSuppressed;
+    public void setSuppressedEffects(long suppressedEffects) {
+        if (mSuppressedEffects == suppressedEffects) return;
+        mSuppressedEffects = suppressedEffects;
         applyRestrictions();
     }
 
+    public long getSuppressedEffects() {
+        return mSuppressedEffects;
+    }
+
     public int getZenMode() {
         return mZenMode;
     }
@@ -484,7 +493,8 @@
         synchronized (mConfig) {
             dump(pw, prefix, "mConfig", mConfig);
         }
-        pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
+
+        pw.print(prefix); pw.print("mSuppressedEffects="); pw.println(mSuppressedEffects);
         mFiltering.dump(pw, prefix);
         mConditions.dump(pw, prefix);
     }
@@ -708,9 +718,11 @@
         final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
 
         // notification restrictions
-        final boolean muteNotifications = mEffectsSuppressed;
+        final boolean muteNotifications =
+                (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
         // call restrictions
-        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
+        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
+                || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
         // total silence restrictions
         final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6d2f7e..8368185 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -905,7 +905,10 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
         synchronized (mSessions) {
-            mSessions.get(sessionId).setPermissionsResult(accepted);
+            PackageInstallerSession session = mSessions.get(sessionId);
+            if (session != null) {
+                session.setPermissionsResult(accepted);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7a61e79..e62450c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3055,7 +3055,7 @@
             }
             if (p == null) {
                 p = mPackages.get(packageName);
-                if (matchFactoryOnly && !isSystemApp(p)) {
+                if (matchFactoryOnly && p != null && !isSystemApp(p)) {
                     return null;
                 }
             }
@@ -8605,7 +8605,7 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        if (cpuAbiOverride == null && pkg.use32bitAbi) {
+                        if (pkg.use32bitAbi) {
                             pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                             pkg.applicationInfo.primaryCpuAbi = abi;
                         } else {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 24eac19..83cd978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -363,7 +363,7 @@
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
-    
+
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
     // names.  The packages appear everwhere else under their original
@@ -578,7 +578,7 @@
         }
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
-                p.secondaryCpuAbiString, p.secondaryCpuAbiString,
+                p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.parentPackageName, p.childPackageNames);
         mDisabledSysPackages.remove(name);
@@ -3756,7 +3756,7 @@
                 }
 
                 String tagName = parser.getName();
-                // Legacy 
+                // Legacy
                 if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
                     readDisabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index bcafddc..5b9d139 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -93,6 +93,7 @@
 
     // Indicates whether we are rebooting into safe mode
     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
+    public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
 
     // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this
     public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode";
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index bae628a..3ef077b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1214,7 +1214,10 @@
             window.type = windowState.mAttrs.type;
             window.layer = windowState.mLayer;
             window.token = windowState.mClient.asBinder();
-            window.title = windowState.mAttrs.getTitle();
+            window.title = windowState.mAttrs.accessibilityTitle;
+            if (window.title == null) {
+                window.title = windowState.mAttrs.getTitle();
+            }
             window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
 
             WindowState attachedWindow = windowState.mAttachedWindow;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3430ac9..446b74b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -720,9 +720,9 @@
                 }
             } else {
                 if (splitHorizontally) {
-                    outBounds.left = position - dockDividerWidth;
+                    outBounds.left = position + dockDividerWidth;
                 } else {
-                    outBounds.top = position - dockDividerWidth;
+                    outBounds.top = position + dockDividerWidth;
                 }
             }
             return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e32c976..27126e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7624,7 +7624,8 @@
         mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                 || volumeDownState > 0;
         try {
-            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
+            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0
+                    || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) {
                 int auditSafeMode = SystemProperties.getInt(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, 0);
 
                 if (auditSafeMode == 0) {
@@ -7648,6 +7649,7 @@
         if (mSafeMode) {
             Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                     + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+            SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
         } else {
             Log.i(TAG_WM, "SAFE MODE not enabled");
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index de485de..7f40079 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2355,6 +2355,10 @@
         return mDragResizing;
     }
 
+    boolean isDockedResizing() {
+        return mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9e3a4db..9c25f63 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1125,13 +1125,21 @@
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) {
+        if (w.isDockedResizing() ||
+                (w.isChildWindow() && w.mAttachedWindow.isDockedResizing())) {
 
             // If we are resizing with the divider, the task bounds might be smaller than the
             // stack bounds. The system decor is used to clip to the task bounds, which we don't
             // want in this case in order to avoid holes.
+            //
+            // We take care to not shrink the width, for surfaces which are larger than
+            // the display region. Of course this area will not eventually be visible
+            // but if we truncate the width now, we will calculate incorrectly
+            // when adjusting to the stack bounds.
             final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
-            mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+            mSystemDecorRect.set(0, 0,
+                    Math.max(width, displayInfo.logicalWidth),
+                    Math.max(height, displayInfo.logicalHeight));
         } else {
             mSystemDecorRect.set(0, 0, width, height);
         }
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 0da1bb1..6c8be39 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -86,6 +86,155 @@
     }
 
     private Test[] mTests = new Test[] {
+            new Test("Post a group") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6000, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("low priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_LOW)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6001, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("default priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6002, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group1")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(6003, n);
+                }
+            },
+            new Test("Post a group (2) w/o summary") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6100, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("low priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_LOW)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6101, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("default priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6102, n);
+                }
+            },
+            new Test("Summary for group 2") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group2")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(6103, n);
+                }
+            },
+            new Test("Group up public-secret") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("public notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PUBLIC)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("public", 7009, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("private only notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PRIVATE)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("no public", 7010, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("private version of notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PRIVATE)
+                            .setGroup("public-secret")
+                            .setPublicVersion(new Notification.Builder(NotificationTestList.this)
+                                    .setSmallIcon(R.drawable.icon2)
+                                    .setContentTitle("public notification of private notification")
+                                    .setPriority(Notification.PRIORITY_DEFAULT)
+                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                    .build())
+                            .build();
+                    mNM.notify("priv with pub", 7011, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("secret notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_SECRET)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("secret", 7012, n);
+
+                    Notification s = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group public-secret")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("public-secret")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(7113, s);
+                }
+            },
+            new Test("Cancel priority autogroup") {
+                public void run()
+                {
+                    try {
+                        mNM.cancel(Integer.MAX_VALUE);
+                    } catch (Exception e) {
+                        Toast.makeText(NotificationTestList.this, "cancel failed (yay)",
+                                Toast.LENGTH_LONG).show();
+                    }
+                }
+            },
             new Test("Min priority") {
                 public void run()
                 {
@@ -95,7 +244,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_MIN)
                             .build();
-                    mNM.notify(7000, n);
+                    mNM.notify("min", 7000, n);
                 }
             },
             new Test("Min priority, high pri flag") {
@@ -123,7 +272,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_LOW)
                             .build();
-                    mNM.notify(7002, n);
+                    mNM.notify("low", 7002, n);
                 }
             },
             new Test("Default priority") {
@@ -135,7 +284,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .build();
-                    mNM.notify(7004, n);
+                    mNM.notify("default", 7004, n);
                 }
             },
             new Test("High priority") {
@@ -150,7 +299,7 @@
                                     getPackageName() + "/raw/ringer"))
                             .setPriority(Notification.PRIORITY_HIGH)
                             .build();
-                    mNM.notify(7006, n);
+                    mNM.notify("high", 7006, n);
                 }
             },
             new Test("Max priority") {
@@ -166,7 +315,7 @@
                             .setPriority(Notification.PRIORITY_MAX)
                             .setFullScreenIntent(makeIntent2(), false)
                             .build();
-                    mNM.notify(7007, n);
+                    mNM.notify("max", 7007, n);
                 }
             },
             new Test("Max priority with delay") {
@@ -199,7 +348,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_PUBLIC)
                             .build();
-                    mNM.notify(7009, n);
+                    mNM.notify("public", 7009, n);
                 }
             },
             new Test("private notification, no public") {
@@ -212,7 +361,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_PRIVATE)
                             .build();
-                    mNM.notify(7010, n);
+                    mNM.notify("no public", 7010, n);
                 }
             },
             new Test("private notification, has public") {
@@ -231,7 +380,7 @@
                                     .setVisibility(Notification.VISIBILITY_PUBLIC)
                                     .build())
                             .build();
-                    mNM.notify(7011, n);
+                    mNM.notify("priv with pub", 7011, n);
                 }
             },
             new Test("secret notification") {
@@ -244,7 +393,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_SECRET)
                             .build();
-                    mNM.notify(7012, n);
+                    mNM.notify("secret", 7012, n);
                 }
             },
         new Test("Off") {