Merge "Another ViewCompat cleanup after minSdk 14 bump."
diff --git a/api/current.txt b/api/current.txt
index 1cb1ecf..63c69c9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6546,6 +6546,8 @@
   public final class MediaControllerCompat {
     ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat);
     ctor public MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
     method public void adjustVolume(int, int);
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public android.os.Bundle getExtras();
@@ -6566,6 +6568,8 @@
     method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
     method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void removeQueueItemAt(int);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat);
     method public void setVolumeTo(int, int);
@@ -6652,11 +6656,14 @@
     method public void setSessionActivity(android.app.PendingIntent);
     method public void setShuffleModeEnabled(boolean);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSessionCompat.Callback {
     ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat, int);
     method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void onCustomAction(java.lang.String, android.os.Bundle);
     method public void onFastForward();
@@ -6670,6 +6677,8 @@
     method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat);
+    method public void onRemoveQueueItemAt(int);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.support.v4.media.RatingCompat);
@@ -7782,9 +7791,9 @@
     method public static int getLayoutMode(android.view.ViewGroup);
     method public static int getNestedScrollAxes(android.view.ViewGroup);
     method public static boolean isTransitionGroup(android.view.ViewGroup);
-    method public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static deprecated boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
     method public static void setLayoutMode(android.view.ViewGroup, int);
-    method public static void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
+    method public static deprecated void setMotionEventSplittingEnabled(android.view.ViewGroup, boolean);
     method public static void setTransitionGroup(android.view.ViewGroup, boolean);
     field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
     field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
@@ -7873,7 +7882,7 @@
     method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
     method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
     method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
-    method public static boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public static deprecated boolean requestSendAccessibilityEvent(android.view.ViewParent, android.view.View, android.view.accessibility.AccessibilityEvent);
   }
 
   public final class ViewPropertyAnimatorCompat {
@@ -8518,15 +8527,16 @@
   }
 
   public final class EdgeEffectCompat {
-    ctor public EdgeEffectCompat(android.content.Context);
-    method public boolean draw(android.graphics.Canvas);
-    method public void finish();
-    method public boolean isFinished();
-    method public boolean onAbsorb(int);
+    ctor public deprecated EdgeEffectCompat(android.content.Context);
+    method public deprecated boolean draw(android.graphics.Canvas);
+    method public deprecated void finish();
+    method public deprecated boolean isFinished();
+    method public deprecated boolean onAbsorb(int);
     method public deprecated boolean onPull(float);
-    method public boolean onPull(float, float);
-    method public boolean onRelease();
-    method public void setSize(int, int);
+    method public deprecated boolean onPull(float, float);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method public deprecated boolean onRelease();
+    method public deprecated void setSize(int, int);
   }
 
   public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
diff --git a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
deleted file mode 100644
index 4a14377..0000000
--- a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(21)
-class ViewGroupCompatLollipop {
-
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        group.setTransitionGroup(isTransitionGroup);
-    }
-
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return group.isTransitionGroup();
-    }
-
-    public static int getNestedScrollAxes(ViewGroup group) {
-        return group.getNestedScrollAxes();
-    }
-}
diff --git a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
deleted file mode 100644
index f64db1e..0000000
--- a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-
-@RequiresApi(21)
-class ViewParentCompatLollipop {
-    private static final String TAG = "ViewParentCompat";
-
-    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        try {
-            return parent.onStartNestedScroll(child, target, nestedScrollAxes);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onStartNestedScroll", e);
-            return false;
-        }
-    }
-
-    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
-            int nestedScrollAxes) {
-        try {
-            parent.onNestedScrollAccepted(child, target, nestedScrollAxes);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedScrollAccepted", e);
-        }
-    }
-
-    public static void onStopNestedScroll(ViewParent parent, View target) {
-        try {
-            parent.onStopNestedScroll(target);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onStopNestedScroll", e);
-        }
-    }
-
-    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
-            int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
-        try {
-            parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedScroll", e);
-        }
-    }
-
-    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
-            int[] consumed) {
-        try {
-            parent.onNestedPreScroll(target, dx, dy, consumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedPreScroll", e);
-        }
-    }
-
-    public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
-            float velocityY, boolean consumed) {
-        try {
-            return parent.onNestedFling(target, velocityX, velocityY, consumed);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedFling", e);
-            return false;
-        }
-    }
-
-    public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
-            float velocityY) {
-        try {
-            return parent.onNestedPreFling(target, velocityX, velocityY);
-        } catch (AbstractMethodError e) {
-            Log.e(TAG, "ViewParent " + parent + " does not implement interface " +
-                    "method onNestedPreFling", e);
-            return false;
-        }
-    }
-}
diff --git a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
deleted file mode 100644
index 017a6b0..0000000
--- a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-
-package android.support.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.widget.EdgeEffect;
-
-@RequiresApi(21)
-class EdgeEffectCompatLollipop {
-    public static boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-        ((EdgeEffect) edgeEffect).onPull(deltaDistance, displacement);
-        return true;
-    }
-}
diff --git a/compat/api23/android/support/v4/graphics/PaintCompatApi23.java b/compat/api23/android/support/v4/graphics/PaintCompatApi23.java
deleted file mode 100644
index c51f175..0000000
--- a/compat/api23/android/support/v4/graphics/PaintCompatApi23.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.graphics;
-
-import android.graphics.Paint;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(23)
-class PaintCompatApi23 {
-    static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
-        return paint.hasGlyph(string);
-    }
-}
diff --git a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
deleted file mode 100644
index 4e010c6..0000000
--- a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(11)
-class ViewGroupCompatHC {
-    private ViewGroupCompatHC() {
-    }
-
-    public static void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-        group.setMotionEventSplittingEnabled(split);
-    }
-}
diff --git a/compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java b/compat/ics/android/support/v4/graphics/PaintCompatApi14.java
similarity index 97%
rename from compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java
rename to compat/ics/android/support/v4/graphics/PaintCompatApi14.java
index 0d1076f..b459623 100644
--- a/compat/gingerbread/android/support/v4/graphics/PaintCompatGingerbread.java
+++ b/compat/ics/android/support/v4/graphics/PaintCompatApi14.java
@@ -19,11 +19,9 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
 import android.support.v4.util.Pair;
 
-@RequiresApi(9)
-class PaintCompatGingerbread {
+class PaintCompatApi14 {
     // U+DFFFD which is very end of unassigned plane.
     private static final String TOFU_STRING = "\uDB3F\uDFFD";
 
diff --git a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
deleted file mode 100644
index d2a0237..0000000
--- a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * ICS specific ViewGroup API implementation.
- */
-
-@RequiresApi(14)
-class ViewGroupCompatIcs {
-    public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
-            AccessibilityEvent event) {
-        return group.onRequestSendAccessibilityEvent(child, event);
-    }
-}
diff --git a/compat/ics/android/support/v4/view/ViewParentCompatICS.java b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
deleted file mode 100644
index 69fefb5..0000000
--- a/compat/ics/android/support/v4/view/ViewParentCompatICS.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * ICS-specific ViewParent API implementation.
- */
-
-@RequiresApi(14)
-class ViewParentCompatICS {
-    public static boolean requestSendAccessibilityEvent(
-            ViewParent parent, View child, AccessibilityEvent event) {
-        return parent.requestSendAccessibilityEvent(child, event);
-    }
-}
diff --git a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
deleted file mode 100644
index 05e3e80..0000000
--- a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.support.v4.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.support.annotation.RequiresApi;
-import android.widget.EdgeEffect;
-
-/**
- * Stub implementation that contains a real EdgeEffect on ICS.
- * <p/>
- * This class is an implementation detail for EdgeEffectCompat
- * and should not be used directly.
- */
-
-@RequiresApi(14)
-class EdgeEffectCompatIcs {
-    public static Object newEdgeEffect(Context context) {
-        return new EdgeEffect(context);
-    }
-
-    public static void setSize(Object edgeEffect, int width, int height) {
-        ((EdgeEffect) edgeEffect).setSize(width, height);
-    }
-
-    public static boolean isFinished(Object edgeEffect) {
-        return ((EdgeEffect) edgeEffect).isFinished();
-    }
-
-    public static void finish(Object edgeEffect) {
-        ((EdgeEffect) edgeEffect).finish();
-    }
-
-    public static boolean onPull(Object edgeEffect, float deltaDistance) {
-        ((EdgeEffect) edgeEffect).onPull(deltaDistance);
-        return true;
-    }
-
-    public static boolean onRelease(Object edgeEffect) {
-        EdgeEffect eff = (EdgeEffect) edgeEffect;
-        eff.onRelease();
-        return eff.isFinished();
-    }
-
-    public static boolean onAbsorb(Object edgeEffect, int velocity) {
-        ((EdgeEffect) edgeEffect).onAbsorb(velocity);
-        return true;
-    }
-
-    public static boolean draw(Object edgeEffect, Canvas canvas) {
-        return ((EdgeEffect) edgeEffect).draw(canvas);
-    }
-}
\ No newline at end of file
diff --git a/compat/java/android/support/v4/graphics/PaintCompat.java b/compat/java/android/support/v4/graphics/PaintCompat.java
index 66599f7..2bc676c 100644
--- a/compat/java/android/support/v4/graphics/PaintCompat.java
+++ b/compat/java/android/support/v4/graphics/PaintCompat.java
@@ -35,9 +35,9 @@
      */
     public static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
         if (Build.VERSION.SDK_INT >= 23) {
-            return PaintCompatApi23.hasGlyph(paint, string);
+            return paint.hasGlyph(string);
         }
-        return PaintCompatGingerbread.hasGlyph(paint, string);
+        return PaintCompatApi14.hasGlyph(paint, string);
     }
 
     private PaintCompat() {}
