diff --git a/api/current.txt b/api/current.txt
index 193407e8830f3d5e2e51dcd9bc882804a76d6697..cf365ff99296872c4a09347ccd5aea386b567244 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1049,6 +1049,7 @@ package android {
     field public static final int screenOrientation = 16842782; // 0x101001e
     field public static final int screenSize = 16843466; // 0x10102ca
     field public static final int scrollHorizontally = 16843099; // 0x101015b
+    field public static final int scrollIndicators = 16844023; // 0x10104f7
     field public static final int scrollViewStyle = 16842880; // 0x1010080
     field public static final int scrollX = 16842962; // 0x10100d2
     field public static final int scrollY = 16842963; // 0x10100d3
@@ -36200,6 +36201,7 @@ package android.view {
     method public int getScrollBarFadeDuration();
     method public int getScrollBarSize();
     method public int getScrollBarStyle();
+    method public int getScrollIndicators();
     method public final int getScrollX();
     method public final int getScrollY();
     method public int getSolidColor();
@@ -36281,6 +36283,7 @@ package android.view {
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
     method public boolean isScrollContainer();
+    method public boolean isScrollIndicatorEnabled(int);
     method public boolean isScrollbarFadingEnabled();
     method public boolean isSelected();
     method public boolean isShown();
@@ -36480,6 +36483,8 @@ package android.view {
     method public void setScrollBarSize(int);
     method public void setScrollBarStyle(int);
     method public void setScrollContainer(boolean);
+    method public void setScrollIndicators(int);
+    method public void setScrollIndicators(int, int);
     method public void setScrollX(int);
     method public void setScrollY(int);
     method public void setScrollbarFadingEnabled(boolean);
@@ -36605,6 +36610,12 @@ package android.view {
     field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
     field public static final int SCROLL_AXIS_NONE = 0; // 0x0
     field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
     field protected static final int[] SELECTED_STATE_SET;
     field protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
     field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
diff --git a/api/system-current.txt b/api/system-current.txt
index 2738bdb05081d474ddd116c114cd3296702103ea..e08ffd4514a099d81ef399084f394cb177549b7c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1121,6 +1121,7 @@ package android {
     field public static final int screenOrientation = 16842782; // 0x101001e
     field public static final int screenSize = 16843466; // 0x10102ca
     field public static final int scrollHorizontally = 16843099; // 0x101015b
+    field public static final int scrollIndicators = 16844023; // 0x10104f7
     field public static final int scrollViewStyle = 16842880; // 0x1010080
     field public static final int scrollX = 16842962; // 0x10100d2
     field public static final int scrollY = 16842963; // 0x10100d3
@@ -38411,6 +38412,7 @@ package android.view {
     method public int getScrollBarFadeDuration();
     method public int getScrollBarSize();
     method public int getScrollBarStyle();
+    method public int getScrollIndicators();
     method public final int getScrollX();
     method public final int getScrollY();
     method public int getSolidColor();
@@ -38492,6 +38494,7 @@ package android.view {
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
     method public boolean isScrollContainer();
+    method public boolean isScrollIndicatorEnabled(int);
     method public boolean isScrollbarFadingEnabled();
     method public boolean isSelected();
     method public boolean isShown();
@@ -38691,6 +38694,8 @@ package android.view {
     method public void setScrollBarSize(int);
     method public void setScrollBarStyle(int);
     method public void setScrollContainer(boolean);
+    method public void setScrollIndicators(int);
+    method public void setScrollIndicators(int, int);
     method public void setScrollX(int);
     method public void setScrollY(int);
     method public void setScrollbarFadingEnabled(boolean);
@@ -38816,6 +38821,12 @@ package android.view {
     field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
     field public static final int SCROLL_AXIS_NONE = 0; // 0x0
     field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
     field protected static final int[] SELECTED_STATE_SET;
     field protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
     field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 75dc0a2d150969296e30e6c3d13c5438e8a720c0..f62e6a2f1449cbf9173a0089fea354479e2c7351 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2395,10 +2395,143 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      */
     static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
 
+    /**
+     * Flag indicating that the bottom scroll indicator should be displayed
+     * when this view can scroll up.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_TOP = 0x0100;
+
+    /**
+     * Flag indicating that the bottom scroll indicator should be displayed
+     * when this view can scroll down.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_BOTTOM = 0x0200;
+
+    /**
+     * Flag indicating that the left scroll indicator should be displayed
+     * when this view can scroll left.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_LEFT = 0x0400;
+
+    /**
+     * Flag indicating that the right scroll indicator should be displayed
+     * when this view can scroll right.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_RIGHT = 0x0800;
+
+    /**
+     * Flag indicating that the start scroll indicator should be displayed
+     * when this view can scroll in the start direction.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_START = 0x1000;
+
+    /**
+     * Flag indicating that the end scroll indicator should be displayed
+     * when this view can scroll in the end direction.
+     */
+    static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
+
     /* End of masks for mPrivateFlags3 */
 
     static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
 
+    static final int SCROLL_INDICATORS_NONE = 0x0000;
+
+    /**
+     * Mask for use with setFlags indicating bits used for indicating which
+     * scroll indicators are enabled.
+     */
+    static final int SCROLL_INDICATORS_PFLAG3_MASK = PFLAG3_SCROLL_INDICATOR_TOP
+            | PFLAG3_SCROLL_INDICATOR_BOTTOM | PFLAG3_SCROLL_INDICATOR_LEFT
+            | PFLAG3_SCROLL_INDICATOR_RIGHT | PFLAG3_SCROLL_INDICATOR_START
+            | PFLAG3_SCROLL_INDICATOR_END;
+
+    /**
+     * Left-shift required to translate between public scroll indicator flags
+     * and internal PFLAGS3 flags. When used as a right-shift, translates
+     * PFLAGS3 flags to public flags.
+     */
+    static final int SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT = 8;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            value = {
+                    SCROLL_INDICATOR_TOP,
+                    SCROLL_INDICATOR_BOTTOM,
+                    SCROLL_INDICATOR_LEFT,
+                    SCROLL_INDICATOR_RIGHT,
+                    SCROLL_INDICATOR_START,
+                    SCROLL_INDICATOR_END,
+            })
+    public @interface ScrollIndicators {}
+
+    /**
+     * Scroll indicator direction for the top edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_TOP =
+            PFLAG3_SCROLL_INDICATOR_TOP >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+    /**
+     * Scroll indicator direction for the bottom edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_BOTTOM =
+            PFLAG3_SCROLL_INDICATOR_BOTTOM >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+    /**
+     * Scroll indicator direction for the left edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_LEFT =
+            PFLAG3_SCROLL_INDICATOR_LEFT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+    /**
+     * Scroll indicator direction for the right edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_RIGHT =
+            PFLAG3_SCROLL_INDICATOR_RIGHT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+    /**
+     * Scroll indicator direction for the starting edge of the view.
+     * <p>
+     * Resolved according to the view's layout direction, see
+     * {@link #getLayoutDirection()} for more information.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_START =
+            PFLAG3_SCROLL_INDICATOR_START >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+    /**
+     * Scroll indicator direction for the ending edge of the view.
+     * <p>
+     * Resolved according to the view's layout direction, see
+     * {@link #getLayoutDirection()} for more information.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_END =
+            PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
     /**
      * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse
      * into this view.<p>
@@ -3217,6 +3350,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
     private ForegroundInfo mForegroundInfo;
 
+    private Drawable mScrollIndicatorDrawable;
+
     /**
      * RenderNode used for backgrounds.
      * <p>
@@ -3769,6 +3904,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
         int overScrollMode = mOverScrollMode;
         boolean initializeScrollbars = false;
+        boolean initializeScrollIndicators = false;
 
         boolean startPaddingDefined = false;
         boolean endPaddingDefined = false;
@@ -4135,6 +4271,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                     }
                     mForegroundInfo.mInsidePadding = a.getBoolean(attr,
                             mForegroundInfo.mInsidePadding);
+                case R.styleable.View_scrollIndicators:
+                    final int scrollIndicators =
+                            a.getInt(attr, SCROLL_INDICATORS_NONE) & SCROLL_INDICATORS_PFLAG3_MASK;
+                    if (scrollIndicators != 0) {
+                        viewFlagValues |= scrollIndicators;
+                        viewFlagMasks |= SCROLL_INDICATORS_PFLAG3_MASK;
+                        initializeScrollIndicators = true;
+                    }
                     break;
             }
         }
@@ -4211,6 +4355,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
             initializeScrollbarsInternal(a);
         }
 
+        if (initializeScrollIndicators) {
+            initializeScrollIndicatorsInternal();
+        }
+
         a.recycle();
 
         // Needs to be called after mViewFlags is set
@@ -4682,6 +4830,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         resolvePadding();
     }
 
+    private void initializeScrollIndicatorsInternal() {
+        // Some day maybe we'll break this into top/left/start/etc. and let the
+        // client control it. Until then, you can have any scroll indicator you
+        // want as long as it's a 1dp foreground-colored rectangle.
+        if (mScrollIndicatorDrawable == null) {
+            mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
+        }
+    }
+
     /**
      * <p>
      * Initalizes the scrollability cache if necessary.
@@ -4721,6 +4878,118 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         return mVerticalScrollbarPosition;
     }
 
+    /**
+     * Sets the state of all scroll indicators.
+     * <p>
+     * See {@link #setScrollIndicators(int, int)} for usage information.
+     *
+     * @param indicators a bitmask of indicators that should be enabled, or
+     *                   {@code 0} to disable all indicators
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     * @attr ref android.R.styleable#View_scrollIndicators
+     */
+    public void setScrollIndicators(@ScrollIndicators int indicators) {
+        setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK);
+    }
+
+    /**
+     * Sets the state of the scroll indicators specified by the mask. To change
+     * all scroll indicators at once, see {@link #setScrollIndicators(int)}.
+     * <p>
+     * When a scroll indicator is enabled, it will be displayed if the view
+     * can scroll in the direction of the indicator.
+     * <p>
+     * Multiple indicator types may be enabled or disabled by passing the
+     * logical OR of the desired types. If multiple types are specified, they
+     * will all be set to the same enabled state.
+     * <p>
+     * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators
+     *
+     * @param indicators the indicator direction, or the logical OR of multiple
+     *             indicator directions. One or more of:
+     *             <ul>
+     *               <li>{@link #SCROLL_INDICATOR_TOP}</li>
+     *               <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
+     *               <li>{@link #SCROLL_INDICATOR_LEFT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_START}</li>
+     *               <li>{@link #SCROLL_INDICATOR_END}</li>
+     *             </ul>
+     * @see #setScrollIndicators(int)
+     * @see #getScrollIndicators()
+     * @attr ref android.R.styleable#View_scrollIndicators
+     */
+    public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask) {
+        // Shift and sanitize mask.
+        mask <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+        mask &= SCROLL_INDICATORS_PFLAG3_MASK;
+
+        // Shift and mask indicators.
+        indicators <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+        indicators &= mask;
+
+        // Merge with non-masked flags.
+        final int updatedFlags = indicators | (mPrivateFlags3 & ~mask);
+
+        if (mPrivateFlags3 != updatedFlags) {
+            mPrivateFlags3 = updatedFlags;
+
+            if (indicators != 0) {
+                initializeScrollIndicatorsInternal();
+            }
+            invalidate();
+        }
+    }
+
+    /**
+     * Returns a bitmask representing the enabled scroll indicators.
+     * <p>
+     * For example, if the top and left scroll indicators are enabled and all
+     * other indicators are disabled, the return value will be
+     * {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
+     * <p>
+     * To check whether the bottom scroll indicator is enabled, use the value
+     * of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
+     *
+     * @return a bitmask representing the enabled scroll indicators
+     */
+    @ScrollIndicators
+    public int getScrollIndicators() {
+        return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)
+                >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+    }
+
+    /**
+     * Returns whether the specified scroll indicator is enabled.
+     * <p>
+     * Multiple indicator types may be queried by passing the logical OR of the
+     * desired types. If multiple types are specified, the return value
+     * represents whether they are all enabled.
+     *
+     * @param direction the indicator direction, or the logical OR of multiple
+     *             indicator directions. One or more of:
+     *             <ul>
+     *               <li>{@link #SCROLL_INDICATOR_TOP}</li>
+     *               <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
+     *               <li>{@link #SCROLL_INDICATOR_LEFT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_START}</li>
+     *               <li>{@link #SCROLL_INDICATOR_END}</li>
+     *             </ul>
+     * @return {@code true} if the specified indicator(s) are enabled,
+     *         {@code false} otherwise
+     * @attr ref android.R.styleable#View_scrollIndicators
+     */
+    public boolean isScrollIndicatorEnabled(int direction) {
+        // Shift and sanitize input.
+        direction <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+        direction &= SCROLL_INDICATORS_PFLAG3_MASK;
+
+        // All of the flags must be set.
+        return (mPrivateFlags3 & direction) == direction;
+    }
+
     ListenerInfo getListenerInfo() {
         if (mListenerInfo != null) {
             return mListenerInfo;
@@ -13444,6 +13713,75 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         }
     }
 
+    void getScrollIndicatorBounds(@NonNull Rect out) {
+        out.left = mScrollX;
+        out.right = mScrollX + mRight - mLeft;
+        out.top = mScrollY;
+        out.bottom = mScrollY + mBottom - mTop;
+    }
+
+    private void onDrawScrollIndicators(Canvas c) {
+        if ((mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK) == 0) {
+            // No scroll indicators enabled.
+            return;
+        }
+
+        final Drawable dr = mScrollIndicatorDrawable;
+        if (dr == null) {
+            // Scroll indicators aren't supported here.
+            return;
+        }
+
+        final int h = dr.getIntrinsicHeight();
+        final int w = dr.getIntrinsicWidth();
+        final Rect rect = mAttachInfo.mTmpInvalRect;
+        getScrollIndicatorBounds(rect);
+
+        if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_TOP) != 0) {
+            final boolean canScrollUp = canScrollVertically(-1);
+            if (canScrollUp) {
+                dr.setBounds(rect.left, rect.top, rect.right, rect.top + h);
+                dr.draw(c);
+            }
+        }
+
+        if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_BOTTOM) != 0) {
+            final boolean canScrollDown = canScrollVertically(1);
+            if (canScrollDown) {
+                dr.setBounds(rect.left, rect.bottom - h, rect.right, rect.bottom);
+                dr.draw(c);
+            }
+        }
+
+        final int leftRtl;
+        final int rightRtl;
+        if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+            leftRtl = PFLAG3_SCROLL_INDICATOR_END;
+            rightRtl = PFLAG3_SCROLL_INDICATOR_START;
+        } else {
+            leftRtl = PFLAG3_SCROLL_INDICATOR_START;
+            rightRtl = PFLAG3_SCROLL_INDICATOR_END;
+        }
+
+        final int leftMask = PFLAG3_SCROLL_INDICATOR_LEFT | leftRtl;
+        if ((mPrivateFlags3 & leftMask) != 0) {
+            final boolean canScrollLeft = canScrollHorizontally(-1);
+            if (canScrollLeft) {
+                dr.setBounds(rect.left, rect.top, rect.left + w, rect.bottom);
+                dr.draw(c);
+            }
+        }
+
+        final int rightMask = PFLAG3_SCROLL_INDICATOR_RIGHT | rightRtl;
+        if ((mPrivateFlags3 & rightMask) != 0) {
+            final boolean canScrollRight = canScrollHorizontally(1);
+            if (canScrollRight) {
+                dr.setBounds(rect.right - w, rect.top, rect.right, rect.bottom);
+                dr.draw(c);
+            }
+        }
+    }
+
     /**
      * <p>Request the drawing of the horizontal and the vertical scrollbar. The
      * scrollbars are painted only if they have been awakened first.</p>
@@ -17272,6 +17610,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      * @param canvas canvas to draw into
      */
     public void onDrawForeground(Canvas canvas) {
+        onDrawScrollIndicators(canvas);
         onDrawScrollBars(canvas);
 
         final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f240fd6ad2a42691fdb64bc25ea40908dea28e83..babb4e9066be6fdf021bf3b0183c3073b1b22441 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -18,6 +18,7 @@ package android.view;
 
 import android.animation.LayoutTransition;
 import android.annotation.IdRes;
+import android.annotation.NonNull;
 import android.annotation.UiThread;
 import android.content.Context;
 import android.content.Intent;
@@ -3547,6 +3548,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
         return child.draw(canvas, this, drawingTime);
     }
 
+    @Override
+    void getScrollIndicatorBounds(@NonNull Rect out) {
+        super.getScrollIndicatorBounds(out);
+
+        // If we have padding and we're supposed to clip children to that
+        // padding, offset the scroll indicators to match our clip bounds.
+        final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
+        if (clipToPadding) {
+            out.left += mPaddingLeft;
+            out.right -= mPaddingRight;
+            out.top += mPaddingTop;
+            out.bottom -= mPaddingBottom;
+        }
+    }
+
     /**
      * Returns whether this group's children are clipped to their bounds before drawing.
      * The default value is true.
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index b8110e3a3ac85b3b11ba7f20551a46f98b4b719b..61ee00c55fe0344675a42d8385dd9d16a32cc3c6 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -526,11 +526,15 @@ public class AlertController {
             mWindow.setCloseOnTouchOutsideIfNotSet(true);
         }
 
-        // Only display the divider if we have a title and a custom view or a
-        // message.
         if (hasTopPanel) {
+            // Only clip scrolling content to padding if we have a title.
+            if (mScrollView != null) {
+                mScrollView.setClipToPadding(true);
+            }
+
+            // Only show the divider if we have a title.
             final View divider;
-            if (mMessage != null || hasCustomPanel || mListView != null) {
+            if (mMessage != null || mListView != null || hasCustomPanel) {
                 divider = topPanel.findViewById(R.id.titleDivider);
             } else {
                 divider = topPanel.findViewById(R.id.titleDividerTop);
@@ -541,6 +545,17 @@ public class AlertController {
             }
         }
 
+        // Update scroll indicators as needed.
+        if (!hasCustomPanel) {
+            final View content = mListView != null ? mListView : mScrollView;
+            if (content != null) {
+                final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
+                        | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
+                content.setScrollIndicators(indicators,
+                        View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
+            }
+        }
+
         final TypedArray a = mContext.obtainStyledAttributes(
                 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
         setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
@@ -654,59 +669,6 @@ public class AlertController {
                 contentPanel.setVisibility(View.GONE);
             }
         }
-
-        // Set up scroll indicators (if present).
-        final View indicatorUp = contentPanel.findViewById(R.id.scrollIndicatorUp);
-        final View indicatorDown = contentPanel.findViewById(R.id.scrollIndicatorDown);
-        if (indicatorUp != null || indicatorDown != null) {
-            if (mMessage != null) {
-                // We're just showing the ScrollView, set up listener.
-                mScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
-                        @Override
-                        public void onScrollChange(View v, int scrollX, int scrollY,
-                                int oldScrollX, int oldScrollY) {
-                            manageScrollIndicators(v, indicatorUp, indicatorDown);
-                        }
-                    });
-                // Set up the indicators following layout.
-                mScrollView.post(new Runnable() {
-                     @Override
-                     public void run() {
-                             manageScrollIndicators(mScrollView, indicatorUp, indicatorDown);
-                         }
-                     });
-
-            } else if (mListView != null) {
-                // We're just showing the AbsListView, set up listener.
-                mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
-                        @Override
-                        public void onScrollStateChanged(AbsListView view, int scrollState) {
-                            // That's cool, I guess?
-                        }
-
-                        @Override
-                        public void onScroll(AbsListView v, int firstVisibleItem,
-                                int visibleItemCount, int totalItemCount) {
-                            manageScrollIndicators(v, indicatorUp, indicatorDown);
-                        }
-                    });
-                // Set up the indicators following layout.
-                mListView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            manageScrollIndicators(mListView, indicatorUp, indicatorDown);
-                        }
-                    });
-            } else {
-                // We don't have any content to scroll, remove the indicators.
-                if (indicatorUp != null) {
-                    contentPanel.removeView(indicatorUp);
-                }
-                if (indicatorDown != null) {
-                    contentPanel.removeView(indicatorDown);
-                }
-            }
-        }
     }
 
     private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
index 64e6c69e3b1294b72e3591640f634e067e80faaf..f58ab03bc7ddad117308f4dbf2c13d6e1a899149 100644
--- a/core/java/com/android/internal/widget/ButtonBarLayout.java
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -17,6 +17,7 @@
 package com.android.internal.widget;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -29,37 +30,39 @@ import com.android.internal.R;
  * orientation when it can't fit its child views horizontally.
  */
 public class ButtonBarLayout extends LinearLayout {
-    /** Spacer used in horizontal orientation. */
-    private final View mSpacer;
-
     /** Whether the current configuration allows stacking. */
-    private final boolean mAllowStacked;
+    private final boolean mAllowStacking;
 
     /** Whether the layout is currently stacked. */
     private boolean mStacked;
 
+    private int mLastWidthSize = -1;
+
     public ButtonBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mAllowStacked = context.getResources().getBoolean(R.bool.allow_stacked_button_bar);
-        mSpacer = findViewById(R.id.spacer);
+        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
+        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+        ta.recycle();
+
+        mStacked = getOrientation() == VERTICAL;
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mAllowStacking) {
+            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+            if (widthSize > mLastWidthSize && mStacked) {
+                // We're being measured wider this time, try un-stacking.
+                setStacked(false);
+            }
 
-        // Maybe we can fit the content now?
-        if (w > oldw && mStacked) {
-            setStacked(false);
+            mLastWidthSize = widthSize;
         }
-    }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mAllowStacked && getOrientation() == LinearLayout.HORIZONTAL) {
+        if (mAllowStacking && !mStacked) {
             final int measuredWidth = getMeasuredWidthAndState();
             final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
             if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
@@ -75,8 +78,9 @@ public class ButtonBarLayout extends LinearLayout {
         setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
         setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
 
-        if (mSpacer != null) {
-            mSpacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
+        final View spacer = findViewById(R.id.spacer);
+        if (spacer != null) {
+            spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
         }
 
         // Reverse the child order. This is specific to the Material button
diff --git a/core/res/res/drawable/scroll_indicator_material.xml b/core/res/res/drawable/scroll_indicator_material.xml
new file mode 100644
index 0000000000000000000000000000000000000000..63cd584ef2b78d4e1370a6adbd1ad1a8d3924434
--- /dev/null
+++ b/core/res/res/drawable/scroll_indicator_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:tint="?attr/colorForeground">
+    <solid android:color="#1f000000" />
+    <size
+        android:height="1dp"
+        android:width="1dp" />
+</shape>
diff --git a/core/res/res/layout/alert_dialog_button_bar_material.xml b/core/res/res/layout/alert_dialog_button_bar_material.xml
index 1eea4e124af31ca41c16319ebc0b5c5b48973c5b..6e102f35b2b8b4b48e2eee0811be26e8abd1f233 100644
--- a/core/res/res/layout/alert_dialog_button_bar_material.xml
+++ b/core/res/res/layout/alert_dialog_button_bar_material.xml
@@ -27,6 +27,7 @@
     android:paddingTop="4dp"
     android:paddingBottom="4dp"
     android:gravity="bottom"
+    android:allowStacking="@bool/allow_stacked_button_bar"
     style="?attr/buttonBarStyle">
 
     <Button
@@ -53,4 +54,4 @@
         style="?attr/buttonBarPositiveButtonStyle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
-</com.android.internal.widget.ButtonBarLayout>
\ No newline at end of file
+</com.android.internal.widget.ButtonBarLayout>
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index bf1e383ed72f9ee6098c26c46535a1d39329f938..95c24599a73847a9597881c7fd633f3795a87b4f 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -24,52 +24,51 @@
 
     <include layout="@layout/alert_dialog_title_material" />
 
-    <FrameLayout android:id="@+id/contentPanel"
+    <FrameLayout
+        android:id="@+id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:minHeight="48dp">
-        <View android:id="@+id/scrollIndicatorUp"
-            android:visibility="gone"
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_gravity="top"
-            android:background="@drawable/list_divider_material"/>
-        <ScrollView android:id="@+id/scrollView"
+
+        <ScrollView
+            android:id="@+id/scrollView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:paddingTop="@dimen/dialog_padding_top_material"
             android:clipToPadding="false">
+
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
-                <TextView android:id="@+id/message"
-                          style="@style/TextAppearance.Material.Subhead"
-                          android:layout_width="match_parent"
-                          android:layout_height="wrap_content"
-                          android:paddingStart="?attr/dialogPreferredPadding"
-                          android:paddingTop="@dimen/dialog_padding_top_material"
-                          android:paddingEnd="?attr/dialogPreferredPadding" />
-                <Space android:id="@+id/textSpacerNoButtons"
-                       android:visibility="gone"
-                       android:layout_width="0dp"
-                       android:layout_height="@dimen/dialog_padding_top_material" />
+
+                <TextView
+                    android:id="@+id/message"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingEnd="?attr/dialogPreferredPadding"
+                    android:paddingStart="?attr/dialogPreferredPadding"
+                    style="@style/TextAppearance.Material.Subhead" />
+
+                <Space
+                    android:id="@+id/textSpacerNoButtons"
+                    android:visibility="gone"
+                    android:layout_width="0dp"
+                    android:layout_height="@dimen/dialog_padding_top_material" />
             </LinearLayout>
         </ScrollView>
-        <View android:id="@+id/scrollIndicatorDown"
-            android:visibility="gone"
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_gravity="bottom"
-            android:background="@drawable/list_divider_material"/>
     </FrameLayout>
 
-    <FrameLayout android:id="@+id/customPanel"
+    <FrameLayout
+        android:id="@+id/customPanel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:minHeight="48dp">
-        <FrameLayout android:id="@+id/custom"
+
+        <FrameLayout
+            android:id="@+id/custom"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
     </FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 29148178268490910a233b25407b31c0eff6c80e..1c4b5f7ec7c8b890d3c0140f12af1d24384905d8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1998,6 +1998,13 @@
         <attr name="needsDefaultBackgrounds" format="boolean" />
     </declare-styleable>
 
+    <!-- @hide -->
+    <declare-styleable name="ButtonBarLayout">
+        <!-- Whether to automatically stack the buttons when there is not
+             enough space to lay them out side-by-side. -->
+        <attr name="allowStacking" format="boolean" />
+    </declare-styleable>
+
     <!-- Fragment animation class attributes. -->
     <declare-styleable name="FragmentAnimation">
         <attr name="fragmentOpenEnterAnimation" format="reference" />
@@ -2715,6 +2722,28 @@
             <enum name="add" value="16" />
         </attr>
 
+        <!-- Defines which scroll indicators should be displayed when the view
+             can be scrolled. Multiple values may be combined using logical OR,
+             for example "top|bottom". -->
+        <attr name="scrollIndicators">
+            <!-- No scroll indicators are displayed. -->
+            <flag name="none" value="0x0000" />
+            <!-- Displays top scroll indicator when view can be scrolled up. -->
+            <flag name="top" value="0x0100" />
+            <!-- Displays bottom scroll indicator when vew can be scrolled down. -->
+            <flag name="bottom" value="0x0200" />
+            <!-- Displays left scroll indicator when vew can be scrolled left. -->
+            <flag name="left" value="0x0400" />
+            <!-- Displays right scroll indicator when vew can be scrolled right. -->
+            <flag name="right" value="0x0800" />
+            <!-- Displays right scroll indicator when vew can be scrolled in the
+                 start direction. -->
+            <flag name="start" value="0x1000" />
+            <!-- Displays right scroll indicator when vew can be scrolled in the
+                 end direction. -->
+            <flag name="end" value="0x2000" />
+        </attr>
+
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 83ac6c1c038ba1bfb8391f08f3b5b7f94687b9b2..297b30272d7ead71291e1546dbefe4c5ce5348e4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2623,7 +2623,6 @@
   <public type="attr" name="fullBackupContent" />
 
   <public type="style" name="Widget.Material.Button.Colored" />
-
   <public type="style" name="Theme.Material.DayNight" />
   <public type="style" name="Theme.Material.DayNight.DarkActionBar" />
   <public type="style" name="Theme.Material.DayNight.Dialog" />
@@ -2686,4 +2685,5 @@
   <public type="attr" name="assistBlocked" />
   <public type="attr" name="stylusButtonPressable" />
   <public type="attr" name="supportsLaunchVoiceAssistFromKeyguard" />
+  <public type="attr" name="scrollIndicators" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a57b3b80e3473eff546a750d668076ebf6ec03b6..28ffbfa183529422ee679765d3673c53c645879e 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2180,8 +2180,6 @@
   <java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
 
   <java-symbol type="layout" name="simple_account_item" />
-  <java-symbol type="id" name="scrollIndicatorUp" />
-  <java-symbol type="id" name="scrollIndicatorDown" />
   <java-symbol type="array" name="config_sms_convert_destination_number_support" />
   <java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
   <java-symbol type="id" name="profile_button" />
@@ -2204,7 +2202,6 @@
   <java-symbol type="string" name="usb_midi_peripheral_manufacturer_name" />
   <java-symbol type="string" name="usb_midi_peripheral_product_name" />
 
-  <java-symbol type="bool" name="allow_stacked_button_bar" />
   <java-symbol type="id" name="spacer" />
 
   <java-symbol type="xml" name="bookmarks" />
@@ -2258,4 +2255,5 @@
   <java-symbol type="id" name="title_icon" />
   <java-symbol type="id" name="day_picker_view_pager" />
   <java-symbol type="layout" name="day_picker_content_material" />
+  <java-symbol type="drawable" name="scroll_indicator_material" />
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index e679e0a7fd830bbc7d4e92046a579534367b5c4f..f02fed1504605cf90cb21ea966c3abe754051a3c 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -207,8 +207,8 @@ please see themes_device_defaults.xml.
 
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
-        <item name="scrollbarDefaultDelayBeforeFade">300</item>
-        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarDefaultDelayBeforeFade">400</item>
+        <item name="scrollbarSize">10dp</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarTrackHorizontal">@null</item>
@@ -563,8 +563,8 @@ please see themes_device_defaults.xml.
 
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
-        <item name="scrollbarDefaultDelayBeforeFade">300</item>
-        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarDefaultDelayBeforeFade">400</item>
+        <item name="scrollbarSize">10dp</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarTrackHorizontal">@null</item>