Merge "Make JobSchedulerService encryption aware."
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index ddb2d46..7d0d1b4 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -27,7 +27,7 @@
         return gApplicationLoaders;
     }
 
-    public ClassLoader getClassLoader(String zip, String librarySearchPath,
+    public ClassLoader getClassLoader(String zip, boolean isBundled, String librarySearchPath,
                                       String libraryPermittedPath, ClassLoader parent)
     {
         /*
@@ -56,7 +56,8 @@
     
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
                 PathClassLoader pathClassloader =
-                    new PathClassLoader(zip, librarySearchPath, libraryPermittedPath, parent);
+                    new PathClassLoader(zip, isBundled, librarySearchPath,
+                                        libraryPermittedPath, parent);
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
                 mLoaders.put(zip, pathClassloader);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 7313fd1..855b21e 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -366,12 +366,21 @@
                     }
                 }
 
+                String libraryPermittedPath = mAppDir + File.pathSeparator + mDataDir;
+                boolean isBundledApp = false;
+
                 if (mApplicationInfo.isSystemApp()) {
+                    isBundledApp = true;
                     // Add path to system libraries to libPaths;
                     // Access to system libs should be limited
                     // to bundled applications; this is why updated
                     // system apps are not included.
                     libPaths.add(System.getProperty("java.library.path"));
+
+                    // This is necessary to grant bundled apps access to
+                    // libraries located in subdirectories of /system/lib
+                    libraryPermittedPath += File.pathSeparator +
+                                            System.getProperty("java.library.path");
                 }
 
                 final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
@@ -389,10 +398,8 @@
                 // as this is early and necessary.
                 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
 
-                String libraryPermittedPath = mAppDir + File.pathSeparator + mDataDir;
-
-                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, librarySearchPath,
-                        libraryPermittedPath, mBaseClassLoader);
+                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
+                        librarySearchPath, libraryPermittedPath, mBaseClassLoader);
 
                 StrictMode.setThreadPolicy(oldPolicy);
             } else {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 620ab50..3ff0896 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3001,6 +3001,7 @@
             resetNotificationHeader(contentView);
             resetContentMargins(contentView);
             contentView.setViewVisibility(R.id.right_icon, View.GONE);
+            contentView.setViewVisibility(R.id.title, View.GONE);
             contentView.setTextViewText(R.id.title, null);
             contentView.setTextViewText(R.id.text, null);
             contentView.setViewVisibility(R.id.line3, View.GONE);
@@ -3047,6 +3048,7 @@
             bindNotificationHeader(contentView);
             bindLargeIcon(contentView);
             if (ex.getCharSequence(EXTRA_TITLE) != null) {
+                contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title,
                         processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
             }
@@ -3065,10 +3067,26 @@
             }
             // Note getStandardView may hide line 3 again.
             contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
+            setContentMinHeight(contentView, showProgress || mN.mLargeIcon != null);
 
             return contentView;
         }
 
+        /**
+         * @param remoteView the remote view to update the minheight in
+         * @param hasMinHeight does it have a mimHeight
+         * @hide
+         */
+        void setContentMinHeight(RemoteViews remoteView, boolean hasMinHeight) {
+            int minHeight = 0;
+            if (hasMinHeight) {
+                // we need to set the minHeight of the notification
+                minHeight = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.notification_min_content_height);
+            }
+            remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
+        }
+
         private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
             final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
             final int progress = ex.getInt(EXTRA_PROGRESS, 0);
@@ -3312,6 +3330,37 @@
             return applyStandardTemplateWithActions(getBigBaseLayoutResource());
         }
 
+        /**
+         * Construct a RemoteViews for the display in public contexts like on the lockscreen.
+         *
+         * @hide
+         */
+        public RemoteViews makePublicContentView() {
+            if (mN.publicVersion != null) {
+                final Builder builder = recoverBuilder(mContext, mN.publicVersion);
+                return builder.makeContentView();
+            }
+            Bundle savedBundle = mN.extras;
+            Style style = mStyle;
+            mStyle = null;
+            Icon largeIcon = mN.mLargeIcon;
+            mN.mLargeIcon = null;
+            Bundle publicExtras = new Bundle();
+            publicExtras.putBoolean(EXTRA_SHOW_WHEN,
+                    savedBundle.getBoolean(EXTRA_SHOW_WHEN));
+            publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER,
+                    savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER));
+            publicExtras.putCharSequence(EXTRA_TITLE,
+                    mContext.getString(R.string.notification_hidden_text));
+            mN.extras = publicExtras;
+            final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
+            mN.extras = savedBundle;
+            mN.mLargeIcon = largeIcon;
+            mStyle = style;
+            return publicView;
+        }
+
+
 
         private RemoteViews generateActionButton(Action action) {
             final boolean tombstone = (action.actionIntent == null);
@@ -3801,12 +3850,7 @@
                 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
                 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
             }