diff --git a/compat/gingerbread/android/support/v4/os/BuildCompat.java b/compat/java/android/support/v4/os/BuildCompat.java
similarity index 92%
rename from compat/gingerbread/android/support/v4/os/BuildCompat.java
rename to compat/java/android/support/v4/os/BuildCompat.java
index b8e9d9b..6358b9e 100644
--- a/compat/gingerbread/android/support/v4/os/BuildCompat.java
+++ b/compat/java/android/support/v4/os/BuildCompat.java
@@ -29,7 +29,8 @@
     /* Boilerplate for isAtLeast${PLATFORM}:
      * public static boolean isAtLeast*() {
      *     return !"REL".equals(VERSION.CODENAME)
-     *             && ("${PLATFORM}".equals(VERSION.CODENAME) || VERSION.CODENAME.startsWith("${PLATFORM}MR"));
+     *             && ("${PLATFORM}".equals(VERSION.CODENAME)
+     *                     || VERSION.CODENAME.startsWith("${PLATFORM}MR"));
      * }
      */
 
diff --git a/compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java b/compat/java/android/support/v4/view/LayoutInflaterFactory.java
similarity index 94%
rename from compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java
rename to compat/java/android/support/v4/view/LayoutInflaterFactory.java
index 614f07f..2ee4704 100644
--- a/compat/gingerbread/android/support/v4/view/LayoutInflaterFactory.java
+++ b/compat/java/android/support/v4/view/LayoutInflaterFactory.java
@@ -43,6 +43,6 @@
      * @return View Newly created view. Return null for the default
      *         behavior.
      */
-    public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
+    View onCreateView(View parent, String name, Context context, AttributeSet attrs);
 
 }
diff --git a/compat/gingerbread/android/support/v4/view/TintableBackgroundView.java b/compat/java/android/support/v4/view/TintableBackgroundView.java
similarity index 100%
rename from compat/gingerbread/android/support/v4/view/TintableBackgroundView.java
rename to compat/java/android/support/v4/view/TintableBackgroundView.java
diff --git a/compat/java/android/support/v4/view/ViewGroupCompat.java b/compat/java/android/support/v4/view/ViewGroupCompat.java
index 0fa76cd..39046a3 100644
--- a/compat/java/android/support/v4/view/ViewGroupCompat.java
+++ b/compat/java/android/support/v4/view/ViewGroupCompat.java
@@ -17,6 +17,7 @@
 package android.support.v4.view;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -43,49 +44,22 @@
      */
     public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
 
-    interface ViewGroupCompatImpl {
-        boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
-                AccessibilityEvent event);
-        void setMotionEventSplittingEnabled(ViewGroup group, boolean split);
-        int getLayoutMode(ViewGroup group);
-        void setLayoutMode(ViewGroup group, int mode);
-        void setTransitionGroup(ViewGroup group, boolean isTransitionGroup);
-        boolean isTransitionGroup(ViewGroup group);
-        int getNestedScrollAxes(ViewGroup group);
-    }
-
-    static class ViewGroupCompatStubImpl implements ViewGroupCompatImpl {
-        @Override
-        public boolean onRequestSendAccessibilityEvent(
-                ViewGroup group, View child, AccessibilityEvent event) {
-            return true;
-        }
-
-        @Override
-        public void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-            // no-op, didn't exist.
-        }
-
-        @Override
+    static class ViewGroupCompatBaseImpl {
         public int getLayoutMode(ViewGroup group) {
             return LAYOUT_MODE_CLIP_BOUNDS;
         }
 
-        @Override
         public void setLayoutMode(ViewGroup group, int mode) {
             // no-op, didn't exist. Views only support clip bounds.
         }
 
-        @Override
         public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
         }
 
-        @Override
         public boolean isTransitionGroup(ViewGroup group) {
             return false;
         }
 
-        @Override
         public int getNestedScrollAxes(ViewGroup group) {
             if (group instanceof NestedScrollingParent) {
                 return ((NestedScrollingParent) group).getNestedScrollAxes();
@@ -94,63 +68,46 @@
         }
     }
 
-    static class ViewGroupCompatHCImpl extends ViewGroupCompatStubImpl {
-        @Override
-        public void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-            ViewGroupCompatHC.setMotionEventSplittingEnabled(group, split);
-        }
-    }
-
-    static class ViewGroupCompatIcsImpl extends ViewGroupCompatHCImpl {
-        @Override
-        public boolean onRequestSendAccessibilityEvent(
-                ViewGroup group, View child, AccessibilityEvent event) {
-            return ViewGroupCompatIcs.onRequestSendAccessibilityEvent(group, child, event);
-        }
-    }
-
-    static class ViewGroupCompatJellybeanMR2Impl extends ViewGroupCompatIcsImpl {
+    @RequiresApi(18)
+    static class ViewGroupCompatApi18Impl extends ViewGroupCompatBaseImpl {
         @Override
         public int getLayoutMode(ViewGroup group) {
-            return ViewGroupCompatJellybeanMR2.getLayoutMode(group);
+            return group.getLayoutMode();
         }
 
         @Override
         public void setLayoutMode(ViewGroup group, int mode) {
-            ViewGroupCompatJellybeanMR2.setLayoutMode(group, mode);
+            group.setLayoutMode(mode);
         }
     }
 
-    static class ViewGroupCompatLollipopImpl extends ViewGroupCompatJellybeanMR2Impl {
+    @RequiresApi(21)
+    static class ViewGroupCompatApi21Impl extends ViewGroupCompatApi18Impl {
         @Override
         public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-            ViewGroupCompatLollipop.setTransitionGroup(group, isTransitionGroup);
+            group.setTransitionGroup(isTransitionGroup);
         }
 
         @Override
         public boolean isTransitionGroup(ViewGroup group) {
-            return ViewGroupCompatLollipop.isTransitionGroup(group);
+            return group.isTransitionGroup();
         }
 
         @Override
         public int getNestedScrollAxes(ViewGroup group) {
-            return ViewGroupCompatLollipop.getNestedScrollAxes(group);
+            return group.getNestedScrollAxes();
         }
     }
 
-    static final ViewGroupCompatImpl IMPL;
+    static final ViewGroupCompatBaseImpl IMPL;
     static {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 21) {
-            IMPL = new ViewGroupCompatLollipopImpl();
+            IMPL = new ViewGroupCompatApi21Impl();
         } else if (version >= 18) {
-            IMPL = new ViewGroupCompatJellybeanMR2Impl();
-        } else if (version >= 14) {
-            IMPL = new ViewGroupCompatIcsImpl();
-        } else if (version >= 11) {
-            IMPL = new ViewGroupCompatHCImpl();
+            IMPL = new ViewGroupCompatApi18Impl();
         } else {
-            IMPL = new ViewGroupCompatStubImpl();
+            IMPL = new ViewGroupCompatBaseImpl();
         }
     }
 
@@ -173,10 +130,14 @@
      * @param child The child which requests sending the event.
      * @param event The event to be sent.
      * @return True if the event should be sent.
+     *
+     * @deprecated Use {@link ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * directly.
      */
+    @Deprecated
     public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
             AccessibilityEvent event) {
-        return IMPL.onRequestSendAccessibilityEvent(group, child, event);
+        return group.onRequestSendAccessibilityEvent(child, event);
     }
 
     /**
@@ -194,9 +155,12 @@
      * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
      *              child views. <code>false</code> to only allow one child view to be the target of
      *              any MotionEvent received by this ViewGroup.
+     *
+     * @deprecated Use {@link ViewGroup#setMotionEventSplittingEnabled(boolean)} directly.
      */
+    @Deprecated
     public static void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {
-        IMPL.setMotionEventSplittingEnabled(group, split);
+        group.setMotionEventSplittingEnabled(split);
     }
 
     /**
diff --git a/compat/java/android/support/v4/view/ViewParentCompat.java b/compat/java/android/support/v4/view/ViewParentCompat.java
index ec97988..53ff8dd 100644
--- a/compat/java/android/support/v4/view/ViewParentCompat.java
+++ b/compat/java/android/support/v4/view/ViewParentCompat.java
@@ -16,15 +16,15 @@
 
 package android.support.v4.view;
 
-import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 
 /**
  * Helper for accessing features in {@link ViewParent}
@@ -32,39 +32,9 @@
  */
 public final class ViewParentCompat {
 
-    interface ViewParentCompatImpl {
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event);
-        boolean onStartNestedScroll(ViewParent parent, View child, View target,
-                int nestedScrollAxes);
-        void onNestedScrollAccepted(ViewParent parent, View child, View target,
-                int nestedScrollAxes);
-        void onStopNestedScroll(ViewParent parent, View target);
-        void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed);
-        void onNestedPreScroll(ViewParent parent, View target, int dx, int dy, int[] consumed);
-        boolean onNestedFling(ViewParent parent, View target, float velocityX, float velocityY,
-                boolean consumed);
-        boolean onNestedPreFling(ViewParent parent, View target, float velocityX, float velocityY);
-        void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-                View source, int changeType);
-    }
+    private static final String TAG = "ViewParentCompat";
 
-    static class ViewParentCompatStubImpl implements ViewParentCompatImpl {
-        @Override
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event) {
-            // Emulate what ViewRootImpl does in ICS and above.
-            if (child == null) {
-                return false;
-            }
-            final AccessibilityManager manager = (AccessibilityManager) child.getContext()
-                    .getSystemService(Context.ACCESSIBILITY_SERVICE);
-            manager.sendAccessibilityEvent(event);
-            return true;
-        }
-
-        @Override
+    static class ViewParentCompatBaseImpl {
         public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
             if (parent instanceof NestedScrollingParent) {
@@ -74,7 +44,6 @@
             return false;
         }
 
-        @Override
         public void onNestedScrollAccepted(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
             if (parent instanceof NestedScrollingParent) {
@@ -83,14 +52,12 @@
             }
         }
 
-        @Override
         public void onStopNestedScroll(ViewParent parent, View target) {
             if (parent instanceof NestedScrollingParent) {
                 ((NestedScrollingParent) parent).onStopNestedScroll(target);
             }
         }
 
-        @Override
         public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -99,7 +66,6 @@
             }
         }
 
-        @Override
         public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
                 int[] consumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -107,7 +73,6 @@
             }
         }
 
-        @Override
         public boolean onNestedFling(ViewParent parent, View target, float velocityX,
                 float velocityY, boolean consumed) {
             if (parent instanceof NestedScrollingParent) {
@@ -117,7 +82,6 @@
             return false;
         }
 
-        @Override
         public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
                 float velocityY) {
             if (parent instanceof NestedScrollingParent) {
@@ -127,88 +91,112 @@
             return false;
         }
 
-        @Override
         public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
                 View source, int changeType) {
         }
     }
 
-    static class ViewParentCompatICSImpl extends ViewParentCompatStubImpl {
-        @Override
-        public boolean requestSendAccessibilityEvent(
-                ViewParent parent, View child, AccessibilityEvent event) {
-            return ViewParentCompatICS.requestSendAccessibilityEvent(parent, child, event);
-        }
-    }
-
-    static class ViewParentCompatKitKatImpl extends ViewParentCompatICSImpl {
+    @RequiresApi(19)
+    static class ViewParentCompatApi19Impl extends ViewParentCompatBaseImpl {
 
         @Override
         public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
                 View source, int changeType) {
-            ViewParentCompatKitKat.notifySubtreeAccessibilityStateChanged(parent, child,
-                    source, changeType);
+            parent.notifySubtreeAccessibilityStateChanged(child, source, changeType);
         }
     }
 
-    static class ViewParentCompatLollipopImpl extends ViewParentCompatKitKatImpl {
+    @RequiresApi(21)
+    static class ViewParentCompatApi21Impl extends ViewParentCompatApi19Impl {
         @Override
         public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
-            return ViewParentCompatLollipop.onStartNestedScroll(parent, child, target,
-                    nestedScrollAxes);
+            try {
+                return parent.onStartNestedScroll(child, target, nestedScrollAxes);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onStartNestedScroll", e);
+                return false;
+            }
         }
 
         @Override
         public void onNestedScrollAccepted(ViewParent parent, View child, View target,
                 int nestedScrollAxes) {
-            ViewParentCompatLollipop.onNestedScrollAccepted(parent, child, target,
-                    nestedScrollAxes);
+            try {
+                parent.onNestedScrollAccepted(child, target, nestedScrollAxes);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedScrollAccepted", e);
+            }
         }
 
         @Override
         public void onStopNestedScroll(ViewParent parent, View target) {
-            ViewParentCompatLollipop.onStopNestedScroll(parent, target);
+            try {
+                parent.onStopNestedScroll(target);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onStopNestedScroll", e);
+            }
         }
 
         @Override
         public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed) {
-            ViewParentCompatLollipop.onNestedScroll(parent, target, dxConsumed, dyConsumed,
-                    dxUnconsumed, dyUnconsumed);
+            try {
+                parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedScroll", e);
+            }
         }
 
         @Override
         public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
                 int[] consumed) {
-            ViewParentCompatLollipop.onNestedPreScroll(parent, target, dx, dy, consumed);
+            try {
+                parent.onNestedPreScroll(target, dx, dy, consumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedPreScroll", e);
+            }
         }
 
         @Override
         public boolean onNestedFling(ViewParent parent, View target, float velocityX,
                 float velocityY, boolean consumed) {
-            return ViewParentCompatLollipop.onNestedFling(parent, target, velocityX, velocityY,
-                    consumed);
+            try {
+                return parent.onNestedFling(target, velocityX, velocityY, consumed);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedFling", e);
+                return false;
+            }
         }
 
         @Override
         public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
                 float velocityY) {
-            return ViewParentCompatLollipop.onNestedPreFling(parent, target, velocityX, velocityY);
+            try {
+                return parent.onNestedPreFling(target, velocityX, velocityY);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "ViewParent " + parent + " does not implement interface "
+                        + "method onNestedPreFling", e);
+                return false;
+            }
         }
     }
 
-    static final ViewParentCompatImpl IMPL;
+    static final ViewParentCompatBaseImpl IMPL;
     static {
         final int version = Build.VERSION.SDK_INT;
         if (version >= 21) {
-            IMPL = new ViewParentCompatLollipopImpl();
+            IMPL = new ViewParentCompatApi21Impl();
         } else if (version >= 19) {
-            IMPL = new ViewParentCompatKitKatImpl();
-        } else if (version >= 14) {
-            IMPL = new ViewParentCompatICSImpl();
+            IMPL = new ViewParentCompatApi19Impl();
         } else {
-            IMPL = new ViewParentCompatStubImpl();
+            IMPL = new ViewParentCompatBaseImpl();
         }
     }
 
@@ -233,10 +221,14 @@
      * @param child The child which requests sending the event.
      * @param event The event to be sent.
      * @return True if the event was sent.
+     *
+     * @deprecated Use {@link ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * directly.
      */
+    @Deprecated
     public static boolean requestSendAccessibilityEvent(
             ViewParent parent, View child, AccessibilityEvent event) {
-        return IMPL.requestSendAccessibilityEvent(parent, child, event);
+        return parent.requestSendAccessibilityEvent(child, event);
     }
 
     /**
diff --git a/compat/java/android/support/v4/widget/EdgeEffectCompat.java b/compat/java/android/support/v4/widget/EdgeEffectCompat.java
index d65fb67..8be2d9f 100644
--- a/compat/java/android/support/v4/widget/EdgeEffectCompat.java
+++ b/compat/java/android/support/v4/widget/EdgeEffectCompat.java
@@ -18,142 +18,41 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.widget.EdgeEffect;
 
 /**
- * Helper for accessing {@link android.widget.EdgeEffect} introduced after
- * API level 4 in a backwards compatible fashion.
+ * Helper for accessing {@link android.widget.EdgeEffect} in a backwards compatible fashion.
  *
- * This class is used to access {@link android.widget.EdgeEffect} on platform versions     
- * that support it. When running on older platforms it will result in no-ops. It should     
- * be used by views that wish to use the standard Android visual effects at the edges       
+ * This class is used to access {@link android.widget.EdgeEffect} on platform versions
+ * that support it. When running on older platforms it will result in no-ops. It should
+ * be used by views that wish to use the standard Android visual effects at the edges
  * of scrolling containers.
  */
 public final class EdgeEffectCompat {
-    private Object mEdgeEffect;
+    private EdgeEffect mEdgeEffect;
 
-    private static final EdgeEffectImpl IMPL;
+    private static final EdgeEffectBaseImpl IMPL;
 
     static {
         if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new EdgeEffectLollipopImpl(); // Lollipop
-        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
-            IMPL = new EdgeEffectIcsImpl();
+            IMPL = new EdgeEffectApi21Impl();
         } else {
-            IMPL = new BaseEdgeEffectImpl();
+            IMPL = new EdgeEffectBaseImpl();
         }
     }
 
-    interface EdgeEffectImpl {
-        public Object newEdgeEffect(Context context);
-        public void setSize(Object edgeEffect, int width, int height);
-        public boolean isFinished(Object edgeEffect);
-        public void finish(Object edgeEffect);
-        public boolean onPull(Object edgeEffect, float deltaDistance);
-        public boolean onRelease(Object edgeEffect);
-        public boolean onAbsorb(Object edgeEffect, int velocity);
-        public boolean draw(Object edgeEffect, Canvas canvas);
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement);
-    }
-
-    /**
-     * Null implementation to use pre-ICS
-     */
-    static class BaseEdgeEffectImpl implements EdgeEffectImpl {
-        @Override
-        public Object newEdgeEffect(Context context) {
-            return null;
-        }
-
-        @Override
-        public void setSize(Object edgeEffect, int width, int height) {
-        }
-
-        @Override
-        public boolean isFinished(Object edgeEffect) {
-            return true;
-        }
-
-        @Override
-        public void finish(Object edgeEffect) {
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance) {
-            return false;
-        }
-
-        @Override
-        public boolean onRelease(Object edgeEffect) {
-            return false;
-        }
-
-        @Override
-        public boolean onAbsorb(Object edgeEffect, int velocity) {
-            return false;
-        }
-
-        @Override
-        public boolean draw(Object edgeEffect, Canvas canvas) {
-            return false;
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return false;
+    static class EdgeEffectBaseImpl {
+        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+            edgeEffect.onPull(deltaDistance);
         }
     }
 
-    static class EdgeEffectIcsImpl implements EdgeEffectImpl {
+    @RequiresApi(21)
+    static class EdgeEffectApi21Impl extends EdgeEffectBaseImpl {
         @Override
-        public Object newEdgeEffect(Context context) {
-            return EdgeEffectCompatIcs.newEdgeEffect(context);
-        }
-
-        @Override
-        public void setSize(Object edgeEffect, int width, int height) {
-            EdgeEffectCompatIcs.setSize(edgeEffect, width, height);
-        }
-
-        @Override
-        public boolean isFinished(Object edgeEffect) {
-            return EdgeEffectCompatIcs.isFinished(edgeEffect);
-        }
-
-        @Override
-        public void finish(Object edgeEffect) {
-            EdgeEffectCompatIcs.finish(edgeEffect);
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance) {
-            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
-        }
-
-        @Override
-        public boolean onRelease(Object edgeEffect) {
-            return EdgeEffectCompatIcs.onRelease(edgeEffect);
-        }
-
-        @Override
-        public boolean onAbsorb(Object edgeEffect, int velocity) {
-            return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity);
-        }
-
-        @Override
-        public boolean draw(Object edgeEffect, Canvas canvas) {
-            return EdgeEffectCompatIcs.draw(edgeEffect, canvas);
-        }
-
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance);
-        }
-    }
-
-    static class EdgeEffectLollipopImpl extends EdgeEffectIcsImpl {
-        @Override
-        public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
-            return EdgeEffectCompatLollipop.onPull(edgeEffect, deltaDistance, displacement);
+        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+            edgeEffect.onPull(deltaDistance, displacement);
         }
     }
 