-            int imageMinHeight = mBuilder.mContext.getResources().getDimensionPixelSize(
-                    R.dimen.notification_big_picture_content_min_height_with_picture);
-            // We need to make space for the right image, so we're enforcing a minheight if there
-            // is a picture.
-            int minHeight = (mBuilder.mN.mLargeIcon == null) ? 0 : imageMinHeight;
-            contentView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
+            mBuilder.setContentMinHeight(contentView, mBuilder.mN.mLargeIcon != null);
 
             if (mBigLargeIconSet) {
                 mBuilder.mN.mLargeIcon = oldLargeIcon;
@@ -3873,8 +3917,7 @@
     public static class BigTextStyle extends Style {
 
         private static final int MAX_LINES = 13;
-        private static final int LINES_CONSUMED_BY_ACTIONS = 3;
-        private static final int LINES_CONSUMED_BY_SUMMARY = 2;
+        private static final int LINES_CONSUMED_BY_ACTIONS = 4;
 
         private CharSequence mBigText;
 
@@ -3944,8 +3987,10 @@
 
             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
 
-            contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
-            contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
+            CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
+            contentView.setTextViewText(R.id.big_text, bigTextText);
+            contentView.setViewVisibility(R.id.big_text,
+                    TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
             contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
 
             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
@@ -3958,14 +4003,9 @@
         private int calculateMaxLines() {
             int lineCount = MAX_LINES;
             boolean hasActions = mBuilder.mActions.size() > 0;
-            boolean hasSummary = (mSummaryTextSet ? mSummaryText
-                    : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
             if (hasActions) {
                 lineCount -= LINES_CONSUMED_BY_ACTIONS;
             }
-            if (hasSummary) {
-                lineCount -= LINES_CONSUMED_BY_SUMMARY;
-            }
             return lineCount;
         }
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2d7ea2e..537f887 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12482,8 +12482,7 @@
      * Determines whether the given point, in local coordinates is inside the view.
      */
     /*package*/ final boolean pointInView(float localX, float localY) {
-        return localX >= 0 && localX < (mRight - mLeft)
-                && localY >= 0 && localY < (mBottom - mTop);
+        return pointInView(localX, localY, 0);
     }
 
     /**
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 6ecb3fb..88a56d2 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -270,7 +270,7 @@
     bool needNativeBridge = false;
 
     void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader,
-                                     libraryPath, isolationPath);
+                                     false, libraryPath, isolationPath);
     if (handle == NULL) {
         if (NativeBridgeIsSupported(pathStr)) {
             handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index b69eb24..fdbbbd6 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -25,12 +25,12 @@
     <LinearLayout
         android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="top"
         android:layout_marginStart="@dimen/notification_content_margin_start"
         android:layout_marginEnd="@dimen/notification_content_margin_end"
         android:layout_marginTop="@dimen/notification_content_margin_top"
-        android:minHeight="@dimen/notification_min_content_height"
+        android:layout_marginBottom="@dimen/notification_content_margin_bottom"
         android:orientation="vertical"
         >
         <include layout="@layout/notification_template_part_line1" />
@@ -42,7 +42,7 @@
         android:layout_gravity="bottom"
         android:layout_marginStart="@dimen/notification_content_margin_start"
         android:layout_marginBottom="11dp"
-        android:layout_marginEnd="@dimen/notification_content_margin_end">
+        android:layout_marginEnd="@dimen/notification_content_margin_end" >
         <include layout="@layout/notification_template_progress" />
     </FrameLayout>
     <include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index eb02e8b..91a5ceb 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -25,7 +25,7 @@
     <FrameLayout
         android:id="@+id/status_bar_latest_event_content"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_min_height"
+        android:layout_height="wrap_content"
         android:layout_gravity="top"
         android:tag="base"
         >
@@ -38,7 +38,7 @@
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:layout_marginTop="@dimen/notification_content_margin_top"
-            android:minHeight="@dimen/notification_min_content_height"
+            android:layout_marginBottom="@dimen/notification_content_margin_bottom"
             android:orientation="vertical"
             >
             <include layout="@layout/notification_template_part_line1" />
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 0427c8a..c3db7c5 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -33,6 +33,7 @@
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/notification_content_margin_top"
         android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:layout_marginBottom="@dimen/notification_content_margin_bottom"
         android:layout_marginEnd="24dp"
         android:layout_toStartOf="@id/right_icon"
         android:minHeight="@dimen/notification_min_content_height"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 9e010f2..de9f572 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,10 +39,11 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:paddingBottom="13dp"
+            android:paddingBottom="@dimen/notification_content_margin_bottom"
             android:orientation="horizontal"
             android:gravity="top"
             android:layout_weight="1"
+            android:layout_marginTop="1.5dp"
             >
             <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
                 android:textAppearance="@style/TextAppearance.Material.Notification"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 8c0abc9..14bf899 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -123,13 +123,6 @@
             android:visibility="gone"
             android:layout_weight="1"
             />
-        <FrameLayout
-            android:id="@+id/inbox_end_pad"
-            android:layout_width="match_parent"
-            android:layout_height="13dp"
-            android:visibility="gone"
-            android:layout_weight="0"
-        />
         <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
     <include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index dc4afb8..f0ced5f 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -29,7 +29,7 @@
     <LinearLayout
         android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_min_content_height"
+        android:layout_height="wrap_content"
         android:background="#00000000"
         android:orientation="horizontal"
         android:layout_marginStart="@dimen/notification_content_margin_start"
@@ -43,6 +43,7 @@
             android:layout_gravity="fill_vertical"
             android:layout_weight="1"
             android:minHeight="@dimen/notification_min_content_height"
+            android:paddingBottom="@dimen/notification_content_margin_bottom"
             android:orientation="vertical"
             >
             <include layout="@layout/notification_template_part_line1"/>
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index e7ac408..308b30f 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -20,7 +20,6 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
-    android:layout_marginBottom="1dp"
     >
     <TextView android:id="@+id/title"
         android:textAppearance="@style/TextAppearance.Material.Notification.Title"
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index 76337ac..dc47a48 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -20,17 +20,18 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
-    android:gravity="center_vertical"
+    android:gravity="top"
     >
     <TextView android:id="@+id/text"
         android:textAppearance="@style/TextAppearance.Material.Notification"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:layout_gravity="center"
+        android:layout_gravity="top"
         android:singleLine="true"
         android:ellipsize="marquee"
         android:fadingEdge="horizontal"
+        android:layout_marginTop="1.5dp"
         />
     <ImageView android:id="@+id/profile_badge_line3"
         android:layout_width="@dimen/notification_badge_size"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b167711..ec24af5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -142,6 +142,9 @@
     <!-- height of the content margin to accomodate for the header -->
     <dimen name="notification_content_margin_top">30dp</dimen>
 
+    <!-- height of the content margin on the bottom -->
+    <dimen name="notification_content_margin_bottom">13dp</dimen>
+
     <!-- height of notification header view if present -->
     <dimen name="notification_header_height">32dp</dimen>
 
@@ -153,14 +156,11 @@
     <!-- The width of the big icons in notifications. -->
     <dimen name="notification_large_icon_height">64dp</dimen>
 
-    <!--  Min height of the notification content. -->
-    <dimen name="notification_min_content_height">54dp</dimen>
-
     <!-- The minimum width of the app name in the header if it shrinks -->
     <dimen name="notification_header_shrink_min_width">72dp</dimen>
 
-    <!-- The minimum height of the content if there is a picture present with big picture -->
-    <dimen name="notification_big_picture_content_min_height_with_picture">41dp</dimen>
+    <!-- The minimum height of the content if there are at least two lines or a picture-->
+    <dimen name="notification_min_content_height">41dp</dimen>
 
     <!-- Preferred width of the search view. -->
     <dimen name="search_view_preferred_width">320dip</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a127d94..595ef54 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -537,6 +537,9 @@
     <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
     <string name="notification_header_divider_symbol" translatable="false">•</string>
 
+    <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
+    <string name="notification_hidden_text">Contents hidden</string>
+
     <!-- Displayed to the user to tell them that they have started up the phone in "safe mode" -->
     <string name="safeMode">Safe mode</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3ee1ca9..2c54af7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2362,6 +2362,7 @@
   <java-symbol type="id" name="deleteButton" />
 
   <java-symbol type="string" name="notification_children_count_bracketed" />
+  <java-symbol type="string" name="notification_hidden_text" />
   <java-symbol type="id" name="app_name_text" />
   <java-symbol type="id" name="number_of_children" />
   <java-symbol type="id" name="header_sub_text" />
@@ -2377,7 +2378,7 @@
   <java-symbol type="drawable" name="ic_expand_bundle" />
   <java-symbol type="drawable" name="ic_collapse_bundle" />
   <java-symbol type="dimen" name="notification_header_height" />
-  <java-symbol type="dimen" name="notification_big_picture_content_min_height_with_picture" />
+  <java-symbol type="dimen" name="notification_min_content_height" />
   <java-symbol type="dimen" name="notification_header_shrink_min_width" />
   <java-symbol type="dimen" name="notification_content_margin_start" />
   <java-symbol type="dimen" name="notification_content_margin_end" />
diff --git a/libs/common_time/common_time_server.cpp b/libs/common_time/common_time_server.cpp
index 01372e0..f72ffaa 100644
--- a/libs/common_time/common_time_server.cpp
+++ b/libs/common_time/common_time_server.cpp
@@ -143,7 +143,7 @@
 
     // Create the eventfd we will use to signal our thread to wake up when
     // needed.
-    mWakeupThreadFD = eventfd(0, EFD_NONBLOCK);
+    mWakeupThreadFD = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
 
     // seed the random number generator (used to generated timeline IDs)
     srand48(static_cast<unsigned int>(systemTime()));
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 0cea7ae..62fdd42 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -65,7 +65,7 @@
         android:id="@+id/notification_guts_stub"
         android:inflatedId="@+id/notification_guts"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         />
 
 </com.android.systemui.statusbar.ExpandableNotificationRow>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f70f38b..24cc6bf 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -49,11 +49,14 @@
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
     <!-- Height of a heads up notification in the status bar -->
-    <dimen name="notification_max_heads_up_height">140dp</dimen>
+    <dimen name="notification_max_heads_up_height">141dp</dimen>
 
     <!-- Height of a the summary ("more card") notification on keyguard. -->
     <dimen name="notification_summary_height">44dp</dimen>
 
+    <!-- Minimum layouted height of a notification in the statusbar-->
+    <dimen name="min_notification_layout_height">48dp</dimen>
+
     <!-- size at which Notification icons will be drawn in the status bar -->
     <dimen name="status_bar_icon_drawing_size">17dip</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 50e0661..6658cfe 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -906,9 +906,6 @@
     <!-- Battery saver notification action text. [CHAR LIMIT=60] -->
     <string name="battery_saver_notification_action_text">Turn off battery saver</string>
 
-    <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
-    <string name="notification_hidden_text">Contents hidden</string>
-
     <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
     <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index aad428a..7e01866 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -237,12 +237,6 @@
         <item name="android:gravity">center</item>
     </style>
 
-    <style name="TextAppearance.Material.Notification.Parenthetical"
-           parent="@*android:style/TextAppearance.Material.Notification">
-        <item name="android:textStyle">italic</item>
-        <item name="android:textColor">#60000000</item>
-    </style>
-
     <style name="TextAppearance.Material.Notification.HeaderTitle"
         parent="@*android:style/TextAppearance.Material.Notification.Info">
     </style>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a6ca50a..3537d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -110,6 +110,7 @@
 import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -988,9 +989,7 @@
     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
         return new SwipeHelper.LongPressListener() {
             @Override
-            public boolean onLongPress(View v, int x, int y) {
-                dismissPopups();
-
+            public boolean onLongPress(View v, final int x, final int y) {
                 if (!(v instanceof ExpandableNotificationRow)) {
                     return false;
                 }
@@ -1011,41 +1010,57 @@
 
                 // Already showing?
                 if (guts.getVisibility() == View.VISIBLE) {
-                    Log.e(TAG, "Trying to show notification guts, but already visible");
+                    dismissPopups(x, y);
                     return false;
                 }
 
                 MetricsLogger.action(mContext, MetricsLogger.ACTION_NOTE_CONTROLS);
-                guts.setVisibility(View.VISIBLE);
 
-                final double horz = Math.max(guts.getWidth() - x, x);
-                final double vert = Math.max(guts.getActualHeight() - y, y);
-                final float r = (float) Math.hypot(horz, vert);
-                final Animator a
-                        = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
-                a.setDuration(400);
-                a.setInterpolator(mLinearOutSlowIn);
-                a.start();
-
-                mNotificationGutsExposed = guts;
-
+                // ensure that it's layouted but not visible until actually laid out
+                guts.setVisibility(View.INVISIBLE);
+                // Post to ensure the the guts are properly layed out.
+                guts.post(new Runnable() {
+                    public void run() {
+                        dismissPopups();
+                        guts.setVisibility(View.VISIBLE);
+                        final double horz = Math.max(guts.getWidth() - x, x);
+                        final double vert = Math.max(guts.getHeight() - y, y);
+                        final float r = (float) Math.hypot(horz, vert);
+                        final Animator a
+                                = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
+                        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                        a.setInterpolator(mLinearOutSlowIn);
+                        a.start();
+                        guts.setExposed(true);
+                        mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+                        mNotificationGutsExposed = guts;
+                    }
+                });
                 return true;
             }
         };
     }
 
     public void dismissPopups() {
+        dismissPopups(-1, -1);
+    }
+
+    private void dismissPopups(int x, int y) {
         if (mNotificationGutsExposed != null) {
             final NotificationGuts v = mNotificationGutsExposed;
             mNotificationGutsExposed = null;
 
             if (v.getWindowToken() == null) return;
-
-            final int x = (v.getLeft() + v.getRight()) / 2;
-            final int y = (v.getTop() + v.getActualHeight() / 2);
+            if (x == -1 || y == -1) {
+                x = (v.getLeft() + v.getRight()) / 2;
+                y = (v.getTop() + v.getHeight() / 2);
+            }
+            final double horz = Math.max(v.getWidth() - x, x);
+            final double vert = Math.max(v.getHeight() - y, y);
+            final float r = (float) Math.hypot(horz, vert);
             final Animator a = ViewAnimationUtils.createCircularReveal(v,
-                    x, y, x, 0);
-            a.setDuration(200);
+                    x, y, r, 0);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
             a.setInterpolator(mFastOutLinearIn);
             a.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -1055,6 +1070,8 @@
                 }
             });
             a.start();
+            v.setExposed(false);
+            mStackScroller.onHeightChanged(null, true /* needsAnimation */);
         }
     }
 
@@ -1382,6 +1399,7 @@
         View contentViewLocal = null;
         View bigContentViewLocal = null;
         View headsUpContentViewLocal = null;
+        View publicViewLocal = null;
         try {
             contentViewLocal = contentView.apply(
                     sbn.getPackageContext(mContext),
@@ -1399,6 +1417,11 @@
                         contentContainer,
                         mOnClickHandler);
             }
+            if (publicContentView != null) {
+                publicViewLocal = publicContentView.apply(
+                        sbn.getPackageContext(mContext),
+                        contentContainerPublic, mOnClickHandler);
+            }
         }
         catch (RuntimeException e) {
             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
@@ -1418,25 +1441,9 @@
             headsUpContentViewLocal.setIsRootNamespace(true);
             contentContainer.setHeadsUpChild(headsUpContentViewLocal);
         }