@@ -164,9 +63,12 @@
      * on the newly constructed object will be mocked/no-ops.</p>
      *
      * @param context Context to use for theming the effect
+     *
+     * @deprecated Use {@link EdgeEffect} constructor directly.
      */
+    @Deprecated
     public EdgeEffectCompat(Context context) {
-        mEdgeEffect = IMPL.newEdgeEffect(context);
+        mEdgeEffect = new EdgeEffect(context);
     }
 
     /**
@@ -174,9 +76,12 @@
      *
      * @param width Effect width in pixels
      * @param height Effect height in pixels
+     *
+     * @deprecated Use {@link EdgeEffect#setSize(int, int)} directly.
      */
+    @Deprecated
     public void setSize(int width, int height) {
-        IMPL.setSize(mEdgeEffect, width, height);
+        mEdgeEffect.setSize(width, height);
     }
 
     /**
@@ -185,17 +90,23 @@
      * drawing pass to continue the animation.
      *
      * @return true if animation is finished, false if drawing should continue on the next frame.
+     *
+     * @deprecated Use {@link EdgeEffect#isFinished()} directly.
      */
+    @Deprecated
     public boolean isFinished() {
-        return IMPL.isFinished(mEdgeEffect);
+        return mEdgeEffect.isFinished();
     }
 
     /**
      * Immediately finish the current animation.
      * After this call {@link #isFinished()} will return true.
+     *
+     * @deprecated Use {@link EdgeEffect#finish()} directly.
      */
+    @Deprecated
     public void finish() {
-        IMPL.finish(mEdgeEffect);
+        mEdgeEffect.finish();
     }
 
     /**
@@ -208,11 +119,13 @@
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
      * @return true if the host view should call invalidate, false if it should not.
-     * @deprecated use {@link #onPull(float, float)}
+     *
+     * @deprecated Use {@link #onPull(EdgeEffect, float, float)}.
      */
     @Deprecated
     public boolean onPull(float deltaDistance) {
-        return IMPL.onPull(mEdgeEffect, deltaDistance);
+        mEdgeEffect.onPull(deltaDistance);
+        return true;
     }
 
     /**
@@ -221,6 +134,9 @@
      * The host view should always {@link android.view.View#invalidate()} if this method
      * returns true and draw the results accordingly.
      *
+     * Views using {@link EdgeEffect} should favor {@link EdgeEffect#onPull(float, float)} when
+     * the displacement of the pull point is known.
+     *
      * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
@@ -228,9 +144,34 @@
      *                     initiating the pull. In the case of touch this is the finger position.
      *                     Values may be from 0-1.
      * @return true if the host view should call invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onPull(float)} directly.
      */
+    @Deprecated
     public boolean onPull(float deltaDistance, float displacement) {
-        return IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
+        IMPL.onPull(mEdgeEffect, deltaDistance, displacement);
+        return true;
+    }
+
+    /**
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after call this method
+     * and draw the results accordingly.
+     *
+     * @param edgeEffect The EdgeEffect that is attached to the view that is getting pulled away
+     *                   from an edge by the user.
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
+     * @param displacement The displacement from the starting side of the effect of the point
+     *                     initiating the pull. In the case of touch this is the finger position.
+     *                     Values may be from 0-1.
+     *
+     * @see {@link EdgeEffect#onPull(float, float)}
+     */
+    public static void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
+        IMPL.onPull(edgeEffect, deltaDistance, displacement);
     }
 
     /**
@@ -240,9 +181,13 @@
      * returns true and thereby draw the results accordingly.
      *
      * @return true if the host view should invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onRelease()} directly.
      */
+    @Deprecated
     public boolean onRelease() {
-        return IMPL.onRelease(mEdgeEffect);
+        mEdgeEffect.onRelease();
+        return mEdgeEffect.isFinished();
     }
 
     /**
@@ -255,9 +200,13 @@
      *
      * @param velocity Velocity at impact in pixels per second.
      * @return true if the host view should invalidate, false if it should not.
+     *
+     * @deprecated Use {@link EdgeEffect#onAbsorb(int)} directly.
      */
+    @Deprecated
     public boolean onAbsorb(int velocity) {
-        return IMPL.onAbsorb(mEdgeEffect, velocity);
+        mEdgeEffect.onAbsorb(velocity);
+        return true;
     }
 
     /**
@@ -269,8 +218,11 @@
      * @param canvas Canvas to draw into
      * @return true if drawing should continue beyond this frame to continue the
      *         animation
+     *
+     * @deprecated Use {@link EdgeEffect#draw(Canvas)} directly.
      */
+    @Deprecated
     public boolean draw(Canvas canvas) {
-        return IMPL.draw(mEdgeEffect, canvas);
+        return mEdgeEffect.draw(canvas);
     }
 }
diff --git a/compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java b/compat/java/android/support/v4/widget/TintableCompoundButton.java
similarity index 86%
rename from compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java
rename to compat/java/android/support/v4/widget/TintableCompoundButton.java
index 6bcfa30..f739fac 100644
--- a/compat/gingerbread/android/support/v4/widget/TintableCompoundButton.java
+++ b/compat/java/android/support/v4/widget/TintableCompoundButton.java
@@ -37,7 +37,7 @@
      *
      * @param tint the tint to apply, may be {@code null} to clear tint
      */
-    public void setSupportButtonTintList(@Nullable ColorStateList tint);
+    void setSupportButtonTintList(@Nullable ColorStateList tint);
 
     /**
      * Returns the tint applied to the button drawable
@@ -45,7 +45,7 @@
      * @see #setSupportButtonTintList(ColorStateList)
      */
     @Nullable
-    public ColorStateList getSupportButtonTintList();
+    ColorStateList getSupportButtonTintList();
 
     /**
      * Specifies the blending mode which should be used to apply the tint specified by
@@ -56,9 +56,10 @@
      *                 {@code null} to clear tint
      *
      * @see #getSupportButtonTintMode()
-     * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
+     * @see android.support.v4.graphics.drawable.DrawableCompat#setTintMode(Drawable,
+     * PorterDuff.Mode)
      */
-    public void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode);
+    void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode);
 
     /**
      * Returns the blending mode used to apply the tint to the button drawable
@@ -66,5 +67,5 @@
      * @see #setSupportButtonTintMode(PorterDuff.Mode)
      */
     @Nullable
-    public PorterDuff.Mode getSupportButtonTintMode();
+    PorterDuff.Mode getSupportButtonTintMode();
 }
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
deleted file mode 100644
index 184ef6c..0000000
--- a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-@RequiresApi(18)
-class ViewGroupCompatJellybeanMR2 {
-    public static int getLayoutMode(ViewGroup group) {
-        return group.getLayoutMode();
-    }
-
-    public static void setLayoutMode(ViewGroup group, int mode) {
-        group.setLayoutMode(mode);
-    }
-}
diff --git a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
deleted file mode 100644
index 78761a4..0000000
--- a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.v4.view;
-
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewParent;
-
-@RequiresApi(19)
-class ViewParentCompatKitKat {
-    public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-            View source, int changeType) {
-        parent.notifySubtreeAccessibilityStateChanged(child, source, changeType);
-    }
-}
diff --git a/core-ui/java/android/support/v4/view/ViewPager.java b/core-ui/java/android/support/v4/view/ViewPager.java
index 2615cd2..7ac7106 100644
--- a/core-ui/java/android/support/v4/view/ViewPager.java
+++ b/core-ui/java/android/support/v4/view/ViewPager.java
@@ -37,7 +37,6 @@
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.support.v4.widget.EdgeEffectCompat;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.FocusFinder;
@@ -52,6 +51,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
+import android.widget.EdgeEffect;
 import android.widget.Scroller;
 
 import java.lang.annotation.ElementType;
@@ -225,8 +225,8 @@
     private boolean mFakeDragging;
     private long mFakeDragBeginTime;
 
-    private EdgeEffectCompat mLeftEdge;
-    private EdgeEffectCompat mRightEdge;
+    private EdgeEffect mLeftEdge;
+    private EdgeEffect mRightEdge;
 
     private boolean mFirstLayout = true;
     private boolean mNeedCalculatePageOffsets = false;
@@ -406,8 +406,8 @@
         mTouchSlop = configuration.getScaledPagingTouchSlop();
         mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mLeftEdge = new EdgeEffectCompat(context);
-        mRightEdge = new EdgeEffectCompat(context);
+        mLeftEdge = new EdgeEffect(context);
+        mRightEdge = new EdgeEffect(context);
 
         mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
         mCloseEnough = (int) (CLOSE_ENOUGH * density);
@@ -2284,7 +2284,9 @@
         boolean needsInvalidate;
         mActivePointerId = INVALID_POINTER;
         endDrag();