-
-        // now the public version
-        View publicViewLocal = null;
-        if (publicContentView != null) {
-            try {
-                publicViewLocal = publicContentView.apply(
-                        sbn.getPackageContext(mContext),
-                        contentContainerPublic, mOnClickHandler);
-
-                if (publicViewLocal != null) {
-                    publicViewLocal.setIsRootNamespace(true);
-                    contentContainerPublic.setContractedChild(publicViewLocal);
-                }
-            }
-            catch (RuntimeException e) {
-                final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
-                Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
-                publicViewLocal = null;
-            }
+        if (publicViewLocal != null) {
+            publicViewLocal.setIsRootNamespace(true);
+            contentContainerPublic.setContractedChild(publicViewLocal);
         }
 
         // Extract target SDK version.
@@ -1446,65 +1453,7 @@
         } catch (NameNotFoundException ex) {
             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
         }
-
-        if (publicViewLocal == null) {
-            // Add a basic notification template
-            publicViewLocal = LayoutInflater.from(mContext).inflate(
-                    R.layout.notification_public_default,
-                    contentContainerPublic, false);
-            publicViewLocal.setIsRootNamespace(true);
-
-            final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
-            try {
-                title.setText(pmUser.getApplicationLabel(
-                        pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
-            } catch (NameNotFoundException e) {
-                title.setText(entry.notification.getPackageName());
-            }
-
-            final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
-            final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
-                    R.id.profile_badge_line3);
-
-            final StatusBarIcon ic = new StatusBarIcon(
-                    entry.notification.getUser(),
-                    entry.notification.getPackageName(),
-                    entry.notification.getNotification().getSmallIcon(),
-                    entry.notification.getNotification().iconLevel,
-                    entry.notification.getNotification().number,
-                    entry.notification.getNotification().tickerText);
-
-            Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
-            icon.setImageDrawable(iconDrawable);
-
-            if (profileBadge != null) {
-                Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
-                        entry.notification.getUser(), 0);
-                if (profileDrawable != null) {
-                    profileBadge.setImageDrawable(profileDrawable);
-                    profileBadge.setVisibility(View.VISIBLE);
-                } else {
-                    profileBadge.setVisibility(View.GONE);
-                }
-            }
-
-            final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
-            final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
-            if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
-                time.setVisibility(View.VISIBLE);
-                time.setTime(entry.notification.getNotification().when);
-            }
-
-            final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
-            if (text != null) {
-                text.setText(R.string.notification_hidden_text);
-                text.setTextAppearance(mContext,
-                        R.style.TextAppearance_Material_Notification_Parenthetical);
-            }
-
-            contentContainerPublic.setContractedChild(publicViewLocal);
-            entry.autoRedacted = true;
-        }
+        entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
         if (MULTIUSER_DEBUG) {
             TextView debug = (TextView) row.findViewById(R.id.debug_info);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 874b76a..83853a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,12 +25,10 @@
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.LinearInterpolator;
 import android.widget.Chronometer;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -49,13 +47,11 @@
 
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
-    private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
     private final int mNotificationMinHeightLegacy;
     private final int mMaxHeadsUpHeightLegacy;
     private final int mMaxHeadsUpHeight;
     private final int mNotificationMinHeight;
     private final int mNotificationMaxHeight;
-    private int mRowMinHeight;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -117,7 +113,7 @@
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
-            if (mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
+            if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
                 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
                 mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
                         mGroupManager.isGroupExpanded(mStatusBarNotification));
@@ -212,6 +208,8 @@
             mNotificationParent.updateChildrenHeaderAppearance();
         }
         onChildrenCountChanged();
+        // The public layouts expand button is always visible
+        mPublicLayout.updateExpandButtons(true);
         updateLimits();
     }
 
@@ -226,10 +224,9 @@
                 != com.android.internal.R.id.status_bar_latest_event_content;
         int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
                 : mMaxHeadsUpHeight;
-        mRowMinHeight = minHeight;
         mMaxViewHeight = mNotificationMaxHeight;
-        mPrivateLayout.setHeights(mRowMinHeight, headsUpheight);
-        mPublicLayout.setHeights(mRowMinHeight, headsUpheight);
+        mPrivateLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+        mPublicLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -489,7 +486,6 @@
     @Override
     public void reset() {
         super.reset();
-        mRowMinHeight = 0;
         final boolean wasExpanded = isExpanded();
         mMaxViewHeight = 0;
         mExpandable = false;
@@ -518,11 +514,6 @@
     }
 
     @Override
-    protected boolean filterMotionEvent(MotionEvent event) {
-        return mIsHeadsUp || super.filterMotionEvent(event);
-    }
-
-    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
@@ -558,12 +549,15 @@
     }
 
     private void updateChildrenVisibility() {
+        mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
+                : INVISIBLE);
         if (mChildrenContainer == null) {
             return;
         }
-        mChildrenContainer.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
-        mNotificationHeader.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
-        mPrivateLayout.setVisibility(!mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
+        mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
+                : INVISIBLE);
+        mNotificationHeader.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
+                : INVISIBLE);
         // The limits might have changed if the view suddenly became a group or vice versa
         updateLimits();
     }