-        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
+        mLeftEdge.onRelease();
+        mRightEdge.onRelease();
+        needsInvalidate = mLeftEdge.isFinished() | mRightEdge.isFinished();
         return needsInvalidate;
     }
 
@@ -2324,13 +2326,15 @@
         if (scrollX < leftBound) {
             if (leftAbsolute) {
                 float over = leftBound - scrollX;
-                needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
+                mLeftEdge.onPull(Math.abs(over) / width);
+                needsInvalidate = true;
             }
             scrollX = leftBound;
         } else if (scrollX > rightBound) {
             if (rightAbsolute) {
                 float over = scrollX - rightBound;
-                needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
+                mRightEdge.onPull(Math.abs(over) / width);
+                needsInvalidate = true;
             }
             scrollX = rightBound;
         }
diff --git a/core-ui/java/android/support/v4/widget/NestedScrollView.java b/core-ui/java/android/support/v4/widget/NestedScrollView.java
index 2cfd551..2bc09d1 100644
--- a/core-ui/java/android/support/v4/widget/NestedScrollView.java
+++ b/core-ui/java/android/support/v4/widget/NestedScrollView.java
@@ -51,6 +51,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
+import android.widget.EdgeEffect;
 import android.widget.FrameLayout;
 import android.widget.ScrollView;
 
@@ -95,8 +96,8 @@
 
     private final Rect mTempRect = new Rect();
     private ScrollerCompat mScroller;
-    private EdgeEffectCompat mEdgeGlowTop;
-    private EdgeEffectCompat mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * Position of the last motion event.
@@ -808,13 +809,13 @@
                         ensureGlows();
                         final int pulledToY = oldY + deltaY;
                         if (pulledToY < 0) {
-                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
                                     ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
                         } else if (pulledToY > range) {
-                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
                                     1.f - ev.getX(activePointerIndex)
                                             / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
@@ -1749,8 +1750,8 @@
         if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                mEdgeGlowTop = new EdgeEffectCompat(context);
-                mEdgeGlowBottom = new EdgeEffectCompat(context);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index 2db3471..1ade508 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -24,8 +24,10 @@
 import android.os.Build;
 import android.os.SystemClock;
 import android.support.design.test.R;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.Suppress;
 import android.widget.ImageView;
 
 import org.junit.Test;
@@ -137,6 +139,8 @@
         assertScrimAlpha(0);
     }
 
+    @Suppress
+    @FlakyTest(bugId = 30701044)
     @Test
     public void testScrollingToolbar() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_scroll,
@@ -247,6 +251,8 @@
         assertScrimAlpha(0);
     }
 
+    @Suppress
+    @FlakyTest(bugId = 30701044)
     @Test
     public void testScrollingToolbarEnterAlways() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_scroll_enteralways,
@@ -352,6 +358,8 @@
         assertScrimAlpha(0);
     }
 
+    @Suppress
+    @FlakyTest(bugId = 30701044)
     @Test
     public void testPinnedToolbarAndAnchoredFab() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin_with_fab,
@@ -418,6 +426,8 @@
         }
     }
 
+    @Suppress
+    @FlakyTest(bugId = 30701044)
     @Test
     public void testPinnedToolbarAndParallaxImage() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_with_image,
@@ -521,6 +531,8 @@
      * inherits from) has an issue with measuring children with margins when run on earlier
      * versions of the platform.
      */
+    @Suppress
+    @FlakyTest(bugId = 30701044)
     @Test
     @SdkSuppress(minSdkVersion = 11)
     public void testPinnedToolbarWithMargins() throws Throwable {
diff --git a/media-compat/java/android/support/v4/media/MediaDescriptionCompat.aidl b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.aidl
new file mode 100644
index 0000000..f002cdd
--- /dev/null
+++ b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.support.v4.media;
+
+parcelable MediaDescriptionCompat;
diff --git a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
index 4f2e38a..969b803 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
@@ -17,6 +17,7 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
+import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
 import android.support.v4.media.session.IMediaControllerCallback;
@@ -33,7 +34,7 @@
  * @hide
  */
 interface IMediaSession {
-    // Next ID: 40
+    // Next ID: 44
     void sendCommand(String command, in Bundle args, in MediaSessionCompat.ResultReceiverWrapper cb) = 0;
     boolean sendMediaButton(in KeyEvent mediaButton) = 1;
     void registerCallbackListener(in IMediaControllerCallback cb) = 2;
@@ -54,6 +55,10 @@
     int getRatingType() = 31;
     int getRepeatMode() = 36;
     boolean isShuffleModeEnabled() = 37;
+    void addQueueItem(in MediaDescriptionCompat description) = 40;
+    void addQueueItemAt(in MediaDescriptionCompat description, int index) = 41;
+    void removeQueueItem(in MediaDescriptionCompat description) = 42;
+    void removeQueueItemAt(int index) = 43;
 
     // These commands are for the TransportControls
     void prepare() = 32;
diff --git a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
index 94030bc..5c80db1 100644
--- a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -30,6 +30,7 @@
 import android.os.ResultReceiver;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.app.SupportActivity;
+import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
 import android.support.v4.media.VolumeProviderCompat;
@@ -62,6 +63,19 @@
 
     static final String COMMAND_GET_EXTRA_BINDER =
             "android.support.v4.media.session.command.GET_EXTRA_BINDER";
+    static final String COMMAND_ADD_QUEUE_ITEM =
+            "android.support.v4.media.session.command.ADD_QUEUE_ITEM";
+    static final String COMMAND_ADD_QUEUE_ITEM_AT =
+            "android.support.v4.media.session.command.ADD_QUEUE_ITEM_AT";
+    static final String COMMAND_REMOVE_QUEUE_ITEM =
+            "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM";
+    static final String COMMAND_REMOVE_QUEUE_ITEM_AT =
+            "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM_AT";
+
+    static final String COMMAND_ARGUMENT_MEDIA_DESCRIPTION =
+            "android.support.v4.media.session.command.ARGUMENT_MEDIA_DESCRIPTION";
+    static final String COMMAND_ARGUMENT_INDEX =
+            "android.support.v4.media.session.command.ARGUMENT_INDEX";
 
     private static class MediaControllerExtraData extends SupportActivity.ExtraData {
         private final MediaControllerCompat mMediaController;
@@ -241,6 +255,62 @@
     }
 
     /**
+     * Add a queue item from the given {@code description} at the end of the play queue
+     * of this session. Not all sessions may support this.
+     *
+     * @param description The {@link MediaDescriptionCompat} for creating the
+     *            {@link MediaSessionCompat.QueueItem} to be inserted.
+     * @throws UnsupportedOperationException If this session doesn't support this.
+     * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS
+     */
+    public void addQueueItem(MediaDescriptionCompat description) {
+        mImpl.addQueueItem(description);
+    }
+
+    /**
+     * Add a queue item from the given {@code description} at the specified position
+     * in the play queue of this session. Shifts the queue item currently at that position
+     * (if any) and any subsequent queue items to the right (adds one to their indices).
+     * Not all sessions may support this.
+     *
+     * @param description The {@link MediaDescriptionCompat} for creating the
+     *            {@link MediaSessionCompat.QueueItem} to be inserted.
+     * @param index The index at which the created {@link MediaSessionCompat.QueueItem}
+     *            is to be inserted.
+     * @throws UnsupportedOperationException If this session doesn't support this.
+     * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS
+     */
+    public void addQueueItem(MediaDescriptionCompat description, int index) {
+        mImpl.addQueueItem(description, index);
+    }
+
+    /**
+     * Remove the first occurrence of the specified {@link MediaSessionCompat.QueueItem}
+     * with the given {@link MediaDescriptionCompat description} in the play queue of the
+     * associated session. Not all sessions may support this.
+     *
+     * @param description The {@link MediaDescriptionCompat} for denoting the
+     *            {@link MediaSessionCompat.QueueItem} to be removed.
+     * @throws UnsupportedOperationException If this session doesn't support this.
+     * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS
+     */
+    public void removeQueueItem(MediaDescriptionCompat description) {
+        mImpl.removeQueueItem(description);
+    }
+
+    /**
+     * Remove an queue item at the specified position in the play queue
+     * of this session. Not all sessions may support this.
+     *
+     * @param index The index of the element to be removed.
+     * @throws UnsupportedOperationException If this session doesn't support this.
+     * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS
+     */
+    public void removeQueueItemAt(int index) {
+        mImpl.removeQueueItemAt(index);
+    }
+
+    /**
      * Get the queue title for this session.
      */
     public CharSequence getQueueTitle() {
@@ -1051,6 +1121,10 @@
         MediaMetadataCompat getMetadata();
 
         List<MediaSessionCompat.QueueItem> getQueue();
+        void addQueueItem(MediaDescriptionCompat description);
+        void addQueueItem(MediaDescriptionCompat description, int index);
+        void removeQueueItem(MediaDescriptionCompat description);
+        void removeQueueItemAt(int index);
         CharSequence getQueueTitle();
         Bundle getExtras();
         int getRatingType();
@@ -1162,6 +1236,62 @@
         }
 
         @Override
+        public void addQueueItem(MediaDescriptionCompat description) {
+            try {
+                long flags = mBinder.getFlags();
+                if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+                    throw new UnsupportedOperationException(
+                            "This session doesn't support queue management operations");
+                }
+                mBinder.addQueueItem(description);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in addQueueItem. " + e);
+            }
+        }
+
+        @Override
+        public void addQueueItem(MediaDescriptionCompat description, int index) {
+            try {
+                long flags = mBinder.getFlags();
+                if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+                    throw new UnsupportedOperationException(
+                            "This session doesn't support queue management operations");
+                }
+                mBinder.addQueueItemAt(description, index);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in addQueueItemAt. " + e);
+            }
+        }
+
+        @Override
+        public void removeQueueItem(MediaDescriptionCompat description) {
+            try {
+                long flags = mBinder.getFlags();
+                if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+                    throw new UnsupportedOperationException(
+                            "This session doesn't support queue management operations");
+                }
+                mBinder.removeQueueItem(description);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeQueueItem. " + e);
+            }
+        }
+
+        @Override
+        public void removeQueueItemAt(int index) {
+            try {
+                long flags = mBinder.getFlags();
+                if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
+                    throw new UnsupportedOperationException(
+                            "This session doesn't support queue management operations");
+                }
+                mBinder.removeQueueItemAt(index);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeQueueItemAt. " + e);
+            }
+        }
+
+        @Override
         public CharSequence getQueueTitle() {
             try {
                 return mBinder.getQueueTitle();
@@ -1589,6 +1719,35 @@
         }
 
         @Override
+        public void addQueueItem(MediaDescriptionCompat description) {
+            Bundle params = new Bundle();
+            params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
+            sendCommand(COMMAND_ADD_QUEUE_ITEM, params, null);
+        }
+
+        @Override
+        public void addQueueItem(MediaDescriptionCompat description, int index) {
+            Bundle params = new Bundle();
+            params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
+            params.putInt(COMMAND_ARGUMENT_INDEX, index);
+            sendCommand(COMMAND_ADD_QUEUE_ITEM_AT, params, null);
+        }
+
+        @Override
+        public void removeQueueItem(MediaDescriptionCompat description) {
+            Bundle params = new Bundle();
+            params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
+            sendCommand(COMMAND_REMOVE_QUEUE_ITEM, params, null);
+        }
+
+        @Override
+        public void removeQueueItemAt(int index) {
+            Bundle params = new Bundle();
+            params.putInt(COMMAND_ARGUMENT_INDEX, index);
+            sendCommand(COMMAND_REMOVE_QUEUE_ITEM_AT, params, null);
+        }
+
+        @Override
         public CharSequence getQueueTitle() {
             return MediaControllerCompatApi21.getQueueTitle(mControllerObj);
         }
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index 4d929a4..c588c7b 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -100,7 +100,10 @@
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
-    @IntDef(flag=true, value={FLAG_HANDLES_MEDIA_BUTTONS, FLAG_HANDLES_TRANSPORT_CONTROLS})
+    @IntDef(flag=true, value={
+            FLAG_HANDLES_MEDIA_BUTTONS,
+            FLAG_HANDLES_TRANSPORT_CONTROLS,
+            FLAG_HANDLES_QUEUE_COMMANDS })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SessionFlags {}
 
@@ -117,6 +120,12 @@
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
+     * Set this flag on the session to indicate that it handles queue
+     * management commands through its {@link Callback}.
+     */
+    public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
+
+    /**
      * Custom action to invoke playFromUri() for the forward compatibility.
      */
     static final String ACTION_PLAY_FROM_URI =
@@ -277,8 +286,6 @@
             mImpl = new MediaSessionImplApi19(context, tag, mbrComponent, mbrIntent);
         } else if (android.os.Build.VERSION.SDK_INT >= 18) {
             mImpl = new MediaSessionImplApi18(context, tag, mbrComponent, mbrIntent);
-        } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-            mImpl = new MediaSessionImplApi14(context, tag, mbrComponent, mbrIntent);
         } else {
             mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent);
         }