@@ -715,7 +709,7 @@
         if (expand && mExpandable) {
             setActualHeight(mMaxExpandHeight);
         } else {
-            setActualHeight(mRowMinHeight);
+            setActualHeight(getMinHeight());
         }
     }
 
@@ -725,20 +719,24 @@
             return getActualHeight();
         }
         boolean inExpansionState = isExpanded();
-        if (mSensitive && mHideSensitiveForIntrinsicHeight) {
-            return mRowMinHeight;
+        if (mGuts != null && mGuts.areGutsExposed()) {
+            return mGuts.getHeight();
+        } else if ((isChildInGroup() && !isGroupExpanded())) {
+            return mPrivateLayout.getMinHeight();
+        } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
+            return getMinHeight();
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
             return mChildrenContainer.getIntrinsicHeight();
         } else if (mIsHeadsUp) {
             if (inExpansionState) {
-                return Math.max(mMaxExpandHeight, mHeadsUpHeight);
+                return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
             } else {
-                return Math.max(mRowMinHeight, mHeadsUpHeight);
+                return Math.max(getMinHeight(), mHeadsUpHeight);
             }
-        } else if (!inExpansionState || (isChildInGroup() && !isGroupExpanded())) {
-            return getMinHeight();
-        } else {
+        } else if (inExpansionState) {
             return getMaxExpandHeight();
+        } else {
+            return getMinHeight();
         }
     }
 
@@ -845,8 +843,7 @@
             mPublicLayout.setAlpha(1f);
             mPrivateLayout.setAlpha(1f);
             mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
-            mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? View.VISIBLE
-                    : View.INVISIBLE);
+            updateChildrenVisibility();
         } else {
             animateShowingPublic(delay, duration);
         }
@@ -857,27 +854,35 @@
     }
 
     private void animateShowingPublic(long delay, long duration) {
-        final View source = mShowingPublic ? mPrivateLayout : mPublicLayout;
-        View target = mShowingPublic ? mPublicLayout : mPrivateLayout;
-        source.setVisibility(View.VISIBLE);
-        target.setVisibility(View.VISIBLE);
-        target.setAlpha(0f);
-        source.animate().cancel();
-        target.animate().cancel();
-        source.animate()
-                .alpha(0f)
-                .setStartDelay(delay)
-                .setDuration(duration)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        source.setVisibility(View.INVISIBLE);
-                    }
-                });
-        target.animate()
-                .alpha(1f)
-                .setStartDelay(delay)
-                .setDuration(duration);
+        View[] privateViews = mIsSummaryWithChildren ?
+                new View[] {mChildrenContainer, mNotificationHeader}
+                : new View[] {mPrivateLayout};
+        View[] publicViews = new View[] {mPublicLayout};
+        View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
+        View[] shownChildren = mShowingPublic ? publicViews : privateViews;
+        for (final View hiddenView : hiddenChildren) {
+            hiddenView.setVisibility(View.VISIBLE);
+            hiddenView.animate().cancel();
+            hiddenView.animate()
+                    .alpha(0f)
+                    .setStartDelay(delay)
+                    .setDuration(duration)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            hiddenView.setVisibility(View.INVISIBLE);
+                        }
+                    });
+        }
+        for (View showView : shownChildren) {
+            showView.setVisibility(View.VISIBLE);
+            showView.setAlpha(0f);
+            showView.animate().cancel();
+            showView.animate()
+                    .alpha(1f)
+                    .setStartDelay(delay)
+                    .setDuration(duration);
+        }
     }
 
     private void updateVetoButton() {
@@ -956,6 +961,9 @@
 
     @Override
     public int getMinHeight() {
+        if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
+            return mChildrenContainer.getMinHeight();
+        }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMinHeight();
     }
@@ -963,17 +971,12 @@
     @Override
     public int getMinExpandHeight() {
         if (mIsSummaryWithChildren && !mOnKeyguard) {
-            return mChildrenContainer.getMinHeight();
+            return mChildrenContainer.getMinExpandHeight();
         }
         return getMinHeight();
     }
 
     @Override
-    protected boolean shouldLimitViewHeight() {
-        return !mIsSummaryWithChildren;
-    }
-
-    @Override
     public void setClipTopAmount(int clipTopAmount) {
         super.setClipTopAmount(clipTopAmount);
         mPrivateLayout.setClipTopAmount(clipTopAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 59cbd40..d6855a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -20,10 +20,10 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+
 import com.android.systemui.R;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
@@ -45,7 +45,6 @@
     private static Rect mClipRect = new Rect();
     private boolean mWillBeGone;
     private int mMinClipTopAmount = 0;
-    private boolean mMeasuredTooHigh;
 
     public ExpandableView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -55,13 +54,10 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        boolean limitViewHeight = shouldLimitViewHeight();
         final int givenSize = MeasureSpec.getSize(heightMeasureSpec);
-        int ownMaxHeight = limitViewHeight ? mMaxViewHeight : Integer.MAX_VALUE;
+        int ownMaxHeight = Integer.MAX_VALUE;
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
-        if (hasFixedHeight) {
-            // We have a height set in our layout, so we want to be at most as big as given
+        if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) {
             ownMaxHeight = Math.min(givenSize, ownMaxHeight);
         }
         int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
@@ -77,7 +73,7 @@
             if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
                 if (layoutParams.height >= 0) {
                     // An actual height is set
-                    childHeightSpec = layoutParams.height > ownMaxHeight && limitViewHeight
+                    childHeightSpec = layoutParams.height > ownMaxHeight
                         ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
                         : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
                 }
@@ -90,7 +86,8 @@
                 mMatchParentViews.add(child);
             }
         }
-        int ownHeight = hasFixedHeight ? ownMaxHeight : Math.min(ownMaxHeight, maxChildHeight);
+        int ownHeight = heightMode == MeasureSpec.EXACTLY
+                ? givenSize : Math.min(ownMaxHeight, maxChildHeight);
         newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
         for (View child : mMatchParentViews) {
             child.measure(getChildMeasureSpec(
@@ -100,11 +97,6 @@
         mMatchParentViews.clear();
         int width = MeasureSpec.getSize(widthMeasureSpec);
         setMeasuredDimension(width, ownHeight);
-        mMeasuredTooHigh = heightMode != MeasureSpec.UNSPECIFIED && ownHeight > givenSize;
-    }
-
-    protected boolean shouldLimitViewHeight() {
-        return true;
     }
 
     @Override
@@ -133,26 +125,11 @@
     }
 
     @Override
-    public boolean dispatchGenericMotionEvent(MotionEvent ev) {
-        if (filterMotionEvent(ev)) {
-            return super.dispatchGenericMotionEvent(ev);
-        }
-        return false;
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (filterMotionEvent(ev)) {
-            return super.dispatchTouchEvent(ev);
-        }
-        return false;
-    }
-
-    protected boolean filterMotionEvent(MotionEvent event) {
-        return event.getActionMasked() != MotionEvent.ACTION_DOWN
-                && event.getActionMasked() != MotionEvent.ACTION_HOVER_ENTER
-                && event.getActionMasked() != MotionEvent.ACTION_HOVER_MOVE
-                || event.getY() > mClipTopAmount && event.getY() < mActualHeight;
+    public boolean pointInView(float localX, float localY, float slop) {
+        float top = mClipTopAmount;
+        float bottom = mActualHeight;
+        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (bottom + slop);
     }
 
     /**
@@ -397,7 +374,8 @@
 
     @Override
     public boolean hasOverlappingRendering() {
-        return super.hasOverlappingRendering() && !mMeasuredTooHigh;
+        // Otherwise it will be clipped
+        return super.hasOverlappingRendering() && getActualHeight() <= getHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 6d90329..02a39e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -24,6 +24,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.os.Build;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.NotificationHeaderView;
@@ -58,6 +59,8 @@
     private final int mRoundRectRadius;
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
     private final boolean mRoundRectClippingEnabled;
+    private final int mMinContractedHeight;
+
 
     private View mContractedChild;
     private View mExpandedChild;
@@ -80,6 +83,7 @@
     private boolean mIsChildInGroup;
     private int mSmallHeight;
     private int mHeadsUpHeight;
+    private int mNotificationMaxHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
@@ -102,6 +106,8 @@
         }
     };
     private OnClickListener mExpandClickListener;
+    private boolean mBeforeN;
+    private boolean mExpandable;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -111,13 +117,16 @@
                 R.dimen.notification_material_rounded_rect_radius);
         mRoundRectClippingEnabled = getResources().getBoolean(
                 R.bool.config_notifications_round_rect_clipping);
+        mMinContractedHeight = getResources().getDimensionPixelSize(
+                R.dimen.min_notification_layout_height);
         reset(true);
         setOutlineProvider(mOutlineProvider);
     }
 
-    public void setHeights(int smallHeight, int headsUpMaxHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
         mSmallHeight = smallHeight;
         mHeadsUpHeight = headsUpMaxHeight;
+        mNotificationMaxHeight = maxHeight;
     }
 
     @Override
@@ -131,13 +140,23 @@
         }
         int maxChildHeight = 0;
         if (mContractedChild != null) {
-            int size = Math.min(maxSize, mSmallHeight);
-            mContractedChild.measure(widthMeasureSpec,
-                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
-            maxChildHeight = Math.max(maxChildHeight, mContractedChild.getMeasuredHeight());
+            int heightSpec;
+            if (shouldContractedBeFixedSize()) {
+                int size = Math.min(maxSize, mSmallHeight);
+                heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+            } else {
+                heightSpec = MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST);
+            }
+            mContractedChild.measure(widthMeasureSpec, heightSpec);
+            int measuredHeight = mContractedChild.getMeasuredHeight();
+            if (measuredHeight < mMinContractedHeight) {
+                heightSpec = MeasureSpec.makeMeasureSpec(mMinContractedHeight, MeasureSpec.EXACTLY);
+                mContractedChild.measure(widthMeasureSpec, heightSpec);
+            }
+            maxChildHeight = Math.max(maxChildHeight, measuredHeight);
         }
         if (mExpandedChild != null) {
-            int size = maxSize;
+            int size = Math.min(maxSize, mNotificationMaxHeight);
             ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
             if (layoutParams.height >= 0) {
                 // An actual height is set
@@ -170,6 +189,10 @@
         setMeasuredDimension(width, ownHeight);
     }
 
+    private boolean shouldContractedBeFixedSize() {
+        return mBeforeN && mContractedWrapper instanceof NotificationCustomViewWrapper;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -292,14 +315,14 @@
         } else if (mIsHeadsUp && mHeadsUpChild != null) {
             return mHeadsUpChild.getHeight();
         }
-        return mSmallHeight;
+        return mContractedChild.getHeight();
     }
 
     public int getMinHeight() {
         if (mIsChildInGroup && !isGroupExpanded()) {
             return mSingleLineView.getHeight();
         } else {
-            return mSmallHeight;
+            return mContractedChild.getHeight();
         }
     }
 
@@ -435,6 +458,9 @@
         if (!noExpandedChild && mContentHeight == mExpandedChild.getHeight()) {
             return VISIBLE_TYPE_EXPANDED;
         }
+        if (mIsChildInGroup && !isGroupExpanded()) {
+            return VISIBLE_TYPE_SINGLELINE;
+        }
 
         if (mIsHeadsUp && mHeadsUpChild != null) {
             if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
@@ -443,9 +469,7 @@
                 return VISIBLE_TYPE_EXPANDED;
             }
         } else {
-            if (mIsChildInGroup && !isGroupExpanded()) {
-                return VISIBLE_TYPE_SINGLELINE;
-            } else if (mContentHeight <= mSmallHeight || noExpandedChild) {
+            if (mContentHeight <= mContractedChild.getHeight() || noExpandedChild) {
                 return VISIBLE_TYPE_CONTRACTED;
             } else {
                 return VISIBLE_TYPE_EXPANDED;
@@ -469,6 +493,7 @@
     public void setHeadsUp(boolean headsUp) {
         mIsHeadsUp = headsUp;
         selectLayout(false /* animate */, true /* force */);
+        updateExpandButtons(mExpandable);
     }
 
     @Override