@@ -895,6 +902,48 @@
         public void onCustomAction(String action, Bundle extras) {
         }
 
+        /**
+         * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem}
+         * with the given {@link MediaDescriptionCompat description} at the end of the play queue.
+         *
+         * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem}
+         *            to be inserted.
+         */
+        public void onAddQueueItem(MediaDescriptionCompat description) {
+        }
+
+        /**
+         * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem}
+         * with the given {@link MediaDescriptionCompat description} at the specified position
+         * in the play queue.
+         *
+         * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem}
+         *            to be inserted.
+         * @param index The index at which the created {@link QueueItem} is to be inserted.
+         */
+        public void onAddQueueItem(MediaDescriptionCompat description, int index) {
+        }
+
+        /**
+         * Called when a {@link MediaControllerCompat} wants to remove the first occurrence of the
+         * specified {@link QueueItem} with the given {@link MediaDescriptionCompat description}
+         * in the play queue.
+         *
+         * @param description The {@link MediaDescriptionCompat} for denoting the {@link QueueItem}
+         *            to be removed.
+         */
+        public void onRemoveQueueItem(MediaDescriptionCompat description) {
+        }
+
+        /**
+         * Called when a {@link MediaControllerCompat} wants to remove a {@link QueueItem} at the
+         * specified position in the play queue.
+         *
+         * @param index The index of the element to be removed.
+         */
+        public void onRemoveQueueItemAt(int index) {
+        }
+
         private class StubApi21 implements MediaSessionCompatApi21.Callback {
 
             StubApi21() {
@@ -909,6 +958,25 @@
                         BundleCompat.putBinder(result, EXTRA_BINDER, impl.getExtraSessionBinder());
                         cb.send(0, result);
                     }
+                } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) {
+                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                    Callback.this.onAddQueueItem(
+                            (MediaDescriptionCompat) extras.getParcelable(
+                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
+                } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) {
+                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                    Callback.this.onAddQueueItem(
+                            (MediaDescriptionCompat) extras.getParcelable(
+                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION),
+                            extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
+                } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) {
+                    extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader());
+                    Callback.this.onRemoveQueueItem(
+                            (MediaDescriptionCompat) extras.getParcelable(
+                                    MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION));
+                } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) {
+                    Callback.this.onRemoveQueueItemAt(
+                            extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX));
                 } else {
                     Callback.this.onCommand(command, extras, cb);
                 }
@@ -1408,6 +1476,9 @@
     }
 
     static class MediaSessionImplBase implements MediaSessionImpl {
+        /***** RemoteControlClient States, we only need none as the others were public *******/
+        static final int RCC_PLAYSTATE_NONE = 0;
+
         private final Context mContext;
         private final ComponentName mMediaButtonReceiverComponentName;
         private final PendingIntent mMediaButtonReceiverIntent;
@@ -1416,6 +1487,7 @@
         final String mPackageName;
         final String mTag;
         final AudioManager mAudioManager;
+        final RemoteControlClient mRcc;
 
         final Object mLock = new Object();
         final RemoteCallbackList<IMediaControllerCallback> mControllerCallbacks
@@ -1425,6 +1497,7 @@
         boolean mDestroyed = false;
         boolean mIsActive = false;
         private boolean mIsMbrRegistered = false;
+        private boolean mIsRccRegistered = false;
         volatile Callback mCallback;
 
         @SessionFlags int mFlags;
@@ -1475,6 +1548,7 @@
             mRatingType = RatingCompat.RATING_NONE;
             mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
             mLocalStream = AudioManager.STREAM_MUSIC;
+            mRcc = new RemoteControlClient(mbrIntent);
         }
 
         @Override
@@ -1494,10 +1568,22 @@
             postToHandler(what, null);
         }
 
+        void postToHandler(int what, int arg1) {
+            postToHandler(what, null, arg1);
+        }
+
         void postToHandler(int what, Object obj) {
             postToHandler(what, obj, null);
         }
 
+        void postToHandler(int what, Object obj, int arg1) {
+            synchronized (mLock) {
+                if (mHandler != null) {
+                    mHandler.post(what, obj, arg1);
+                }
+            }
+        }
+
         void postToHandler(int what, Object obj, Bundle extras) {
             synchronized (mLock) {
                 if (mHandler != null) {
@@ -1586,6 +1672,83 @@
                 mState = state;
             }
             sendState(state);
+            if (!mIsActive) {
+                // Don't set the state until after the RCC is registered
+                return;
+            }
+            if (state == null) {
+                mRcc.setPlaybackState(0);
+                mRcc.setTransportControlFlags(0);
+            } else {
+                // Set state
+                setRccState(state);
+
+                // Set transport control flags
+                mRcc.setTransportControlFlags(
+                        getRccTransportControlFlagsFromActions(state.getActions()));
+            }
+        }
+
+        void setRccState(PlaybackStateCompat state) {
+            mRcc.setPlaybackState(getRccStateFromState(state.getState()));
+        }
+
+        int getRccStateFromState(int state) {
+            switch (state) {
+                case PlaybackStateCompat.STATE_CONNECTING:
+                case PlaybackStateCompat.STATE_BUFFERING:
+                    return RemoteControlClient.PLAYSTATE_BUFFERING;
+                case PlaybackStateCompat.STATE_ERROR:
+                    return RemoteControlClient.PLAYSTATE_ERROR;
+                case PlaybackStateCompat.STATE_FAST_FORWARDING:
+                    return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
+                case PlaybackStateCompat.STATE_NONE:
+                    return RCC_PLAYSTATE_NONE;
+                case PlaybackStateCompat.STATE_PAUSED:
+                    return RemoteControlClient.PLAYSTATE_PAUSED;
+                case PlaybackStateCompat.STATE_PLAYING:
+                    return RemoteControlClient.PLAYSTATE_PLAYING;
+                case PlaybackStateCompat.STATE_REWINDING:
+                    return RemoteControlClient.PLAYSTATE_REWINDING;
+                case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS:
+                    return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
+                case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT:
+                case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM:
+                    return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
+                case PlaybackStateCompat.STATE_STOPPED:
+                    return RemoteControlClient.PLAYSTATE_STOPPED;
+                default:
+                    return -1;
+            }
+        }
+
+        int getRccTransportControlFlagsFromActions(long actions) {
+            int transportControlFlags = 0;
+            if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
+            }
+            if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
+                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+            }
+            return transportControlFlags;
         }
 
         @Override