@@ -490,6 +515,7 @@
 
     public void onNotificationUpdated(NotificationData.Entry entry) {
         mStatusBarNotification = entry.notification;
+        mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateSingleLineView();
         applyRemoteInput(entry);
         selectLayout(false /* animate */, true /* force */);
@@ -587,6 +613,13 @@
     }
 
     public void updateExpandButtons(boolean expandable) {
+        mExpandable = expandable;
+        // if the expanded child has the same height as the collapsed one we hide it.
+        if (mExpandedChild != null && mExpandedChild.getHeight() != 0 &&
+                ((mIsHeadsUp && mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) ||
+                (!mIsHeadsUp && mExpandedChild.getHeight() == mContractedChild.getHeight()))) {
+            expandable = false;
+        }
         if (mExpandedChild != null) {
             mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c458d21..f7680a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -115,17 +115,14 @@
                         updatedNotificationBuilder.makeBigContentView();
                 final RemoteViews newHeadsUpContentView =
                         updatedNotificationBuilder.makeHeadsUpContentView();
-                final Notification updatedPublicNotification = updatedNotification.publicVersion;
-                final RemoteViews newPubContentView = (updatedPublicNotification != null)
-                        ? Notification.Builder.recoverBuilder(
-                                ctx, updatedPublicNotification).makeContentView()
-                        : null;
+                final RemoteViews newPublicNotification
+                        = updatedNotificationBuilder.makePublicContentView();
 
                 applyInPlace = compareRemoteViews(cachedContentView, newContentView)
                         && compareRemoteViews(cachedBigContentView, newBigContentView)
                         && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
-                        && compareRemoteViews(cachedPublicContentView, newPubContentView);
-                cachedPublicContentView = newPubContentView;
+                        && compareRemoteViews(cachedPublicContentView, newPublicNotification);
+                cachedPublicContentView = newPublicNotification;
                 cachedHeadsUpContentView = newHeadsUpContentView;
                 cachedBigContentView = newBigContentView;
                 cachedContentView = newContentView;
@@ -136,14 +133,8 @@
                 cachedContentView = builder.makeContentView();
                 cachedBigContentView = builder.makeBigContentView();
                 cachedHeadsUpContentView = builder.makeHeadsUpContentView();
+                cachedPublicContentView = builder.makePublicContentView();
 
-                final Notification publicNotification =
-                        notification.getNotification().publicVersion;
-                if (publicNotification != null) {
-                    final Notification.Builder publicBuilder
-                            = Notification.Builder.recoverBuilder(ctx, publicNotification);
-                    cachedPublicContentView = publicBuilder.makeContentView();
-                }
                 applyInPlace = false;
             }
             return applyInPlace;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 79236be..6850f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -45,6 +45,7 @@
     private Drawable mBackground;
     private int mClipTopAmount;
     private int mActualHeight;
+    private boolean mExposed;
 
     public NotificationGuts(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -224,4 +225,12 @@
         // Prevents this view from creating a layer when alpha is animating.
         return false;
     }
+
+    public void setExposed(boolean exposed) {
+        mExposed = exposed;
+    }
+
+    public boolean areGutsExposed() {
+        return mExposed;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 5cfd174..b38c3fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -378,23 +378,22 @@
             return;
         }
         if (mHasPinnedNotification) {
-            int minX = Integer.MAX_VALUE;
+            int minX = 0;
             int maxX = 0;
-            int minY = Integer.MAX_VALUE;
             int maxY = 0;
             for (HeadsUpEntry entry : mSortedEntries) {
                 ExpandableNotificationRow row = entry.entry.row;
                 if (row.isPinned()) {
                     row.getLocationOnScreen(mTmpTwoArray);
-                    minX = Math.min(minX, mTmpTwoArray[0]);
-                    minY = Math.min(minY, 0);
-                    maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
-                    maxY = Math.max(maxY, row.getHeadsUpHeight());
+                    minX = mTmpTwoArray[0];
+                    maxX = mTmpTwoArray[0] + row.getWidth();
+                    maxY = row.getHeadsUpHeight();
+                    break;
                 }
             }
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, minY, maxX, maxY + mNotificationsTopPadding);
+            info.touchableRegion.set(minX, 0, maxX, maxY + mNotificationsTopPadding);
         } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index beaa3ad..baccd2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -56,6 +56,7 @@
     private ExpandableNotificationRow mNotificationParent;
     private HybridNotificationView mGroupOverflowContainer;
     private ViewState mGroupOverFlowState;