@@ -1600,6 +1763,81 @@
                 mMetadata = metadata;
             }
             sendMetadata(metadata);
+            if (!mIsActive) {
+                // Don't set metadata until after the rcc has been registered
+                return;
+            }
+            RemoteControlClient.MetadataEditor editor = buildRccMetadata(
+                    metadata == null ? null : metadata.getBundle());
+            editor.apply();
+        }
+
+        RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
+            RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true);
+            if (metadata == null) {
+                return editor;
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) {
+                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART);
+                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
+            } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
+                // Fall back to album art if the track art wasn't available
+                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
+                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) {
+                editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
+            }
+            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) {
+                editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER,
+                        metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER));
+            }
+            return editor;
         }
 
         @Override
@@ -1670,9 +1908,9 @@
 
         // Registers/unregisters components as needed.
         boolean update() {
+            boolean registeredRcc = false;
             if (mIsActive) {
-                // Register a MBR if it's supported, unregister it
-                // if support was removed.
+                // Register a MBR if it's supported, unregister it if support was removed.
                 if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) {
                     registerMediaButtonEventReceiver(mMediaButtonReceiverIntent,
                             mMediaButtonReceiverComponentName);
@@ -1682,6 +1920,20 @@
                             mMediaButtonReceiverComponentName);
                     mIsMbrRegistered = false;
                 }
+                // Register a RCC if it's supported, unregister it if support was removed.
+                if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
+                    mAudioManager.registerRemoteControlClient(mRcc);
+                    mIsRccRegistered = true;
+                    registeredRcc = true;
+                } else if (mIsRccRegistered
+                        && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
+                    // RCC keeps the state while the system resets its state internally when
+                    // we register RCC. Reset the state so that the states in RCC and the system
+                    // are in sync when we re-register the RCC.
+                    mRcc.setPlaybackState(0);
+                    mAudioManager.unregisterRemoteControlClient(mRcc);
+                    mIsRccRegistered = false;
+                }
             } else {
                 // When inactive remove any registered components.
                 if (mIsMbrRegistered) {
@@ -1689,8 +1941,16 @@
                             mMediaButtonReceiverComponentName);
                     mIsMbrRegistered = false;
                 }
+                if (mIsRccRegistered) {
+                    // RCC keeps the state while the system resets its state internally when
+                    // we register RCC. Reset the state so that the states in RCC and the system
+                    // are in sync when we re-register the RCC.
+                    mRcc.setPlaybackState(0);
+                    mAudioManager.unregisterRemoteControlClient(mRcc);
+                    mIsRccRegistered = false;
+                }
             }
-            return false;
+            return registeredRcc;
         }
 
         void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) {
@@ -2097,6 +2357,26 @@
             }
 
             @Override
+            public void addQueueItem(MediaDescriptionCompat description) {
+                postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM, description);
+            }
+
+            @Override
+            public void addQueueItemAt(MediaDescriptionCompat description, int index) {
+                postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index);
+            }
+
+            @Override
+            public void removeQueueItem(MediaDescriptionCompat description) {
+                postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM, description);
+            }
+
+            @Override
+            public void removeQueueItemAt(int index) {
+                postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index);
+            }
+
+            @Override
             public CharSequence getQueueTitle() {
                 return mQueueTitle;
             }
@@ -2169,6 +2449,10 @@
             private static final int MSG_SET_VOLUME = 22;
             private static final int MSG_SET_REPEAT_MODE = 23;
             private static final int MSG_SET_SHUFFLE_MODE_ENABLED = 24;
+            private static final int MSG_ADD_QUEUE_ITEM = 25;
+            private static final int MSG_ADD_QUEUE_ITEM_AT = 26;
+            private static final int MSG_REMOVE_QUEUE_ITEM = 27;
+            private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
 
             // KeyEvent constants only available on API 11+
             private static final int KEYCODE_MEDIA_PAUSE = 127;
@@ -2270,14 +2554,26 @@
                     case MSG_CUSTOM_ACTION:
                         cb.onCustomAction((String) msg.obj, msg.getData());
                         break;
+                    case MSG_ADD_QUEUE_ITEM:
+                        cb.onAddQueueItem((MediaDescriptionCompat) msg.obj);
+                        break;
+                    case MSG_ADD_QUEUE_ITEM_AT:
+                        cb.onAddQueueItem((MediaDescriptionCompat) msg.obj, msg.arg1);
+                        break;
+                    case MSG_REMOVE_QUEUE_ITEM:
+                        cb.onRemoveQueueItem((MediaDescriptionCompat) msg.obj);
+                        break;
+                    case MSG_REMOVE_QUEUE_ITEM_AT:
+                        cb.onRemoveQueueItemAt(msg.arg1);
+                        break;
                     case MSG_ADJUST_VOLUME:
-                        adjustVolume((int) msg.obj, 0);
+                        adjustVolume(msg.arg1, 0);
                         break;
                     case MSG_SET_VOLUME:
-                        setVolumeTo((int) msg.obj, 0);
+                        setVolumeTo(msg.arg1, 0);
                         break;
                     case MSG_SET_REPEAT_MODE:
-                        cb.onSetRepeatMode((int) msg.obj);
+                        cb.onSetRepeatMode(msg.arg1);
                         break;
                     case MSG_SET_SHUFFLE_MODE_ENABLED:
                         cb.onSetShuffleModeEnabled((boolean) msg.obj);
@@ -2347,219 +2643,8 @@
         }
     }
 
-    @RequiresApi(14)
-    static class MediaSessionImplApi14 extends MediaSessionImplBase {
-        /***** RemoteControlClient States, we only need none as the others were public *******/
-        static final int RCC_PLAYSTATE_NONE = 0;
-
-        final RemoteControlClient mRcc;
-
-        private boolean mIsRccRegistered = false;
-
-        MediaSessionImplApi14(Context context, String tag, ComponentName mbrComponent,
-                PendingIntent mbrIntent) {
-            super(context, tag, mbrComponent, mbrIntent);
-            mRcc = new RemoteControlClient(mbrIntent);
-        }
-
-        @Override
-        public void setPlaybackState(PlaybackStateCompat state) {
-            super.setPlaybackState(state);
-            if (!mIsActive) {
-                // Don't set the state until after the RCC is registered
-                return;
-            }
-            if (state == null) {
-                mRcc.setPlaybackState(0);
-                mRcc.setTransportControlFlags(0);
-            } else {
-                // Set state
-                setRccState(state);
-
-                // Set transport control flags
-                mRcc.setTransportControlFlags(
-                        getRccTransportControlFlagsFromActions(state.getActions()));
-            }
-        }
-
-        void setRccState(PlaybackStateCompat state) {
-            mRcc.setPlaybackState(getRccStateFromState(state.getState()));
-        }
-
-        int getRccStateFromState(int state) {
-            switch (state) {
-                case PlaybackStateCompat.STATE_CONNECTING:
-                case PlaybackStateCompat.STATE_BUFFERING:
-                    return RemoteControlClient.PLAYSTATE_BUFFERING;
-                case PlaybackStateCompat.STATE_ERROR:
-                    return RemoteControlClient.PLAYSTATE_ERROR;
-                case PlaybackStateCompat.STATE_FAST_FORWARDING:
-                    return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
-                case PlaybackStateCompat.STATE_NONE:
-                    return RCC_PLAYSTATE_NONE;
-                case PlaybackStateCompat.STATE_PAUSED:
-                    return RemoteControlClient.PLAYSTATE_PAUSED;
-                case PlaybackStateCompat.STATE_PLAYING:
-                    return RemoteControlClient.PLAYSTATE_PLAYING;
-                case PlaybackStateCompat.STATE_REWINDING:
-                    return RemoteControlClient.PLAYSTATE_REWINDING;
-                case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS:
-                    return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
-                case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT:
-                case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM:
-                    return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
-                case PlaybackStateCompat.STATE_STOPPED:
-                    return RemoteControlClient.PLAYSTATE_STOPPED;
-                default:
-                    return -1;
-            }
-        }
-
-        int getRccTransportControlFlagsFromActions(long actions) {
-            int transportControlFlags = 0;
-            if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
-            }
-            if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
-                transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
-            }
-            return transportControlFlags;
-        }
-
-        @Override
-        public void setMetadata(MediaMetadataCompat metadata) {
-            super.setMetadata(metadata);
-            if (!mIsActive) {
-                // Don't set metadata until after the rcc has been registered
-                return;
-            }
-            RemoteControlClient.MetadataEditor editor = buildRccMetadata(
-                    metadata == null ? null : metadata.getBundle());
-            editor.apply();
-        }
-
-        RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) {
-            RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true);
-            if (metadata == null) {
-                return editor;
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) {
-                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART);
-                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
-            } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
-                // Fall back to album art if the track art wasn't available
-                Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
-                editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art);
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) {
-                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
-                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) {
-                editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
-                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) {
-                editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
-                        metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
-            }
-            if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) {
-                editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER,
-                        metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER));
-            }
-            return editor;
-        }
-
-        @Override
-        boolean update() {
-            super.update();
-            boolean registeredRcc = false;
-            if (mIsActive) {
-                // Register a RCC if it's supported, unregister it if not.
-                if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
-                    mAudioManager.registerRemoteControlClient(mRcc);
-                    mIsRccRegistered = true;
-                    registeredRcc = true;
-                } else if (mIsRccRegistered
-                        && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) {
-                    // RCC keeps the state while the system resets its state internally when
-                    // we register RCC. Reset the state so that the states in RCC and the system
-                    // are in sync when we re-register the RCC.
-                    mRcc.setPlaybackState(0);
-                    mAudioManager.unregisterRemoteControlClient(mRcc);
-                    mIsRccRegistered = false;
-                }
-            } else {
-                // When inactive remove any registered components.
-                if (mIsRccRegistered) {
-                    // RCC keeps the state while the system resets its state internally when
-                    // we register RCC. Reset the state so that the states in RCC and the system
-                    // are in sync when we re-register the RCC.
-                    mRcc.setPlaybackState(0);
-                    mAudioManager.unregisterRemoteControlClient(mRcc);
-                    mIsRccRegistered = false;
-                }
-            }
-            return registeredRcc;
-        }
-    }
-
     @RequiresApi(18)
-    static class MediaSessionImplApi18 extends MediaSessionImplApi14 {
+    static class MediaSessionImplApi18 extends MediaSessionImplBase {
         private static boolean sIsMbrPendingIntentSupported = true;
 
         MediaSessionImplApi18(Context context, String tag, ComponentName mbrComponent,
@@ -3131,6 +3216,30 @@
             }
 
             @Override
+            public void addQueueItem(MediaDescriptionCompat descriptionCompat) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void addQueueItemAt(MediaDescriptionCompat descriptionCompat, int index) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void removeQueueItem(MediaDescriptionCompat description) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void removeQueueItemAt(int index) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
             public CharSequence getQueueTitle() {
                 // Will not be called.
                 throw new AssertionError();
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
index bf176ac..2598bef 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
@@ -31,6 +31,7 @@
 import android.os.ResultReceiver;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.PollingCheck;
 import android.support.v4.media.RatingCompat;
 import android.support.v4.media.VolumeProviderCompat;
@@ -65,6 +66,7 @@
             public void run() {
                 mSession = new MediaSessionCompat(getContext(), SESSION_TAG);
                 mSession.setCallback(mCallback, mHandler);
+                mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);
                 mController = mSession.getController();
             }
         });
@@ -123,7 +125,47 @@
         }
     }
 
-    // TODO(hdmoon): Uncomment after fixing this test. This test causes an Exception on System UI.
+    @Test
+    @SmallTest
+    public void testAddRemoveQueueItems() throws Exception {
+        final String mediaId = "media_id";
+        final String mediaTitle = "media_title";
+        MediaDescriptionCompat itemDescription = new MediaDescriptionCompat.Builder()
+                .setMediaId(mediaId).setTitle(mediaTitle).build();
+
+        synchronized (mWaitLock) {
+            mCallback.reset();
+            mController.addQueueItem(itemDescription);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnAddQueueItemCalled);
+            assertEquals(-1, mCallback.mQueueIndex);
+            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+
+            mCallback.reset();
+            mController.addQueueItem(itemDescription, 0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnAddQueueItemAtCalled);
+            assertEquals(0, mCallback.mQueueIndex);
+            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+
+            mCallback.reset();
+            mController.removeQueueItemAt(0);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnRemoveQueueItemAtCalled);
+            assertEquals(0, mCallback.mQueueIndex);
+
+            mCallback.reset();
+            mController.removeQueueItem(itemDescription);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnRemoveQueueItemCalled);
+            assertEquals(mediaId, mCallback.mQueueDescription.getMediaId());
+            assertEquals(mediaTitle, mCallback.mQueueDescription.getTitle());
+        }
+    }
+
+    // TODO: Uncomment after fixing this test. This test causes an Exception on System UI.
     // @Test
     // @SmallTest
     public void testVolumeControl() throws Exception {
@@ -354,6 +396,8 @@
         private ResultReceiver mCommandCallback;
         private int mRepeatMode;
         private boolean mShuffleModeEnabled;
+        private int mQueueIndex;
+        private MediaDescriptionCompat mQueueDescription;
 
         private boolean mOnPlayCalled;
         private boolean mOnPauseCalled;
@@ -376,6 +420,10 @@
         private boolean mOnPrepareFromUriCalled;
         private boolean mOnSetRepeatModeCalled;
         private boolean mOnSetShuffleModeEnabledCalled;
+        private boolean mOnAddQueueItemCalled;
+        private boolean mOnAddQueueItemAtCalled;
+        private boolean mOnRemoveQueueItemCalled;
+        private boolean mOnRemoveQueueItemAtCalled;
 
         public void reset() {
             mSeekPosition = -1;
@@ -390,6 +438,8 @@
             mCommandCallback = null;
             mShuffleModeEnabled = false;
             mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+            mQueueIndex = -1;
+            mQueueDescription = null;
 
             mOnPlayCalled = false;
             mOnPauseCalled = false;
@@ -412,6 +462,10 @@
             mOnPrepareFromUriCalled = false;
             mOnSetRepeatModeCalled = false;
             mOnSetShuffleModeEnabledCalled = false;
+            mOnAddQueueItemCalled = false;
+            mOnAddQueueItemAtCalled = false;
+            mOnRemoveQueueItemCalled = false;
+            mOnRemoveQueueItemAtCalled = false;
         }
 
         @Override
@@ -596,6 +650,34 @@
         }
 
         @Override
+        public void onAddQueueItem(MediaDescriptionCompat description) {
+            synchronized (mWaitLock) {
+                mOnAddQueueItemCalled = true;
+                mQueueDescription = description;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onAddQueueItem(MediaDescriptionCompat description, int index) {
+            synchronized (mWaitLock) {
+                mOnAddQueueItemAtCalled = true;
+                mQueueIndex = index;
+                mQueueDescription = description;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onRemoveQueueItem(MediaDescriptionCompat description) {
+            synchronized (mWaitLock) {
+                mOnRemoveQueueItemCalled = true;
+                mQueueDescription = description;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
         public void onSetShuffleModeEnabled(boolean enabled) {
             synchronized (mWaitLock) {
                 mOnSetShuffleModeEnabledCalled = true;
@@ -603,5 +685,14 @@
                 mWaitLock.notify();
             }
         }
+
+        @Override
+        public void onRemoveQueueItemAt(int index) {
+            synchronized (mWaitLock) {
+                mOnRemoveQueueItemAtCalled = true;
+                mQueueIndex = index;
+                mWaitLock.notify();
+            }
+        }
     }
 }
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index a584abc..e941c1d 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -68,6 +68,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
+import android.widget.EdgeEffect;
 import android.widget.OverScroller;
 
 import java.lang.annotation.Retention;
@@ -403,7 +404,7 @@
      */
     private int mDispatchScrollCounter = 0;
 
-    private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
+    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
 
     ItemAnimator mItemAnimator = new DefaultItemAnimator();
 
@@ -2105,26 +2106,22 @@
         boolean invalidate = false;
         if (overscrollX < 0) {
             ensureLeftGlow();
-            if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
+            invalidate = true;
         } else if (overscrollX > 0) {
             ensureRightGlow();
-            if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
+            invalidate = true;
         }
 
         if (overscrollY < 0) {
             ensureTopGlow();
-            if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
+            invalidate = true;
         } else if (overscrollY > 0) {
             ensureBottomGlow();
-            if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
-                invalidate = true;
-            }
+            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
+            invalidate = true;
         }
 
         if (invalidate || overscrollX != 0 || overscrollY != 0) {
@@ -2134,10 +2131,22 @@
 
     private void releaseGlows() {
         boolean needsInvalidate = false;
-        if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
-        if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
-        if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
-        if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
+        if (mLeftGlow != null) {
+            mLeftGlow.onRelease();
+            needsInvalidate = mLeftGlow.isFinished();
+        }
+        if (mTopGlow != null) {
+            mTopGlow.onRelease();
+            needsInvalidate |= mTopGlow.isFinished();
+        }
+        if (mRightGlow != null) {
+            mRightGlow.onRelease();
+            needsInvalidate |= mRightGlow.isFinished();
+        }
+        if (mBottomGlow != null) {
+            mBottomGlow.onRelease();
+            needsInvalidate |= mBottomGlow.isFinished();
+        }
         if (needsInvalidate) {
             ViewCompat.postInvalidateOnAnimation(this);
         }
@@ -2146,16 +2155,20 @@
     void considerReleasingGlowsOnScroll(int dx, int dy) {
         boolean needsInvalidate = false;
         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
-            needsInvalidate = mLeftGlow.onRelease();
+            mLeftGlow.onRelease();
+            needsInvalidate = mLeftGlow.isFinished();
         }
         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
-            needsInvalidate |= mRightGlow.onRelease();
+            mRightGlow.onRelease();
+            needsInvalidate |= mRightGlow.isFinished();
         }
         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
-            needsInvalidate |= mTopGlow.onRelease();
+            mTopGlow.onRelease();
+            needsInvalidate |= mTopGlow.isFinished();
         }
         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
-            needsInvalidate |= mBottomGlow.onRelease();
+            mBottomGlow.onRelease();
+            needsInvalidate |= mBottomGlow.isFinished();
         }
         if (needsInvalidate) {
             ViewCompat.postInvalidateOnAnimation(this);
@@ -2188,7 +2201,7 @@
         if (mLeftGlow != null) {
             return;
         }
-        mLeftGlow = new EdgeEffectCompat(getContext());
+        mLeftGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2201,7 +2214,7 @@
         if (mRightGlow != null) {
             return;
         }
-        mRightGlow = new EdgeEffectCompat(getContext());
+        mRightGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2214,7 +2227,7 @@
         if (mTopGlow != null) {
             return;
         }
-        mTopGlow = new EdgeEffectCompat(getContext());
+        mTopGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2228,7 +2241,7 @@
         if (mBottomGlow != null) {
             return;
         }
-        mBottomGlow = new EdgeEffectCompat(getContext());
+        mBottomGlow = new EdgeEffect(getContext());
         if (mClipToPadding) {
             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());