+    private int mRealHeight;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -111,8 +112,8 @@
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
         boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
         boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+        int size = MeasureSpec.getSize(heightMeasureSpec);
         if (hasFixedHeight || isHeightLimited) {
-            int size = MeasureSpec.getSize(heightMeasureSpec);
             ownMaxHeight = Math.min(ownMaxHeight, size);
         }
         int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
@@ -133,9 +134,19 @@
         if (mGroupOverflowContainer != null) {
             mGroupOverflowContainer.measure(widthMeasureSpec, newHeightSpec);
         }
+        mRealHeight = height;
+        if (heightMode != MeasureSpec.UNSPECIFIED) {
+            height = Math.min(height, size);
+        }
         setMeasuredDimension(width, height);
     }
 
+    @Override
+    public boolean pointInView(float localX, float localY, float slop) {
+        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (mRealHeight + slop);
+    }
+
     /**
      * Add a child notification to this view.
      *
@@ -452,6 +463,10 @@
     }
 
     public int getMinHeight() {
+        return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+    }
+
+    public int getMinExpandHeight() {
         return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 2ce19a2..0ed1527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -118,7 +118,7 @@
     /**
      * The algorithm which calculates the properties for our children
      */
-    private StackScrollAlgorithm mStackScrollAlgorithm;
+    private final StackScrollAlgorithm mStackScrollAlgorithm;
 
     /**
      * The current State this Layout is in
@@ -258,6 +258,7 @@
 
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
         mSwipeHelper.setLongPressListener(mLongPressListener);
+        mStackScrollAlgorithm = new StackScrollAlgorithm(context);
         initView(context);
         if (DEBUG) {
             setWillNotDraw(false);
@@ -303,8 +304,7 @@
                 .getDimensionPixelSize(R.dimen.notification_min_height);
         mBottomStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
-        mStackScrollAlgorithm = new StackScrollAlgorithm(context);
-        mStackScrollAlgorithm.setDimmed(mAmbientState.isDimmed());
+        mStackScrollAlgorithm.initView(context);
         mPaddingBetweenElementsDimmed = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
         mPaddingBetweenElementsNormal = context.getResources()
@@ -1338,7 +1338,7 @@
     /**
      * @return the first child which has visibility unequal to GONE
      */
-    private ExpandableView getFirstChildNotGone() {
+    public ExpandableView getFirstChildNotGone() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -2836,7 +2836,6 @@
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
-        mStackScrollAlgorithm.setHeadsUpManager(headsUpManager);
     }
 
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 59b446b..5496963 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -25,7 +25,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -68,19 +67,22 @@
     private int mBottomStackSlowDownLength;
     private int mTopStackSlowDownLength;
     private int mCollapseSecondCardPadding;
-    private boolean mIsSmallScreen;
-    private int mMaxNotificationHeight;
     private boolean mScaleDimmed;
-    private HeadsUpManager mHeadsUpManager;
+    private ExpandableView mFirstChild;
     private int mFirstChildMinHeight;
+    private boolean mDimmed;
 
     public StackScrollAlgorithm(Context context) {
-        initConstants(context);
-        updatePadding(false);
+        initView(context);
     }
 
-    private void updatePadding(boolean dimmed) {
-        mPaddingBetweenElements = dimmed && mScaleDimmed
+    public void initView(Context context) {
+        initConstants(context);
+        updatePadding();
+    }
+
+    private void updatePadding() {
+        mPaddingBetweenElements = mDimmed && mScaleDimmed
                 ? mPaddingBetweenElementsDimmed
                 : mPaddingBetweenElementsNormal;
         mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
@@ -110,8 +112,6 @@
                 .getDimensionPixelSize(R.dimen.notifications_top_padding);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
-        mMaxNotificationHeight = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_max_height);
         mTopStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
         mBottomStackPeekSize = context.getResources()
@@ -149,6 +149,7 @@
         algorithmState.scrolledPixelsTop = 0;
         algorithmState.itemsInBottomStack = 0.0f;
         algorithmState.partialInBottom = 0.0f;
+        mFirstChildMinHeight = mFirstChild == null ? 0 : mFirstChild.getMinHeight();
         float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
 
         int scrollY = ambientState.getScrollY();
@@ -544,7 +545,7 @@
             if (row.isPinned()) {
                 childState.yTranslation = Math.max(childState.yTranslation,
                         mNotificationsTopPadding);
-                childState.height = row.getHeadsUpHeight();
+                childState.height = Math.max(row.getHeadsUpHeight(), childState.height);
                 if (!isTopEntry) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
                     // the top most z-position
@@ -933,10 +934,7 @@
     }
 
     public void notifyChildrenChanged(final NotificationStackScrollLayout hostView) {
-        int firstItemMinHeight = hostView.getFirstItemMinHeight();
-        if (firstItemMinHeight != mFirstChildMinHeight) {
-            mFirstChildMinHeight = firstItemMinHeight;
-        }
+        mFirstChild = hostView.getFirstChildNotGone();
         if (mIsExpansionChanging) {
             hostView.post(new Runnable() {
                 @Override
@@ -948,7 +946,8 @@
     }
 
     public void setDimmed(boolean dimmed) {
-        updatePadding(dimmed);
+        mDimmed = dimmed;
+        updatePadding();
     }
 
     public void onReset(ExpandableView view) {
@@ -957,10 +956,6 @@
         }
     }
 
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
     class StackScrollAlgorithmState {
 
         /**