leanback: Use weareference for details transition timeout task am: 2056c3e524 am: 18f26b71f8 am: 9ea0501864
am: 81db5b459d

Change-Id: Ic3b5069375ded491da94adf8471281f9aea22701
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..4caef90
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,6 @@
+[Hook Scripts]
+checkstyle_hook = ../../development/tools/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+
+[Builtin Hooks]
+commit_msg_changeid_field = true
+commit_msg_test_field = true
diff --git a/annotations/AndroidManifest.xml b/annotations/AndroidManifest.xml
index 6f24ecb..2593e0e 100644
--- a/annotations/AndroidManifest.xml
+++ b/annotations/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="android.support.annotations" />
+<manifest package="android.support.annotation" />
diff --git a/api/current.txt b/api/current.txt
index 77b062b..231b9f4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -312,6 +312,40 @@
     method public boolean onRequestChildRectangleOnScreen(android.support.design.widget.CoordinatorLayout, android.view.View, android.graphics.Rect, boolean);
   }
 
+  public abstract class BaseTransientBottomBar<B extends android.support.design.widget.BaseTransientBottomBar<B>> {
+    ctor protected BaseTransientBottomBar(android.view.ViewGroup, android.view.View, android.support.design.widget.BaseTransientBottomBar.ContentViewCallback);
+    method public B addCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback);
+    method public void dismiss();
+    method public android.content.Context getContext();
+    method public int getDuration();
+    method public android.view.View getView();
+    method public boolean isShown();
+    method public boolean isShownOrQueued();
+    method public B removeCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback);
+    method public deprecated B setCallback(android.support.design.widget.BaseTransientBottomBar.BaseCallback);
+    method public B setDuration(int);
+    method public void show();
+    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
+    field public static final int LENGTH_LONG = 0; // 0x0
+    field public static final int LENGTH_SHORT = -1; // 0xffffffff
+  }
+
+  public static abstract class BaseTransientBottomBar.BaseCallback<B> {
+    ctor public BaseTransientBottomBar.BaseCallback();
+    method public void onDismissed(B, int);
+    method public void onShown(B);
+    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
+    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
+    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
+    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
+    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
+  }
+
+  public static abstract interface BaseTransientBottomBar.ContentViewCallback {
+    method public abstract void animateContentIn(int, int);
+    method public abstract void animateContentOut(int, int);
+  }
+
   public class BottomNavigationView extends android.widget.FrameLayout {
     ctor public BottomNavigationView(android.content.Context);
     ctor public BottomNavigationView(android.content.Context, android.util.AttributeSet);
@@ -604,37 +638,19 @@
     field public android.os.Bundle menuState;
   }
 
-  public final class Snackbar {
-    method public void dismiss();
-    method public int getDuration();
-    method public android.view.View getView();
-    method public boolean isShown();
-    method public boolean isShownOrQueued();
+  public final class Snackbar extends android.support.design.widget.BaseTransientBottomBar {
     method public static android.support.design.widget.Snackbar make(android.view.View, java.lang.CharSequence, int);
     method public static android.support.design.widget.Snackbar make(android.view.View, int, int);
     method public android.support.design.widget.Snackbar setAction(int, android.view.View.OnClickListener);
     method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
     method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
     method public android.support.design.widget.Snackbar setActionTextColor(int);
-    method public android.support.design.widget.Snackbar setCallback(android.support.design.widget.Snackbar.Callback);
-    method public android.support.design.widget.Snackbar setDuration(int);
     method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
     method public android.support.design.widget.Snackbar setText(int);
-    method public void show();
-    field public static final int LENGTH_INDEFINITE = -2; // 0xfffffffe
-    field public static final int LENGTH_LONG = 0; // 0x0
-    field public static final int LENGTH_SHORT = -1; // 0xffffffff
   }
 
-  public static abstract class Snackbar.Callback {
+  public static class Snackbar.Callback extends android.support.design.widget.BaseTransientBottomBar.BaseCallback {
     ctor public Snackbar.Callback();
-    method public void onDismissed(android.support.design.widget.Snackbar, int);
-    method public void onShown(android.support.design.widget.Snackbar);
-    field public static final int DISMISS_EVENT_ACTION = 1; // 0x1
-    field public static final int DISMISS_EVENT_CONSECUTIVE = 4; // 0x4
-    field public static final int DISMISS_EVENT_MANUAL = 3; // 0x3
-    field public static final int DISMISS_EVENT_SWIPE = 0; // 0x0
-    field public static final int DISMISS_EVENT_TIMEOUT = 2; // 0x2
   }
 
   public class SwipeDismissBehavior<V extends android.view.View> extends android.support.design.widget.CoordinatorLayout.Behavior {
@@ -1210,6 +1226,7 @@
 
   public final class BackgroundManager {
     method public void attach(android.view.Window);
+    method public void attachToView(android.view.View);
     method public final int getColor();
     method public android.graphics.drawable.Drawable getDefaultDimLayer();
     method public android.graphics.drawable.Drawable getDimLayer();
@@ -1228,6 +1245,7 @@
     method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
     method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
     method public void onTransitionEnd();
     method public boolean onTransitionPrepare();
     method public void onTransitionStart();
@@ -1242,6 +1260,7 @@
     method public final android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public final android.support.v17.leanback.widget.PresenterSelector getPresenterSelector();
     method public int getSelectedPosition();
+    method public final android.support.v17.leanback.widget.VerticalGridView getVerticalGridView();
     method public void onTransitionEnd();
     method public boolean onTransitionPrepare();
     method public void onTransitionStart();
@@ -1304,6 +1323,7 @@
     method public int getBrandColor();
     method public android.support.v17.leanback.app.HeadersFragment getHeadersFragment();
     method public int getHeadersState();
+    method public android.app.Fragment getMainFragment();
     method public final android.support.v17.leanback.app.BrowseFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
     method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
     method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
@@ -1405,6 +1425,7 @@
     method public int getBrandColor();
     method public int getHeadersState();
     method public android.support.v17.leanback.app.HeadersSupportFragment getHeadersSupportFragment();
+    method public android.support.v4.app.Fragment getMainFragment();
     method public final android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentAdapterRegistry getMainFragmentRegistry();
     method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
     method public android.support.v17.leanback.widget.OnItemViewSelectedListener getOnItemViewSelectedListener();
@@ -1496,13 +1517,32 @@
     method public abstract android.support.v17.leanback.app.BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter();
   }
 
+  public final class DetailsBackgroundParallaxHelper {
+    method public android.graphics.drawable.Drawable getCoverImageDrawable();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void setColor(int);
+    method public void setCoverImageBitmap(android.graphics.Bitmap);
+  }
+
+  public static class DetailsBackgroundParallaxHelper.ParallaxBuilder {
+    ctor public DetailsBackgroundParallaxHelper.ParallaxBuilder(android.content.Context, android.support.v17.leanback.app.DetailsParallaxManager);
+    method public android.support.v17.leanback.app.DetailsBackgroundParallaxHelper build();
+    method public android.support.v17.leanback.app.DetailsBackgroundParallaxHelper.ParallaxBuilder setColor(int);
+    method public android.support.v17.leanback.app.DetailsBackgroundParallaxHelper.ParallaxBuilder setCoverImageMinVerticalOffset(int);
+  }
+
   public class DetailsFragment extends android.support.v17.leanback.app.BrandedFragment {
     ctor public DetailsFragment();
     method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost();
+    method public final android.app.Fragment findOrCreateVideoFragment();
     method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.app.DetailsParallaxManager getParallaxManager();
     method public android.support.v17.leanback.app.RowsFragment getRowsFragment();
     method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public android.app.Fragment onCreateVideoFragment();
+    method public android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost onCreateVideoFragmentHost(android.app.Fragment);
     method protected void onEntranceTransitionEnd();
     method protected void onEntranceTransitionPrepare();
     method protected void onEntranceTransitionStart();
@@ -1518,13 +1558,31 @@
     method protected void setupPresenter(android.support.v17.leanback.widget.Presenter);
   }
 
+  public class DetailsFragmentVideoHelper {
+    ctor public DetailsFragmentVideoHelper(android.support.v17.leanback.app.PlaybackGlue, android.support.v17.leanback.app.DetailsParallaxManager);
+    method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+  }
+
+  public class DetailsParallaxManager {
+    ctor public DetailsParallaxManager(android.support.v7.widget.RecyclerView);
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty getFrameBottom();
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty getFrameTop();
+    method public android.support.v17.leanback.widget.Parallax getParallax();
+    method public android.support.v7.widget.RecyclerView getRecyclerView();
+  }
+
   public class DetailsSupportFragment extends android.support.v17.leanback.app.BrandedSupportFragment {
     ctor public DetailsSupportFragment();
     method protected java.lang.Object createEntranceTransition();
+    method public final android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost();
+    method public final android.support.v4.app.Fragment findOrCreateVideoSupportFragment();
     method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public android.support.v17.leanback.widget.BaseOnItemViewClickedListener getOnItemViewClickedListener();
+    method public android.support.v17.leanback.app.DetailsParallaxManager getParallaxManager();
     method public android.support.v17.leanback.app.RowsSupportFragment getRowsSupportFragment();
     method protected deprecated android.view.View inflateTitle(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
+    method public android.support.v4.app.Fragment onCreateVideoSupportFragment();
+    method public android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost onCreateVideoSupportFragmentHost(android.support.v4.app.Fragment);
     method protected void onEntranceTransitionEnd();
     method protected void onEntranceTransitionPrepare();
     method protected void onEntranceTransitionStart();
@@ -1577,7 +1635,9 @@
     method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment);
     method public static int add(android.app.FragmentManager, android.support.v17.leanback.app.GuidedStepFragment, int);
     method public static int addAsRoot(android.app.Activity, android.support.v17.leanback.app.GuidedStepFragment, int);
+    method public void collapseAction(boolean);
     method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
     method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
     method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
     method public int findActionPositionById(long);
@@ -1595,6 +1655,7 @@
     method public int getSelectedActionPosition();
     method public int getSelectedButtonActionPosition();
     method public int getUiStyle();
+    method public boolean isExpanded();
     method public boolean isFocusOutEndAllowed();
     method public boolean isFocusOutStartAllowed();
     method public boolean isSubActionsExpanded();
@@ -1634,7 +1695,9 @@
     method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment);
     method public static int add(android.support.v4.app.FragmentManager, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
     method public static int addAsRoot(android.support.v4.app.FragmentActivity, android.support.v17.leanback.app.GuidedStepSupportFragment, int);
+    method public void collapseAction(boolean);
     method public void collapseSubActions();
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
     method public void expandSubActions(android.support.v17.leanback.widget.GuidedAction);
     method public android.support.v17.leanback.widget.GuidedAction findActionById(long);
     method public int findActionPositionById(long);
@@ -1652,6 +1715,7 @@
     method public int getSelectedActionPosition();
     method public int getSelectedButtonActionPosition();
     method public int getUiStyle();
+    method public boolean isExpanded();
     method public boolean isFocusOutEndAllowed();
     method public boolean isFocusOutStartAllowed();
     method public boolean isSubActionsExpanded();
@@ -1717,8 +1781,9 @@
   }
 
   public abstract class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
     method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
     method public void detach();
     method public int getCurrentPosition();
@@ -1731,10 +1796,6 @@
     method public long getSupportedActions();
     method public boolean hasValidMedia();
     method public boolean isMediaPlaying();
-    method protected void pausePlayback();
-    method protected void skipToNext();
-    method protected void skipToPrevious();
-    method protected void startPlayback(int);
   }
 
   public abstract class OnboardingFragment extends android.app.Fragment {
@@ -1773,25 +1834,25 @@
     method public final void setLogoResourceId(int);
   }
 
-  public abstract class PlaybackControlGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.app.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
     ctor public PlaybackControlGlue(android.content.Context, int[]);
     ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
     method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
     method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
     method public void enableProgressUpdating(boolean);
-    method public android.content.Context getContext();
     method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
     method public abstract int getCurrentPosition();
     method public abstract int getCurrentSpeedId();
     method public int[] getFastForwardSpeeds();
-    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
+    method public deprecated android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
     method public abstract android.graphics.drawable.Drawable getMediaArt();
     method public abstract int getMediaDuration();
     method public abstract java.lang.CharSequence getMediaSubtitle();
     method public abstract java.lang.CharSequence getMediaTitle();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
+    method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
     method public int[] getRewindSpeeds();
     method public abstract long getSupportedActions();
     method public int getUpdatePeriod();
@@ -1801,15 +1862,15 @@
     method public void onActionClicked(android.support.v17.leanback.widget.Action);
     method public boolean onKey(android.view.View, int, android.view.KeyEvent);
     method protected void onMetadataChanged();
-    method protected abstract void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method protected deprecated void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
     method protected void onStateChanged();
-    method protected abstract void pausePlayback();
+    method protected deprecated void pausePlayback();
     method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
     method public void setFadingEnabled(boolean);
     method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method protected abstract void skipToNext();
-    method protected abstract void skipToPrevious();
-    method protected abstract void startPlayback(int);
+    method protected deprecated void skipToNext();
+    method protected deprecated void skipToPrevious();
+    method protected deprecated void startPlayback(int);
     method public void updateProgress();
     field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
     field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
@@ -1828,80 +1889,110 @@
     field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
   }
 
-  public abstract class PlaybackControlSupportGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+  public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
+    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  }
+
+  public abstract deprecated class PlaybackControlSupportGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
     ctor public PlaybackControlSupportGlue(android.content.Context, int[]);
     ctor public PlaybackControlSupportGlue(android.content.Context, int[], int[]);
     ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[]);
     ctor public PlaybackControlSupportGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlaySupportFragment, int[], int[]);
-    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
-    method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    method public void enableProgressUpdating(boolean);
-    method public android.content.Context getContext();
-    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
-    method public abstract int getCurrentPosition();
-    method public abstract int getCurrentSpeedId();
-    method public int[] getFastForwardSpeeds();
-    method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment getFragment();
-    method public abstract android.graphics.drawable.Drawable getMediaArt();
-    method public abstract int getMediaDuration();
-    method public abstract java.lang.CharSequence getMediaSubtitle();
-    method public abstract java.lang.CharSequence getMediaTitle();
-    method public android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public int[] getRewindSpeeds();
-    method public abstract long getSupportedActions();
-    method public int getUpdatePeriod();
-    method public abstract boolean hasValidMedia();
-    method public boolean isFadingEnabled();
-    method public abstract boolean isMediaPlaying();
-    method public void onActionClicked(android.support.v17.leanback.widget.Action);
-    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
-    method protected void onMetadataChanged();
-    method protected abstract void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method protected void onStateChanged();
-    method protected abstract void pausePlayback();
-    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method public void setFadingEnabled(boolean);
-    method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
-    method protected abstract void skipToNext();
-    method protected abstract void skipToPrevious();
-    method protected abstract void startPlayback(int);
-    method public void updateProgress();
-    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
-    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
-    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
-    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
-    field public static final int ACTION_REWIND = 32; // 0x20
-    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
-    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
-    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
-    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
-    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
-    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
-    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
-    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
-    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
-    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
   }
 
-  public class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
-    ctor public PlaybackOverlayFragment();
+  public class PlaybackFragment extends android.app.Fragment {
+    ctor public PlaybackFragment();
     method public void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
     method public int getBackgroundType();
-    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
-    method public final android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public final android.view.View.OnKeyListener getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackFragment.OnFadeCompleteListener getFadeCompleteListener();
     method public boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method public void resetFocus();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
     method public void setBackgroundType(int);
-    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public final void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
     method public void tickle();
     field public static final int BG_DARK = 1; // 0x1
     field public static final int BG_LIGHT = 2; // 0x2
     field public static final int BG_NONE = 0; // 0x0
   }
 
-  public static abstract interface PlaybackOverlayFragment.InputEventHandler {
-    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  public static class PlaybackFragment.OnFadeCompleteListener {
+    ctor public PlaybackFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+    ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost getHost();
+    method public boolean isReadyForPlayback();
+    method public void next();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public void setHost(android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost);
+    method public void setPlayerCallback(android.support.v17.leanback.app.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.HostLifecycleCallback {
+    ctor public PlaybackGlue.HostLifecycleCallback();
+    method public abstract void onHostStart();
+    method public abstract void onHostStop();
+  }
+
+  public static class PlaybackGlue.PlaybackGlueHost {
+    ctor public PlaybackGlue.PlaybackGlueHost();
+    method public void notifyPlaybackRowChanged();
+    method public void setFadingEnabled(boolean);
+    method public void setHostLifeCycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public abstract void onReadyForPlayback();
+  }
+
+  public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
+    ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
+    method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
+    method public boolean isFadingEnabled();
+    method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static abstract deprecated interface PlaybackOverlayFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
   }
 
   public static class PlaybackOverlayFragment.OnFadeCompleteListener {
@@ -1910,25 +2001,26 @@
     method public void onFadeOutComplete();
   }
 
-  public class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
+  public deprecated class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
     ctor public PlaybackOverlaySupportFragment();
     method public void fadeOut();
     method public int getBackgroundType();
+    method public final android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler getEventHandler();
     method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
-    method public final android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
+    method public final deprecated android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
     method public boolean isFadingEnabled();
     method public void setBackgroundType(int);
+    method public final void setEventHandler(android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler);
     method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public final void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
+    method public final deprecated void setInputEventHandler(android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler);
     method public void tickle();
     field public static final int BG_DARK = 1; // 0x1
     field public static final int BG_LIGHT = 2; // 0x2
     field public static final int BG_NONE = 0; // 0x0
   }
 
-  public static abstract interface PlaybackOverlaySupportFragment.InputEventHandler {
-    method public abstract boolean handleInputEvent(android.view.InputEvent);
+  public static abstract deprecated interface PlaybackOverlaySupportFragment.InputEventHandler implements android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler {
   }
 
   public static class PlaybackOverlaySupportFragment.OnFadeCompleteListener {
@@ -1937,6 +2029,44 @@
     method public void onFadeOutComplete();
   }
 
+  public class PlaybackSupportFragment extends android.support.v4.app.Fragment {
+    ctor public PlaybackSupportFragment();
+    method public void fadeOut();
+    method public android.support.v17.leanback.widget.ObjectAdapter getAdapter();
+    method public int getBackgroundType();
+    method public final android.view.View.OnKeyListener getEventHandler();
+    method public android.support.v17.leanback.app.PlaybackSupportFragment.OnFadeCompleteListener getFadeCompleteListener();
+    method public boolean isFadingEnabled();
+    method public void notifyPlaybackRowChanged();
+    method public void resetFocus();
+    method public void setAdapter(android.support.v17.leanback.widget.ObjectAdapter);
+    method public void setBackgroundType(int);
+    method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackSupportFragment.OnFadeCompleteListener);
+    method public void setFadingEnabled(boolean);
+    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+    method public void setSelectedPosition(int);
+    method public void setSelectedPosition(int, boolean);
+    method public void tickle();
+    field public static final int BG_DARK = 1; // 0x1
+    field public static final int BG_LIGHT = 2; // 0x2
+    field public static final int BG_NONE = 0; // 0x0
+  }
+
+  public static class PlaybackSupportFragment.OnFadeCompleteListener {
+    ctor public PlaybackSupportFragment.OnFadeCompleteListener();
+    method public void onFadeInComplete();
+    method public void onFadeOutComplete();
+  }
+
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+    ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
+  }
+
   public final class ProgressBarManager {
     ctor public ProgressBarManager();
     method public void disableProgressBar();
@@ -2012,6 +2142,8 @@
     method public void setBadgeDrawable(android.graphics.drawable.Drawable);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
     method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
     method public void setSearchQuery(java.lang.String, boolean);
     method public void setSearchQuery(android.content.Intent, boolean);
     method public void setSearchResultProvider(android.support.v17.leanback.app.SearchFragment.SearchResultProvider);
@@ -2039,6 +2171,8 @@
     method public void setBadgeDrawable(android.graphics.drawable.Drawable);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
     method public void setOnItemViewSelectedListener(android.support.v17.leanback.widget.OnItemViewSelectedListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
     method public void setSearchQuery(java.lang.String, boolean);
     method public void setSearchQuery(android.content.Intent, boolean);
     method public void setSearchResultProvider(android.support.v17.leanback.app.SearchSupportFragment.SearchResultProvider);
@@ -2081,6 +2215,26 @@
     method public void setSelectedPosition(int);
   }
 
+  public class VideoFragment extends android.support.v17.leanback.app.PlaybackFragment {
+    ctor public VideoFragment();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoFragmentGlueHost extends android.support.v17.leanback.app.PlaybackFragmentGlueHost {
+    ctor public VideoFragmentGlueHost(android.support.v17.leanback.app.VideoFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragment extends android.support.v17.leanback.app.PlaybackSupportFragment {
+    ctor public VideoSupportFragment();
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+  public class VideoSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost {
+    ctor public VideoSupportFragmentGlueHost(android.support.v17.leanback.app.VideoSupportFragment);
+    method public void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
 }
 
 package android.support.v17.leanback.database {
@@ -2096,6 +2250,26 @@
 
 package android.support.v17.leanback.graphics {
 
+  public class BoundsRule {
+    ctor public BoundsRule();
+    ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
+    method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule mBottom;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule mLeft;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule mRight;
+    field public android.support.v17.leanback.graphics.BoundsRule.ValueRule mTop;
+  }
+
+  public static final class BoundsRule.ValueRule {
+    method public int getAbsoluteValue();
+    method public float getFraction();
+    method public void setAbsoluteValue(int);
+    method public void setFraction(float);
+  }
+
   public final class ColorFilterCache {
     method public static android.support.v17.leanback.graphics.ColorFilterCache getColorFilterCache(int);
     method public android.graphics.ColorFilter getFilterForLevel(float);
@@ -2122,6 +2296,52 @@
     method public void setActiveLevel(float);
   }
 
+  public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public CompositeDrawable();
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
+    method public int getChildCount();
+    method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void removeChild(int);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+  }
+
+  public static final class CompositeDrawable.ChildDrawable {
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, android.support.v17.leanback.graphics.CompositeDrawable);
+    method public android.support.v17.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
+    method public void recomputeBounds();
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> BOTTOM_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> BOTTOM_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> LEFT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> LEFT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> RIGHT_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> RIGHT_FRACTION;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Integer> TOP_ABSOLUTE;
+    field public static final android.util.Property<android.support.v17.leanback.graphics.CompositeDrawable.ChildDrawable, java.lang.Float> TOP_FRACTION;
+  }
+
+  public class FitWidthBitmapDrawable extends android.graphics.drawable.Drawable {
+    ctor public FitWidthBitmapDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.Bitmap getBitmap();
+    method public int getOpacity();
+    method public android.graphics.Rect getSource();
+    method public int getVerticalOffset();
+    method public void setAlpha(int);
+    method public void setBitmap(android.graphics.Bitmap);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setSource(android.graphics.Rect);
+    method public void setVerticalOffset(int);
+  }
+
 }
 
 package android.support.v17.leanback.system {
@@ -2249,11 +2469,11 @@
     ctor public BaseCardView(android.content.Context, android.util.AttributeSet);
     ctor public BaseCardView(android.content.Context, android.util.AttributeSet, int);
     method public int getCardType();
-    method public int getExtraVisibility();
+    method public deprecated int getExtraVisibility();
     method public int getInfoVisibility();
     method public boolean isSelectedAnimationDelayed();
     method public void setCardType(int);
-    method public void setExtraVisibility(int);
+    method public deprecated void setExtraVisibility(int);
     method public void setInfoVisibility(int);
     method public void setSelectedAnimationDelayed(boolean);
     field public static final int CARD_REGION_VISIBLE_ACTIVATED = 1; // 0x1
@@ -2291,6 +2511,7 @@
     method public android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener getOnChildFocusListener();
     method public android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener();
     method public void setOnChildFocusListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnChildFocusListener);
+    method public void setOnDispatchKeyListener(android.view.View.OnKeyListener);
     method public void setOnFocusSearchListener(android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener);
   }
 
@@ -2623,12 +2844,17 @@
 
   public class GuidedActionsStylist implements android.support.v17.leanback.widget.FragmentAnimationProvider {
     ctor public GuidedActionsStylist();
+    method public void collapseAction(boolean);
+    method public void expandAction(android.support.v17.leanback.widget.GuidedAction, boolean);
     method public android.support.v17.leanback.widget.VerticalGridView getActionsGridView();
     method public android.support.v17.leanback.widget.GuidedAction getExpandedAction();
     method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
     method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
+    method public final boolean isBackKeyToCollapseActivatorView();
+    method public final boolean isBackKeyToCollapseSubActions();
     method public boolean isButtonActions();
     method public boolean isExpandTransitionSupported();
+    method public boolean isExpanded();
     method public boolean isInExpandTransition();
     method public boolean isSubActionsExpanded();
     method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
@@ -2643,7 +2869,8 @@
     method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
     method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
     method public void onDestroyView();
-    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected deprecated void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
+    method protected void onEditingModeChange(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean, boolean);
     method public void onImeAppearing(java.util.List<android.animation.Animator>);
     method public void onImeDisappearing(java.util.List<android.animation.Animator>);
     method public int onProvideItemLayoutId();
@@ -2652,10 +2879,11 @@
     method public boolean onUpdateActivatorView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
     method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     method public void setAsButtonActions();
-    method public void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
-    method public void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public final void setBackKeyToCollapseActivatorView(boolean);
+    method public final void setBackKeyToCollapseSubActions(boolean);
+    method public deprecated void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
-    method public void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public deprecated void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
     field public static final int VIEW_TYPE_DATE_PICKER = 1; // 0x1
     field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
   }
@@ -2709,9 +2937,11 @@
     ctor public HeaderItem(long, java.lang.String);
     ctor public HeaderItem(java.lang.String);
     method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getDescription();
     method public final long getId();
     method public final java.lang.String getName();
     method public void setContentDescription(java.lang.CharSequence);
+    method public void setDescription(java.lang.CharSequence);
   }
 
   public class HorizontalGridView extends android.support.v7.widget.RecyclerView {
@@ -2959,7 +3189,7 @@
     method public final boolean hasStableIds();
     method public boolean isImmediateNotifySupported();
     method protected final void notifyChanged();
-    method protected final void notifyItemRangeChanged(int, int);
+    method public final void notifyItemRangeChanged(int, int);
     method protected final void notifyItemRangeInserted(int, int);
     method protected final void notifyItemRangeRemoved(int, int);
     method protected void onHasStableIdsChanged();
@@ -2996,6 +3226,7 @@
   public abstract class OnChildViewHolderSelectedListener {
     ctor public OnChildViewHolderSelectedListener();
     method public void onChildViewHolderSelected(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
+    method public void onChildViewHolderSelectedAndPositioned(android.support.v7.widget.RecyclerView, android.support.v7.widget.RecyclerView.ViewHolder, int, int);
   }
 
   public abstract interface OnItemViewClickedListener implements android.support.v17.leanback.widget.BaseOnItemViewClickedListener {
@@ -3009,25 +3240,169 @@
     method public final boolean isRenderedAsRowView();
   }
 
+  public final class Parallax {
+    ctor public Parallax();
+    method public void addEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue...);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue...);
+    method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public android.support.v17.leanback.widget.ParallaxSource getSource();
+    method public void removeAllEffects();
+    method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
+    method public void setSource(android.support.v17.leanback.widget.ParallaxSource);
+  }
+
+  public abstract class ParallaxEffect<ParallaxEffectT extends android.support.v17.leanback.widget.ParallaxEffect, PropertyKeyValueT extends android.support.v17.leanback.widget.ParallaxSource.PropertyKeyValue> {
+    ctor public ParallaxEffect();
+    method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method protected abstract float calculateFraction(android.support.v17.leanback.widget.ParallaxSource);
+    method public final java.util.List<PropertyKeyValueT> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
+    method public final void performMapping(android.support.v17.leanback.widget.ParallaxSource);
+    method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final void setPropertyRanges(PropertyKeyValueT...);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
+    method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
+  }
+
+  public static final class ParallaxEffect.FloatEffect extends android.support.v17.leanback.widget.ParallaxEffect {
+    ctor public ParallaxEffect.FloatEffect();
+    method protected float calculateFraction(android.support.v17.leanback.widget.ParallaxSource);
+  }
+
+  public static final class ParallaxEffect.IntEffect extends android.support.v17.leanback.widget.ParallaxEffect {
+    ctor public ParallaxEffect.IntEffect();
+    method protected float calculateFraction(android.support.v17.leanback.widget.ParallaxSource);
+  }
+
+  public class ParallaxRecyclerViewSource extends android.support.v17.leanback.widget.ParallaxSource.IntSource {
+    ctor public ParallaxRecyclerViewSource(android.support.v7.widget.RecyclerView);
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty createProperty(java.lang.String, int);
+    method public int getMaxParentVisibleSize();
+    method public void setListener(android.support.v17.leanback.widget.ParallaxSource.Listener);
+  }
+
+  public static final class ParallaxRecyclerViewSource.ChildPositionProperty extends android.support.v17.leanback.widget.ParallaxSource.IntProperty {
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty adapterPosition(int);
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty fraction(float);
+    method public int getAdapterPosition();
+    method public float getFraction();
+    method public int getOffset();
+    method public int getViewId();
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty offset(int);
+    method public android.support.v17.leanback.widget.ParallaxRecyclerViewSource.ChildPositionProperty viewId(int);
+  }
+
+  public abstract class ParallaxSource<PropertyT extends android.util.Property> {
+    ctor public ParallaxSource();
+    method public abstract PropertyT addProperty(java.lang.String);
+    method public abstract PropertyT createProperty(java.lang.String, int);
+    method public final java.util.List<PropertyT> getProperties();
+    method public abstract void setListener(android.support.v17.leanback.widget.ParallaxSource.Listener);
+    method public abstract void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static class ParallaxSource.FloatProperty extends android.util.Property {
+    ctor public ParallaxSource.FloatProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue at(float, float);
+    method public final android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue atFraction(float);
+    method public final java.lang.Float get(android.support.v17.leanback.widget.ParallaxSource.FloatSource);
+    method public final int getIndex();
+    method public final void set(android.support.v17.leanback.widget.ParallaxSource.FloatSource, java.lang.Float);
+    field public static final float UNKNOWN_AFTER = 3.4028235E38f;
+    field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
+  }
+
+  public static class ParallaxSource.FloatPropertyKeyValue extends android.support.v17.leanback.widget.ParallaxSource.PropertyKeyValue {
+    ctor public ParallaxSource.FloatPropertyKeyValue(android.support.v17.leanback.widget.ParallaxSource.FloatProperty, float);
+    ctor public ParallaxSource.FloatPropertyKeyValue(android.support.v17.leanback.widget.ParallaxSource.FloatProperty, float, float);
+    method public final float getKeyValue(android.support.v17.leanback.widget.ParallaxSource.FloatSource);
+  }
+
+  public static abstract class ParallaxSource.FloatSource<FloatPropertyT extends android.support.v17.leanback.widget.ParallaxSource.FloatProperty> extends android.support.v17.leanback.widget.ParallaxSource {
+    ctor public ParallaxSource.FloatSource();
+    method public final FloatPropertyT addProperty(java.lang.String);
+    method public abstract float getMaxParentVisibleSize();
+    method public final float getPropertyValue(int);
+    method public final void setPropertyValue(int, float);
+    method public final void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static class ParallaxSource.IntProperty extends android.util.Property {
+    ctor public ParallaxSource.IntProperty(java.lang.String, int);
+    method public final android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue at(int, float);
+    method public final android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue atFraction(float);
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.ParallaxSource.IntSource);
+    method public final int getIndex();
+    method public final void set(android.support.v17.leanback.widget.ParallaxSource.IntSource, java.lang.Integer);
+    field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
+    field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
+  }
+
+  public static class ParallaxSource.IntPropertyKeyValue extends android.support.v17.leanback.widget.ParallaxSource.PropertyKeyValue {
+    ctor public ParallaxSource.IntPropertyKeyValue(android.support.v17.leanback.widget.ParallaxSource.IntProperty, int);
+    ctor public ParallaxSource.IntPropertyKeyValue(android.support.v17.leanback.widget.ParallaxSource.IntProperty, int, float);
+    method public final int getKeyValue(android.support.v17.leanback.widget.ParallaxSource.IntSource);
+  }
+
+  public static abstract class ParallaxSource.IntSource<IntPropertyT extends android.support.v17.leanback.widget.ParallaxSource.IntProperty> extends android.support.v17.leanback.widget.ParallaxSource {
+    ctor public ParallaxSource.IntSource();
+    method public final IntPropertyT addProperty(java.lang.String);
+    method public abstract int getMaxParentVisibleSize();
+    method public final int getPropertyValue(int);
+    method public final void setPropertyValue(int, int);
+    method public final void verifyProperties() throws java.lang.IllegalStateException;
+  }
+
+  public static abstract class ParallaxSource.Listener {
+    ctor public ParallaxSource.Listener();
+    method public void onPropertiesChanged(android.support.v17.leanback.widget.ParallaxSource);
+  }
+
+  public static class ParallaxSource.PropertyKeyValue<PropertyT extends android.util.Property> {
+    ctor public ParallaxSource.PropertyKeyValue(PropertyT);
+    method public PropertyT getProperty();
+  }
+
+  public abstract class ParallaxTarget {
+    ctor public ParallaxTarget();
+    method public abstract float getFraction();
+    method public abstract void update(float);
+  }
+
+  public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
+    method public float getFraction();
+    method public void update(float);
+  }
+
   public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
     ctor public PlaybackControlsRow(java.lang.Object);
     ctor public PlaybackControlsRow();
     method public android.support.v17.leanback.widget.Action getActionForKeyCode(int);
     method public android.support.v17.leanback.widget.Action getActionForKeyCode(android.support.v17.leanback.widget.ObjectAdapter, int);
     method public int getBufferedProgress();
+    method public long getBufferedProgressLong();
     method public int getCurrentTime();
+    method public long getCurrentTimeLong();
     method public final android.graphics.drawable.Drawable getImageDrawable();
     method public final java.lang.Object getItem();
     method public final android.support.v17.leanback.widget.ObjectAdapter getPrimaryActionsAdapter();
     method public final android.support.v17.leanback.widget.ObjectAdapter getSecondaryActionsAdapter();
     method public int getTotalTime();
+    method public long getTotalTimeLong();
     method public void setBufferedProgress(int);
+    method public void setBufferedProgressLong(long);
     method public void setCurrentTime(int);
+    method public void setCurrentTimeLong(long);
     method public final void setImageBitmap(android.content.Context, android.graphics.Bitmap);
     method public final void setImageDrawable(android.graphics.drawable.Drawable);
     method public final void setPrimaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
     method public final void setSecondaryActionsAdapter(android.support.v17.leanback.widget.ObjectAdapter);
     method public void setTotalTime(int);
+    method public void setTotalTimeLong(long);
   }
 
   public static class PlaybackControlsRow.ClosedCaptioningAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
@@ -3120,7 +3495,7 @@
     ctor public PlaybackControlsRow.ThumbsUpAction(android.content.Context);
   }
 
-  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+  public class PlaybackControlsRowPresenter extends android.support.v17.leanback.widget.PlaybackRowPresenter {
     ctor public PlaybackControlsRowPresenter(android.support.v17.leanback.widget.Presenter);
     ctor public PlaybackControlsRowPresenter();
     method public boolean areSecondaryActionsHidden();
@@ -3136,10 +3511,19 @@
     method public void showPrimaryActions(android.support.v17.leanback.widget.PlaybackControlsRowPresenter.ViewHolder);
   }
 
-  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+  public class PlaybackControlsRowPresenter.ViewHolder extends android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder {
     field public final android.support.v17.leanback.widget.Presenter.ViewHolder mDescriptionViewHolder;
   }
 
+  public abstract class PlaybackRowPresenter extends android.support.v17.leanback.widget.RowPresenter {
+    ctor public PlaybackRowPresenter();
+    method public void onReappear(android.support.v17.leanback.widget.RowPresenter.ViewHolder);
+  }
+
+  public static class PlaybackRowPresenter.ViewHolder extends android.support.v17.leanback.widget.RowPresenter.ViewHolder {
+    ctor public PlaybackRowPresenter.ViewHolder(android.view.View);
+  }
+
   public abstract class Presenter implements android.support.v17.leanback.widget.FacetProvider {
     ctor public Presenter();
     method protected static void cancelAnimationsRecursive(android.view.View);
@@ -3287,6 +3671,8 @@
     method public boolean isRecognizing();
     method public void setBadgeDrawable(android.graphics.drawable.Drawable);
     method public void setPermissionListener(android.support.v17.leanback.widget.SearchBar.SearchBarPermissionListener);
+    method public void setSearchAffordanceColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setSearchAffordanceColorsInListening(android.support.v17.leanback.widget.SearchOrbView.Colors);
     method public void setSearchBarListener(android.support.v17.leanback.widget.SearchBar.SearchBarListener);
     method public void setSearchQuery(java.lang.String);
     method public void setSpeechRecognitionCallback(android.support.v17.leanback.widget.SpeechRecognitionCallback);
@@ -3438,6 +3824,8 @@
     ctor public SpeechOrbView(android.content.Context);
     ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet);
     ctor public SpeechOrbView(android.content.Context, android.util.AttributeSet, int);
+    method public void setListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
+    method public void setNotListeningOrbColors(android.support.v17.leanback.widget.SearchOrbView.Colors);
     method public void setSoundLevel(int);
     method public void showListening();
     method public void showNotListening();
@@ -3649,6 +4037,7 @@
 
   public abstract class LeanbackPreferenceFragment extends android.support.v17.preference.BaseLeanbackPreferenceFragment {
     ctor public LeanbackPreferenceFragment();
+    method public void setTitle(java.lang.CharSequence);
   }
 
   public abstract class LeanbackSettingsFragment extends android.app.Fragment implements android.support.v14.preference.PreferenceFragment.OnPreferenceDisplayDialogCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartFragmentCallback android.support.v14.preference.PreferenceFragment.OnPreferenceStartScreenCallback {
@@ -3877,6 +4266,7 @@
     method public void onStop();
     method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void onViewStateRestored(android.os.Bundle);
+    method public void postponeEnterTransition();
     method public void registerForContextMenu(android.view.View);
     method public final void requestPermissions(java.lang.String[], int);
     method public void setAllowEnterTransitionOverlap(boolean);
@@ -3902,6 +4292,7 @@
     method public void startActivityForResult(android.content.Intent, int);
     method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startPostponedEnterTransition();
     method public void unregisterForContextMenu(android.view.View);
   }
 
@@ -4089,6 +4480,7 @@
     method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
     method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
     method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
+    method public abstract android.support.v4.app.FragmentTransaction setAllowOptimization(boolean);
     method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(int);
     method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbShortTitle(java.lang.CharSequence);
     method public abstract android.support.v4.app.FragmentTransaction setBreadCrumbTitle(int);
@@ -5306,9 +5698,11 @@
     method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem> getQueue();
     method public java.lang.CharSequence getQueueTitle();
     method public int getRatingType();
+    method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
     method public android.support.v4.media.session.MediaControllerCompat.TransportControls getTransportControls();
+    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 sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
@@ -5325,8 +5719,10 @@
     method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat);
     method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
+    method public void onRepeatModeChanged(int);
     method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
+    method public void onShuffleModeChanged(boolean);
   }
 
   public static final class MediaControllerCompat.PlaybackInfo {
@@ -5355,6 +5751,8 @@
     method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction, android.os.Bundle);
     method public abstract void sendCustomAction(java.lang.String, android.os.Bundle);
     method public abstract void setRating(android.support.v4.media.RatingCompat);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleModeEnabled(boolean);
     method public abstract void skipToNext();
     method public abstract void skipToPrevious();
     method public abstract void skipToQueueItem(long);
@@ -5388,7 +5786,9 @@
     method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
     method public void setRatingType(int);
+    method public void setRepeatMode(int);
     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_TRANSPORT_CONTROLS = 2; // 0x2
   }
@@ -5411,6 +5811,8 @@
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.support.v4.media.RatingCompat);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -5486,12 +5888,17 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat> CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
     field public static final int STATE_ERROR = 7; // 0x7
@@ -5575,6 +5982,7 @@
   public class BuildCompat {
     method public static boolean isAtLeastN();
     method public static boolean isAtLeastNMR1();
+    method public static boolean isAtLeastO();
   }
 
   public final class CancellationSignal {
@@ -5669,6 +6077,7 @@
     method public abstract boolean isDirectory();
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public abstract boolean isFile();
+    method public abstract boolean isVirtual();
     method public abstract long lastModified();
     method public abstract long length();
     method public abstract android.support.v4.provider.DocumentFile[] listFiles();
@@ -5765,6 +6174,31 @@
     method public java.util.Collection<V> values();
   }
 
+  public final class ArraySet<E> implements java.util.Collection java.util.Set {
+    ctor public ArraySet();
+    ctor public ArraySet(int);
+    ctor public ArraySet(android.support.v4.util.ArraySet<E>);
+    method public boolean add(E);
+    method public void addAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(java.lang.Object);
+    method public boolean containsAll(java.util.Collection<?>);
+    method public void ensureCapacity(int);
+    method public int indexOf(java.lang.Object);
+    method public boolean isEmpty();
+    method public java.util.Iterator<E> iterator();
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(android.support.v4.util.ArraySet<? extends E>);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public E removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public int size();
+    method public java.lang.Object[] toArray();
+    method public <T> T[] toArray(T[]);
+    method public E valueAt(int);
+  }
+
   public class AtomicFile {
     ctor public AtomicFile(java.io.File);
     method public void delete();
@@ -8331,6 +8765,7 @@
     method public void addCallback(android.support.v7.media.MediaRouteSelector, android.support.v7.media.MediaRouter.Callback, int);
     method public void addProvider(android.support.v7.media.MediaRouteProvider);
     method public void addRemoteControlClient(java.lang.Object);
+    method public android.support.v7.media.MediaRouter.RouteInfo getBluetoothRoute();
     method public android.support.v7.media.MediaRouter.RouteInfo getDefaultRoute();
     method public static android.support.v7.media.MediaRouter getInstance(android.content.Context);
     method public android.support.v4.media.session.MediaSessionCompat.Token getMediaSessionToken();
@@ -8404,8 +8839,10 @@
     method public int getVolume();
     method public int getVolumeHandling();
     method public int getVolumeMax();
+    method public boolean isBluetooth();
     method public boolean isConnecting();
     method public boolean isDefault();
+    method public boolean isDeviceSpeaker();
     method public boolean isEnabled();
     method public boolean isSelected();
     method public boolean matchesSelector(android.support.v7.media.MediaRouteSelector);
@@ -8778,6 +9215,7 @@
     method public android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener();
     method public android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener getOnNavigateToScreenListener();
     method public android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener getOnPreferenceTreeClickListener();
+    method public android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback getPreferenceComparisonCallback();
     method public android.support.v7.preference.PreferenceScreen getPreferenceScreen();
     method public android.content.SharedPreferences getSharedPreferences();
     method public int getSharedPreferencesMode();
@@ -8789,6 +9227,7 @@
     method public void setOnDisplayPreferenceDialogListener(android.support.v7.preference.PreferenceManager.OnDisplayPreferenceDialogListener);
     method public void setOnNavigateToScreenListener(android.support.v7.preference.PreferenceManager.OnNavigateToScreenListener);
     method public void setOnPreferenceTreeClickListener(android.support.v7.preference.PreferenceManager.OnPreferenceTreeClickListener);
+    method public void setPreferenceComparisonCallback(android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback);
     method public boolean setPreferences(android.support.v7.preference.PreferenceScreen);
     method public void setSharedPreferencesMode(int);
     method public void setSharedPreferencesName(java.lang.String);
@@ -8810,6 +9249,18 @@
     method public abstract boolean onPreferenceTreeClick(android.support.v7.preference.Preference);
   }
 
+  public static abstract class PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.PreferenceComparisonCallback();
+    method public abstract boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public abstract boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
+  public static class PreferenceManager.SimplePreferenceComparisonCallback extends android.support.v7.preference.PreferenceManager.PreferenceComparisonCallback {
+    ctor public PreferenceManager.SimplePreferenceComparisonCallback();
+    method public boolean arePreferenceContentsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+    method public boolean arePreferenceItemsTheSame(android.support.v7.preference.Preference, android.support.v7.preference.Preference);
+  }
+
   public final class PreferenceScreen extends android.support.v7.preference.PreferenceGroup {
     method public void setShouldUseGeneratedIds(boolean);
     method public boolean shouldUseGeneratedIds();
@@ -8823,6 +9274,21 @@
     method public void setDividerAllowedBelow(boolean);
   }
 
+  public class SeekBarPreference extends android.support.v7.preference.Preference {
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SeekBarPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SeekBarPreference(android.content.Context);
+    method public int getMax();
+    method public int getMin();
+    method public int getValue();
+    method public boolean isAdjustable();
+    method public void setAdjustable(boolean);
+    method public void setMax(int);
+    method public void setMin(int);
+    method public void setValue(int);
+  }
+
   public class SwitchPreferenceCompat extends android.support.v7.preference.TwoStatePreference {
     ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int, int);
     ctor public SwitchPreferenceCompat(android.content.Context, android.util.AttributeSet, int);
@@ -9463,6 +9929,13 @@
     field protected final android.support.v7.widget.RecyclerView.LayoutManager mLayoutManager;
   }
 
+  public class PagerSnapHelper extends android.support.v7.widget.SnapHelper {
+    ctor public PagerSnapHelper();
+    method public int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
+    method public android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
+    method public int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
+  }
+
   public class PopupMenu {
     ctor public PopupMenu(android.content.Context, android.view.View);
     ctor public PopupMenu(android.content.Context, android.view.View, int);
@@ -9564,6 +10037,7 @@
     method public void setScrollingTouchSlop(int);
     method public void setViewCacheExtension(android.support.v7.widget.RecyclerView.ViewCacheExtension);
     method public void smoothScrollBy(int, int);
+    method public void smoothScrollBy(int, int, android.view.animation.Interpolator);
     method public void smoothScrollToPosition(int);
     method public void stopScroll();
     method public void swapAdapter(android.support.v7.widget.RecyclerView.Adapter, boolean);
@@ -9880,6 +10354,7 @@
     ctor public RecyclerView.RecycledViewPool();
     method public void clear();
     method public android.support.v7.widget.RecyclerView.ViewHolder getRecycledView(int);
+    method public int getRecycledViewCount(int);
     method public void putRecycledView(android.support.v7.widget.RecyclerView.ViewHolder);
     method public void setMaxRecycledViews(int, int);
   }
@@ -10081,6 +10556,7 @@
     method public void attachToRecyclerView(android.support.v7.widget.RecyclerView) throws java.lang.IllegalStateException;
     method public abstract int[] calculateDistanceToFinalSnap(android.support.v7.widget.RecyclerView.LayoutManager, android.view.View);
     method public int[] calculateScrollDistance(int, int);
+    method protected android.support.v7.widget.LinearSmoothScroller createSnapScroller(android.support.v7.widget.RecyclerView.LayoutManager);
     method public abstract android.view.View findSnapView(android.support.v7.widget.RecyclerView.LayoutManager);
     method public abstract int findTargetSnapPosition(android.support.v7.widget.RecyclerView.LayoutManager, int, int);
     method public boolean onFling(int, int);
diff --git a/build.gradle b/build.gradle
index 952f187..e47de23 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,8 @@
         maven { url "../../prebuilts/maven_repo/android" }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.2.0'
+        // Keep gradle plugin version in sync with ub_supportlib-master manifest.
+        classpath 'com.android.tools.build:gradle:2.2.1'
     }
 }
 
@@ -30,7 +31,7 @@
     doclava project(':doclava')
 }
 
-ext.supportVersion = '25.0.1'
+ext.supportVersion = '25.1.0-SNAPSHOT'
 ext.extraVersion = 40
 ext.supportRepoOut = ''
 ext.buildToolsVersion = '23.0.2'
@@ -181,95 +182,6 @@
     files("${project.rootDir}/../../prebuilts/sdk/$apiLevel/android.jar")
 }
 
-/**
- * Populates the sub-project's set of source sets with the specified modules.
- *
- * @param subProject the sub-project to which the modules belong
- * @param apiModules the modules from which to populate
- */
-void createApiSourceSets(Project subProject, List<ApiModule> apiModules) {
-    subProject.ext._apiModules = apiModules
-    subProject.ext.allSS = []
-    if (gradle.ext.studioCompat.enableApiModules) {
-        // nothing to do, they are all modules
-        return
-    }
-    // create a jar task for the api specific internal implementations
-    def internalJar = subProject.tasks.create(name: "internalJar", type: Jar) {
-        baseName "internal_impl"
-    }
-    apiModules.each { ApiModule apiModule ->
-        apiModule.sourceSet = createApiSourceset(subProject, apiModule.folderName, apiModule.folderName,
-                apiModule.apiForSourceSet.toString(), apiModule.prev == null ? null : apiModule.prev.sourceSet)
-        subProject.ext.allSS.add(apiModule.sourceSet)
-    }
-    subProject.android.libraryVariants.all { variant ->
-        variant.javaCompile.dependsOn internalJar
-    }
-}
-
-/**
- * Adds the specified module to the sub-project's set of source sets and
- * internal JAR. Also sets up dependencies, if supplied.
- *
- * @param subProject the sub-project to which the module belongs
- * @param name the name of the module
- * @param folder the module's source folder
- * @param apiLevel the module's compile API level
- * @param previousSource source set dependency (optional)
- * @return a source set for the module
- */
-SourceSet createApiSourceset(Project subProject, String name, String folder, String apiLevel,
-                       SourceSet previousSource) {
-    def sourceSet = subProject.sourceSets.create(name)
-    sourceSet.java.srcDirs = [folder]
-
-    // The Android gradle plugin doesn't touch Java sub-tasks, so we need to
-    // manually set the Java task's boot classpath to the correct Android SDK.
-    def compileJavaTaskName = sourceSet.getCompileJavaTaskName();
-    def compileJavaOptions = subProject.tasks."${compileJavaTaskName}".options
-    compileJavaOptions.bootClasspath = getAndroidPrebuilt(apiLevel)
-
-    // Useful for cleaning up compiler warnings...
-    //compileJavaOptions.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
-
-    def configName = sourceSet.getCompileConfigurationName()
-    subProject.getDependencies().add(configName, getAndroidPrebuilt(apiLevel))
-    if (previousSource != null) {
-        setupDependencies(subProject, configName, previousSource)
-    }
-    subProject.ext.allSS.add(sourceSet)
-    subProject.tasks.internalJar.from sourceSet.output
-    return sourceSet
-}
-
-/**
- * Adds the specified source set as a dependency for the sub-project.
- *
- * @param subProject the sub-project to modify
- * @param configName
- * @param previousSourceSet the source set to add as a dependency
- */
-void setupDependencies(Project subProject, String configName, SourceSet previousSourceSet) {
-    subProject.getDependencies().add(configName, previousSourceSet.output)
-    subProject.getDependencies().add(configName, previousSourceSet.compileClasspath)
-}
-
-void setApiModuleDependencies(Project subProject, DependencyHandler handler, List extraDeps) {
-    if (gradle.ext.studioCompat.enableApiModules) {
-        subProject.android.enforceUniquePackageName=false
-        // add dependency on the latest module
-        handler.compile(project(subProject.ext._apiModules.last().moduleName))
-    } else {
-        handler.compile(files(subProject.tasks.internalJar.archivePath))
-        def firstModule = subProject.ext._apiModules[0]
-        extraDeps.each { dep ->
-            handler."${firstModule.folderName}Compile"(project(dep))
-            handler.compile(project(dep))
-        }
-    }
-}
-
 void registerForDocsTask(Task task, Project subProject, releaseVariant) {
     task.dependsOn releaseVariant.javaCompile
     task.source {
@@ -280,12 +192,6 @@
     }
     task.classpath += files(releaseVariant.javaCompile.classpath) +
             files(releaseVariant.javaCompile.destinationDir)
-
-    if (subProject.hasProperty('allSS')) {
-        subProject.allSS.each { ss ->
-            task.source ss.java
-        }
-    }
 }
 
 // Generates online docs.
@@ -368,8 +274,6 @@
     // current SDK is set in studioCompat.gradle
     project.ext.currentSdk = gradle.ext.currentSdk
     apply plugin: 'maven'
-    project.ext.createApiSourceSets = this.&createApiSourceset
-    project.ext.setApiModuleDependencies = this.&setApiModuleDependencies
 
     version = rootProject.ext.supportVersion
     group = 'com.android.support'
diff --git a/buildSrc/studioCompat.gradle b/buildSrc/studioCompat.gradle
index 1712bda..f845af1 100644
--- a/buildSrc/studioCompat.gradle
+++ b/buildSrc/studioCompat.gradle
@@ -44,7 +44,7 @@
                 new ApiModule("api21",21),
                 new ApiModule("api22",22),
                 new ApiModule("api23",23),
-                new ApiModule("api24", ApiModule.CURRENT)
+                new ApiModule("api24",24)
             ],
             dependencies : [":support-annotations"],
             folder : "compat",
@@ -58,7 +58,8 @@
                         new ApiModule("api21",21),
                         new ApiModule("api22",22),
                         new ApiModule("api23",23),
-                        new ApiModule("api24", ApiModule.CURRENT)
+                        new ApiModule("api24",24),
+                        new ApiModule("api25",ApiModule.CURRENT)
                 ],
                 dependencies : [":support-compat"],
                 folder : "media-compat",
@@ -73,7 +74,7 @@
                         new ApiModule("api20",20),
                         new ApiModule("api21",21),
                         new ApiModule("api23",23),
-                        new ApiModule("api24", ApiModule.CURRENT)
+                        new ApiModule("api24",24)
                 ],
                 dependencies : [":support-compat"],
                 folder : "core-utils",
@@ -118,7 +119,7 @@
                 new ApiModule("jellybean", 16),
                 new ApiModule("jellybean-mr1", 17),
                 new ApiModule("jellybean-mr2", 18),
-                new ApiModule("api24", ApiModule.CURRENT)
+                new ApiModule("api24",24)
             ],
             folder : "v7/mediarouter",
             moduleName : "support-mediarouter-v7"
diff --git a/compat/Android.mk b/compat/Android.mk
index 8fea612..ba5b958 100644
--- a/compat/Android.mk
+++ b/compat/Android.mk
@@ -14,181 +14,40 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb-mr1
-LOCAL_SDK_VERSION := 12
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-honeycomb-mr2
-LOCAL_SDK_VERSION := 13
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb_mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-honeycomb-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-ics-mr1
-LOCAL_SDK_VERSION := 15
-LOCAL_SRC_FILES := $(call all-java-files-under, ics-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-ics-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V20 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api20
-LOCAL_SDK_VERSION := 20
-LOCAL_SRC_FILES := $(call all-java-files-under, api20)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api20
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V22 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api22
-LOCAL_SDK_VERSION := 22
-LOCAL_SRC_FILES := $(call all-java-files-under, api22)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api22
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-compat-api24
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-compat-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-compat
-LOCAL_SDK_VERSION := 9
-LOCAL_AIDL_INCLUDES := frameworks/support/compat/java
-LOCAL_SRC_FILES := $(call all-java-files-under, java) \
-    $(call all-Iaidl-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,gingerbread) \
+    $(call all-java-files-under,honeycomb) \
+    $(call all-java-files-under,honeycomb_mr1) \
+    $(call all-java-files-under,honeycomb_mr2) \
+    $(call all-java-files-under,ics) \
+    $(call all-java-files-under,ics-mr1) \
+    $(call all-java-files-under,jellybean) \
+    $(call all-java-files-under,jellybean-mr1) \
+    $(call all-java-files-under,jellybean-mr2) \
+    $(call all-java-files-under,kitkat) \
+    $(call all-java-files-under,api20) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,api22) \
+    $(call all-java-files-under,api23) \
+    $(call all-java-files-under,api24) \
+    $(call all-java-files-under,java) \
+    $(call all-Iaidl-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-compat-api24
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/compat/api20/android/support/v4/app/NotificationCompatApi20.java b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
index 88ea703..8f4059b 100644
--- a/compat/api20/android/support/v4/app/NotificationCompatApi20.java
+++ b/compat/api20/android/support/v4/app/NotificationCompatApi20.java
@@ -23,10 +23,14 @@
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 
+@RequiresApi(20)
+@TargetApi(20)
 class NotificationCompatApi20 {
     public static class Builder implements NotificationBuilderWithBuilderAccessor,
             NotificationBuilderWithActions {
diff --git a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
index 5f302f1..2949cfd 100644
--- a/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
+++ b/compat/api20/android/support/v4/app/RemoteInputCompatApi20.java
@@ -19,7 +19,11 @@
 import android.app.RemoteInput;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(20)
+@TargetApi(20)
 class RemoteInputCompatApi20 {
     static RemoteInputCompatBase.RemoteInput[] toCompat(RemoteInput[] srcArray,
             RemoteInputCompatBase.RemoteInput.Factory factory) {
diff --git a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java b/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
index e29f6b0..617920c 100644
--- a/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
+++ b/compat/api20/android/support/v4/view/WindowInsetsCompatApi20.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.WindowInsets;
 
+@RequiresApi(20)
+@TargetApi(20)
 class WindowInsetsCompatApi20 {
     public static Object consumeSystemWindowInsets(Object insets) {
         return ((WindowInsets) insets).consumeSystemWindowInsets();
diff --git a/compat/api21/android/support/v4/app/ActivityCompatApi21.java b/compat/api21/android/support/v4/app/ActivityCompatApi21.java
index 51888d4..248b0aa 100644
--- a/compat/api21/android/support/v4/app/ActivityCompatApi21.java
+++ b/compat/api21/android/support/v4/app/ActivityCompatApi21.java
@@ -23,13 +23,15 @@
 import android.graphics.RectF;
 import android.media.session.MediaController;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
-import java.lang.Override;
-import java.lang.String;
 import java.util.List;
 import java.util.Map;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ActivityCompatApi21 {
 
     public static void setMediaController(Activity activity, Object mediaControllerObj) {
diff --git a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java b/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
index 85673bd..16287d2 100644
--- a/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
+++ b/compat/api21/android/support/v4/app/ActivityOptionsCompat21.java
@@ -16,14 +16,18 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.util.Pair;
 import android.view.View;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ActivityOptionsCompat21 {
 
     private final ActivityOptions mActivityOptions;
diff --git a/compat/api21/android/support/v4/app/NotificationCompatApi21.java b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
index f060486..feeb044 100644
--- a/compat/api21/android/support/v4/app/NotificationCompatApi21.java
+++ b/compat/api21/android/support/v4/app/NotificationCompatApi21.java
@@ -22,10 +22,14 @@
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 
+@RequiresApi(21)
+@TargetApi(21)
 class NotificationCompatApi21 {
 
     public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
diff --git a/compat/api21/android/support/v4/content/ContextCompatApi21.java b/compat/api21/android/support/v4/content/ContextCompatApi21.java
index cf93924..97a0b37 100644
--- a/compat/api21/android/support/v4/content/ContextCompatApi21.java
+++ b/compat/api21/android/support/v4/content/ContextCompatApi21.java
@@ -18,9 +18,13 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.File;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ContextCompatApi21 {
     public static Drawable getDrawable(Context context, int id) {
         return context.getDrawable(id);
diff --git a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java b/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
index 2272c02..f08dbe1 100644
--- a/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
+++ b/compat/api21/android/support/v4/content/res/ResourcesCompatApi21.java
@@ -20,7 +20,11 @@
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ResourcesCompatApi21 {
     public static Drawable getDrawable(Resources res, int id, Theme theme)
             throws NotFoundException {
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
index e43ed95..a5e8650 100644
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
+++ b/compat/api21/android/support/v4/graphics/drawable/DrawableCompatLollipop.java
@@ -23,6 +23,8 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
 import android.graphics.drawable.InsetDrawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.AttributeSet;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -33,6 +35,9 @@
 /**
  * Implementation of drawable compatibility that can call L APIs.
  */
+
+@RequiresApi(21)
+@TargetApi(21)
 class DrawableCompatLollipop {
 
     public static void setHotspot(Drawable drawable, float x, float y) {
diff --git a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
index ea69677..9458f7b 100644
--- a/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
+++ b/compat/api21/android/support/v4/graphics/drawable/DrawableWrapperLollipop.java
@@ -28,7 +28,11 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(21)
+@TargetApi(21)
 class DrawableWrapperLollipop extends DrawableWrapperKitKat {
 
     DrawableWrapperLollipop(Drawable drawable) {
diff --git a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java b/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
index b62c74e..7fae8a8 100644
--- a/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/LayoutInflaterCompatLollipop.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.LayoutInflater;
 
+@RequiresApi(21)
+@TargetApi(21)
 class LayoutInflaterCompatLollipop {
     static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
         inflater.setFactory2(factory != null
diff --git a/compat/api21/android/support/v4/view/ViewCompatLollipop.java b/compat/api21/android/support/v4/view/ViewCompatLollipop.java
index 478e37e..26c462a 100644
--- a/compat/api21/android/support/v4/view/ViewCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewCompatLollipop.java
@@ -21,10 +21,14 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.WindowInsets;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ViewCompatLollipop {
 
     public interface OnApplyWindowInsetsListenerBridge {
diff --git a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
index 1a62404..03430e6 100644
--- a/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewGroupCompatLollipop.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ViewGroup;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ViewGroupCompatLollipop {
 
     public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
diff --git a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
index 7dbcf61..1e65a09 100644
--- a/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewParentCompatLollipop.java
@@ -17,10 +17,14 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ViewParentCompatLollipop {
     private static final String TAG = "ViewParentCompat";
 
diff --git a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java b/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
index 3bfc427..2b979a9 100644
--- a/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
+++ b/compat/api21/android/support/v4/view/ViewPropertyAnimatorCompatLollipop.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ViewPropertyAnimatorCompatLollipop {
 
     public static void translationZ(View view, float value) {
diff --git a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java b/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
index 3fcbfed..5bbb802 100644
--- a/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
+++ b/compat/api21/android/support/v4/view/WindowInsetsCompatApi21.java
@@ -17,8 +17,12 @@
 package android.support.v4.view;
 
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.WindowInsets;
 
+@RequiresApi(21)
+@TargetApi(21)
 class WindowInsetsCompatApi21 {
     public static Object consumeStableInsets(Object insets) {
         return ((WindowInsets) insets).consumeStableInsets();
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
index 0ac8e9d..e24b873 100644
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
+++ b/compat/api21/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi21.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -25,6 +27,9 @@
 /**
  * Api21-specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(21)
+@TargetApi(21)
 class AccessibilityNodeInfoCompatApi21 {
     static List<Object> getActionList(Object info) {
         Object result = ((AccessibilityNodeInfo) info).getActionList();
diff --git a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java b/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
index c166530..23fd7ca 100644
--- a/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
+++ b/compat/api21/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi21.java
@@ -17,11 +17,16 @@
 package android.support.v4.view.accessibility;
 
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityWindowInfo;
 
 /**
  * Api21-specific AccessibilityWindowInfo API implementation.
  */
+
+@RequiresApi(21)
+@TargetApi(21)
 class AccessibilityWindowInfoCompatApi21 {
 
     public static Object obtain() {
diff --git a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java b/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
index 3767f27..835e4e0 100644
--- a/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
+++ b/compat/api21/android/support/v4/view/animation/PathInterpolatorCompatApi21.java
@@ -17,12 +17,17 @@
 package android.support.v4.view.animation;
 
 import android.graphics.Path;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
 /**
  * API 21+ implementation for path interpolator compatibility.
  */
+
+@RequiresApi(21)
+@TargetApi(21)
 class PathInterpolatorCompatApi21  {
 
     private PathInterpolatorCompatApi21() {
diff --git a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java b/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
index 17f4fdb..42aa89a 100644
--- a/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
+++ b/compat/api21/android/support/v4/widget/CompoundButtonCompatLollipop.java
@@ -18,8 +18,12 @@
 
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.CompoundButton;
 
+@RequiresApi(21)
+@TargetApi(21)
 class CompoundButtonCompatLollipop {
 
     static void setButtonTintList(CompoundButton button, ColorStateList tint) {
diff --git a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
index 6ba379c..f12bc23 100644
--- a/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
+++ b/compat/api21/android/support/v4/widget/EdgeEffectCompatLollipop.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.EdgeEffect;
 
+@RequiresApi(21)
+@TargetApi(21)
 class EdgeEffectCompatLollipop {
     public static boolean onPull(Object edgeEffect, float deltaDistance, float displacement) {
         ((EdgeEffect) edgeEffect).onPull(deltaDistance, displacement);
diff --git a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
index 3440f3c..393efa6 100644
--- a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
+++ b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 import android.widget.PopupWindow;
 
 import java.lang.reflect.Field;
 
+@RequiresApi(21)
+@TargetApi(21)
 class PopupWindowCompatApi21 {
 
     private static final String TAG = "PopupWindowCompatApi21";
diff --git a/compat/api22/android/support/v4/app/ActivityCompatApi22.java b/compat/api22/android/support/v4/app/ActivityCompatApi22.java
index c56d490..1efef64 100644
--- a/compat/api22/android/support/v4/app/ActivityCompatApi22.java
+++ b/compat/api22/android/support/v4/app/ActivityCompatApi22.java
@@ -18,7 +18,11 @@
 
 import android.app.Activity;
 import android.net.Uri;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(22)
+@TargetApi(22)
 class ActivityCompatApi22 {
     public static Uri getReferrer(Activity activity) {
         return activity.getReferrer();
diff --git a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java b/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
index 786318d..dd482d4 100644
--- a/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
+++ b/compat/api22/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi22.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view.accessibility;
 
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * Api22-specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(22)
+@TargetApi(22)
 class AccessibilityNodeInfoCompatApi22 {
 
     public static Object getTraversalBefore(Object info) {
diff --git a/compat/api23/android/support/v4/app/ActivityCompatApi23.java b/compat/api23/android/support/v4/app/ActivityCompatApi23.java
index ab46058..9012f56 100644
--- a/compat/api23/android/support/v4/app/ActivityCompatApi23.java
+++ b/compat/api23/android/support/v4/app/ActivityCompatApi23.java
@@ -22,11 +22,15 @@
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
 import java.util.List;
 import java.util.Map;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ActivityCompatApi23 {
     public interface OnSharedElementsReadyListenerBridge {
         void onSharedElementsReady();
diff --git a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java b/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
index b33fb11..81be941 100644
--- a/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
+++ b/compat/api23/android/support/v4/app/ActivityOptionsCompat23.java
@@ -22,9 +22,13 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Pair;
 import android.view.View;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ActivityOptionsCompat23 {
 
     private final ActivityOptions mActivityOptions;
diff --git a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java b/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
index 72e07bf..853fd5d 100644
--- a/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
+++ b/compat/api23/android/support/v4/app/AppOpsManagerCompat23.java
@@ -18,10 +18,15 @@
 
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * AppOpsManager implementations for API 23.
  */
+
+@RequiresApi(23)
+@TargetApi(23)
 class AppOpsManagerCompat23 {
     public static String permissionToOp(String permission) {
         return AppOpsManager.permissionToOp(permission);
diff --git a/compat/api23/android/support/v4/app/NotificationCompatApi23.java b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
index 61173d1..5262ef3 100644
--- a/compat/api23/android/support/v4/app/NotificationCompatApi23.java
+++ b/compat/api23/android/support/v4/app/NotificationCompatApi23.java
@@ -17,7 +17,11 @@
 package android.support.v4.app;
 
 import android.app.Notification;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class NotificationCompatApi23 {
 
     public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
diff --git a/compat/api23/android/support/v4/content/ContextCompatApi23.java b/compat/api23/android/support/v4/content/ContextCompatApi23.java
index 64f1c15..c22f5b6 100644
--- a/compat/api23/android/support/v4/content/ContextCompatApi23.java
+++ b/compat/api23/android/support/v4/content/ContextCompatApi23.java
@@ -18,10 +18,11 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
-import java.io.File;
-
+@RequiresApi(23)
+@TargetApi(23)
 class ContextCompatApi23 {
     public static ColorStateList getColorStateList(Context context, int id) {
         return context.getColorStateList(id);
diff --git a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java b/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
index c44f9ce..eade1ef 100644
--- a/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
+++ b/compat/api23/android/support/v4/content/res/ResourcesCompatApi23.java
@@ -20,7 +20,11 @@
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ResourcesCompatApi23 {
     public static int getColor(Resources res, int id, Theme theme) throws NotFoundException {
         return res.getColor(id, theme);
diff --git a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java b/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
index 2a41b60..e454d41 100644
--- a/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
+++ b/compat/api23/android/support/v4/graphics/drawable/DrawableCompatApi23.java
@@ -17,10 +17,15 @@
 package android.support.v4.graphics.drawable;
 
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of drawable compatibility that can call M APIs.
  */
+
+@RequiresApi(23)
+@TargetApi(23)
 class DrawableCompatApi23 {
     public static boolean setLayoutDirection(Drawable drawable, int layoutDirection) {
         return drawable.setLayoutDirection(layoutDirection);
diff --git a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
index 801b8ea..7143850 100644
--- a/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
+++ b/compat/api23/android/support/v4/hardware/fingerprint/FingerprintManagerCompatApi23.java
@@ -16,9 +16,11 @@
 
 package android.support.v4.hardware.fingerprint;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
 import java.security.Signature;
@@ -32,6 +34,8 @@
  * Actual FingerprintManagerCompat implementation for API level 23 and later.
  * @hide
  */
+@RequiresApi(23)
+@TargetApi(23)
 @RestrictTo(GROUP_ID)
 public final class FingerprintManagerCompatApi23 {
 
diff --git a/compat/api23/android/support/v4/text/ICUCompatApi23.java b/compat/api23/android/support/v4/text/ICUCompatApi23.java
index 1a3c54f..182c6f3 100644
--- a/compat/api23/android/support/v4/text/ICUCompatApi23.java
+++ b/compat/api23/android/support/v4/text/ICUCompatApi23.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.text;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Locale;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ICUCompatApi23 {
 
     private static final String TAG = "ICUCompatIcs";
diff --git a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java b/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
index 38fa479..30645ec 100644
--- a/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
+++ b/compat/api23/android/support/v4/view/ViewCompatMarshmallow.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ViewCompatMarshmallow {
     public static void setScrollIndicators(View view, int indicators) {
         view.setScrollIndicators(indicators);
diff --git a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java b/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
index e6175c9..457cd39 100644
--- a/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
+++ b/compat/api23/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi23.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+@RequiresApi(23)
+@TargetApi(23)
 class AccessibilityNodeInfoCompatApi23 {
     public static Object getActionScrollToPosition() {
         return AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION;
diff --git a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java b/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
index 0c55bbc..6dddbdb 100644
--- a/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/CompoundButtonCompatApi23.java
@@ -16,12 +16,13 @@
 
 package android.support.v4.widget;
 
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.CompoundButton;
 
+@RequiresApi(23)
+@TargetApi(23)
 class CompoundButtonCompatApi23 {
 
     static Drawable getButtonDrawable(CompoundButton button) {
diff --git a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
index 96bf8d9..1483e41 100644
--- a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.PopupWindow;
 
+@RequiresApi(23)
+@TargetApi(23)
 class PopupWindowCompatApi23 {
 
     static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
diff --git a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java b/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
index ad21409..f31242b 100644
--- a/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
+++ b/compat/api23/android/support/v4/widget/TextViewCompatApi23.java
@@ -16,11 +16,14 @@
 
 package android.support.v4.widget;
 
-import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.support.annotation.StyleRes;
 import android.widget.TextView;
 
+@RequiresApi(23)
+@TargetApi(23)
 class TextViewCompatApi23 {
     public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
         textView.setTextAppearance(resId);
diff --git a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java b/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
index 4d166e4..d33d8a2 100644
--- a/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
+++ b/compat/api24/android/support/v4/app/ActivityOptionsCompat24.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
@@ -24,9 +25,12 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.util.Pair;
 import android.view.View;
 
+@RequiresApi(24)
+@TargetApi(24)
 class ActivityOptionsCompat24 {
 
     public static ActivityOptionsCompat24 makeCustomAnimation(Context context,
diff --git a/compat/api24/android/support/v4/app/NotificationCompatApi24.java b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
index a631f23..6a29d89 100644
--- a/compat/api24/android/support/v4/app/NotificationCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationCompatApi24.java
@@ -23,11 +23,15 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(24)
+@TargetApi(24)
 class NotificationCompatApi24 {
 
     public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
diff --git a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java b/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
index b8f599f..468592f 100644
--- a/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/app/NotificationManagerCompatApi24.java
@@ -16,7 +16,11 @@
 package android.support.v4.app;
 
 import android.app.NotificationManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(24)
+@TargetApi(24)
 class NotificationManagerCompatApi24 {
     public static boolean areNotificationsEnabled(NotificationManager notificationManager) {
         return notificationManager.areNotificationsEnabled();
diff --git a/compat/api24/android/support/v4/app/ServiceCompatApi24.java b/compat/api24/android/support/v4/app/ServiceCompatApi24.java
index ad5296d..29b6112 100644
--- a/compat/api24/android/support/v4/app/ServiceCompatApi24.java
+++ b/compat/api24/android/support/v4/app/ServiceCompatApi24.java
@@ -16,7 +16,11 @@
 package android.support.v4.app;
 
 import android.app.Service;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(24)
+@TargetApi(24)
 class ServiceCompatApi24 {
     public static void stopForeground(Service service, int flags) {
         service.stopForeground(flags);
diff --git a/compat/api24/android/support/v4/content/ContextCompatApi24.java b/compat/api24/android/support/v4/content/ContextCompatApi24.java
index e94d702..a65f21b 100644
--- a/compat/api24/android/support/v4/content/ContextCompatApi24.java
+++ b/compat/api24/android/support/v4/content/ContextCompatApi24.java
@@ -17,9 +17,13 @@
 package android.support.v4.content;
 
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.File;
 
+@RequiresApi(24)
+@TargetApi(24)
 class ContextCompatApi24 {
     public static File getDataDir(Context context) {
         return context.getDataDir();
diff --git a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java b/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
index 5e0edf9..b6e86cb 100644
--- a/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/net/ConnectivityManagerCompatApi24.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.net;
 
+import android.annotation.TargetApi;
 import android.net.ConnectivityManager;
+import android.support.annotation.RequiresApi;
 
 /**
  * Implementation of ConnectivityManagerCompat that can use API 24 APIs.
  */
+@RequiresApi(24)
+@TargetApi(24)
 class ConnectivityManagerCompatApi24 {
     public static int getRestrictBackgroundStatus(ConnectivityManager cm) {
         return cm.getRestrictBackgroundStatus();
diff --git a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java b/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
index 02a9699..b2ebec4 100644
--- a/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
+++ b/compat/api24/android/support/v4/net/TrafficStatsCompatApi24.java
@@ -16,7 +16,9 @@
 
 package android.support.v4.net;
 
+import android.annotation.TargetApi;
 import android.net.TrafficStats;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
 import java.net.DatagramSocket;
@@ -24,7 +26,9 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
-/** {@hide} */
+/** @hide */
+@RequiresApi(24)
+@TargetApi(24)
 @RestrictTo(GROUP_ID)
 public class TrafficStatsCompatApi24 {
     public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
diff --git a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
index e5f7a7b..0d96ae0 100644
--- a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
+++ b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
@@ -16,13 +16,17 @@
 
 package android.support.v4.os;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.UserManager;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
-/** {@hide} */
+/** @hide */
+@RequiresApi(24)
+@TargetApi(24)
 @RestrictTo(GROUP_ID)
 public class UserManagerCompatApi24 {
     public static boolean isUserUnlocked(Context context) {
diff --git a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java b/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
index d8c7ff0..424af92 100644
--- a/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
+++ b/compat/api24/android/support/v4/view/PointerIconCompatApi24.java
@@ -19,8 +19,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.PointerIcon;
 
+@RequiresApi(24)
+@TargetApi(24)
 class PointerIconCompatApi24 {
     public static Object getSystemIcon(Context context, int style) {
         return PointerIcon.getSystemIcon(context, style);
diff --git a/compat/api24/android/support/v4/view/ViewCompatApi24.java b/compat/api24/android/support/v4/view/ViewCompatApi24.java
index 517f3cc..71366a8 100644
--- a/compat/api24/android/support/v4/view/ViewCompatApi24.java
+++ b/compat/api24/android/support/v4/view/ViewCompatApi24.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.PointerIcon;
 import android.view.View;
 
+@RequiresApi(24)
+@TargetApi(24)
 class ViewCompatApi24 {
     public static void setPointerIcon(View view, Object pointerIcon) {
         view.setPointerIcon((PointerIcon)pointerIcon);
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
index 14fe273..5e64091 100644
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
+++ b/compat/api24/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatApi24.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * Api24-specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(24)
+@TargetApi(24)
 class AccessibilityNodeInfoCompatApi24 {
     public static Object getActionSetProgress() {
         return AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS;
diff --git a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java b/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
index 34ba542..c8aa21a 100644
--- a/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
+++ b/compat/api24/android/support/v4/view/accessibility/AccessibilityWindowInfoCompatApi24.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityWindowInfo;
 
 /**
  * Api24-specific AccessibilityWindowInfo API implementation.
  */
+
+@RequiresApi(24)
+@TargetApi(24)
 class AccessibilityWindowInfoCompatApi24 {
     public static CharSequence getTitle(Object info) {
         return ((AccessibilityWindowInfo) info).getTitle();
diff --git a/compat/build.gradle b/compat/build.gradle
index cd9e693..2f43b54 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -1,9 +1,8 @@
 apply plugin: 'com.android.library'
 archivesBaseName = 'support-compat'
 
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.compat.apiTargets)
 dependencies {
+    compile project(':support-annotations')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
     }
@@ -18,22 +17,35 @@
 
 sourceCompatibility = JavaVersion.VERSION_1_7
 targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.compat.dependencies)
 
 android {
-    compileSdkVersion 9
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
-
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
+        main.java.srcDirs = [
+                'gingerbread',
+                'honeycomb',
+                'honeycomb_mr1',
+                'honeycomb_mr2',
+                'ics',
+                'ics-mr1',
+                'jellybean',
+                'jellybean-mr1',
+                'jellybean-mr2',
+                'kitkat',
+                'api20',
+                'api21',
+                'api22',
+                'api23',
+                'api24',
+                'java'
+        ]
         main.aidl.srcDirs = ['java']
 
         androidTest.setRoot('tests')
@@ -89,11 +101,6 @@
         exclude('android/service/media/**')
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
index 83e2d58..83ba12a 100644
--- a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
+++ b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.animation;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
 import java.util.ArrayList;
@@ -26,6 +28,9 @@
  * <p>
  * This is not a fully implemented API which is why it is not public.
  */
+
+@RequiresApi(9)
+@TargetApi(9)
 class GingerbreadAnimatorCompatProvider implements AnimatorProvider {
 
     @Override
diff --git a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java b/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
index b232baa..f7656be 100644
--- a/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/app/BundleCompatGingerbread.java
@@ -18,11 +18,15 @@
 
 import android.os.Bundle;
 import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+@RequiresApi(9)
+@TargetApi(9)
 class BundleCompatGingerbread {
     private static final String TAG = "BundleCompatGingerbread";
 
diff --git a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java b/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
index e4d0f2b..5f1d8fb 100644
--- a/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
+++ b/compat/gingerbread/android/support/v4/app/NotificationCompatBase.java
@@ -16,19 +16,27 @@
 
 package android.support.v4.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 
 /**
  * @hide
  */
 @RestrictTo(GROUP_ID)
+@RequiresApi(9)
+@TargetApi(9)
 public class NotificationCompatBase {
+    private static Method sSetLatestEventInfo;
 
     public static abstract class Action {
         public abstract int getIcon();
@@ -66,7 +74,26 @@
     public static Notification add(Notification notification, Context context,
             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent,
             PendingIntent fullScreenIntent) {
-        notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
+        if (sSetLatestEventInfo == null) {
+            try {
+                sSetLatestEventInfo = Notification.class.getMethod("setLatestEventInfo",
+                        Context.class, CharSequence.class, CharSequence.class, PendingIntent.class);
+            } catch (NoSuchMethodException e) {
+                // This method was @removed, so it must exist on later
+                // versions even if it's not in public API.
+                throw new RuntimeException(e);
+            }
+        }
+
+        try {
+            sSetLatestEventInfo.invoke(notification, context,
+                    contentTitle, contentText, contentIntent);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            // This method was @removed, so it must be invokable on later
+            // versions even if it's not in public API.
+            throw new RuntimeException(e);
+        }
+
         notification.fullScreenIntent = fullScreenIntent;
         return notification;
     }
diff --git a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java b/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
index 2449336..85117dd 100644
--- a/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
+++ b/compat/gingerbread/android/support/v4/app/RemoteInputCompatBase.java
@@ -17,7 +17,11 @@
 package android.support.v4.app;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(9)
+@TargetApi(9)
 class RemoteInputCompatBase {
 
     public static abstract class RemoteInput {
diff --git a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java b/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
index 1a18404..6667431 100644
--- a/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
+++ b/compat/gingerbread/android/support/v4/content/res/ConfigurationHelperGingerbread.java
@@ -16,11 +16,14 @@
 
 package android.support.v4.content.res;
 
-import android.content.Context;
 import android.content.res.Resources;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.DisplayMetrics;
 
+@RequiresApi(9)
+@TargetApi(9)
 class ConfigurationHelperGingerbread {
 
     static int getScreenHeightDp(@NonNull Resources resources) {
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
index 9645df2..8e5cd9f 100644
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
+++ b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableCompatBase.java
@@ -20,7 +20,10 @@
 import android.content.res.Resources;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.AttributeSet;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -29,6 +32,9 @@
 /**
  * Base implementation of drawable compatibility.
  */
+
+@RequiresApi(9)
+@TargetApi(9)
 class DrawableCompatBase {
 
     public static void setTint(Drawable drawable, int tint) {
diff --git a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
index ccaebf7..646c677 100644
--- a/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
+++ b/compat/gingerbread/android/support/v4/graphics/drawable/DrawableWrapperGingerbread.java
@@ -26,13 +26,18 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
- * Drawable which delegates all calls to it's wrapped {@link android.graphics.drawable.Drawable}.
- * <p>
+ * Drawable which delegates all calls to it's wrapped {@link Drawable}.
+ * <p/>
  * Also allows backward compatible tinting via a color or {@link ColorStateList}.
  * This functionality is accessed via static methods in {@code DrawableCompat}.
  */
+
+@RequiresApi(9)
+@TargetApi(9)
 class DrawableWrapperGingerbread extends Drawable
         implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
 
diff --git a/compat/gingerbread/android/support/v4/os/BuildCompat.java b/compat/gingerbread/android/support/v4/os/BuildCompat.java
index 3c73e4d..d406caf 100644
--- a/compat/gingerbread/android/support/v4/os/BuildCompat.java
+++ b/compat/gingerbread/android/support/v4/os/BuildCompat.java
@@ -45,4 +45,14 @@
     public static boolean isAtLeastNMR1() {
         return VERSION.SDK_INT >= 25;
     }
+
+    /**
+     * Check if the device is running on the Android O release or newer.
+     *
+     * @return {@code true} if O APIs are available for use
+     */
+    public static boolean isAtLeastO() {
+        return !"REL".equals(VERSION.CODENAME)
+                && "O".compareTo(VERSION.CODENAME) <= 0;
+    }
 }
diff --git a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java b/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
index c85235c..5d97d04 100644
--- a/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/LayoutInflaterCompatBase.java
@@ -17,10 +17,14 @@
 package android.support.v4.view;
 
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 
+@RequiresApi(9)
+@TargetApi(9)
 class LayoutInflaterCompatBase {
 
     static class FactoryWrapper implements LayoutInflater.Factory {
diff --git a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java b/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
index e7882bd..7c81f5e 100644
--- a/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/ViewCompatBase.java
@@ -16,9 +16,11 @@
 
 package android.support.v4.view;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
+import android.support.annotation.RequiresApi;
 import android.view.Display;
 import android.view.View;
 import android.view.ViewParent;
@@ -26,6 +28,8 @@
 
 import java.lang.reflect.Field;
 
+@RequiresApi(9)
+@TargetApi(9)
 class ViewCompatBase {
 
     private static final String TAG = "ViewCompatBase";
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
index 9857632..5f3e253 100644
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
+++ b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorCompatBase.java
@@ -17,11 +17,16 @@
 package android.support.v4.view.animation;
 
 import android.graphics.Path;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.animation.Interpolator;
 
 /**
  * Base implementation for path interpolator compatibility.
  */
+
+@RequiresApi(9)
+@TargetApi(9)
 class PathInterpolatorCompatBase  {
 
     private PathInterpolatorCompatBase() {
diff --git a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
index b95fc04..4c96b97 100644
--- a/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
+++ b/compat/gingerbread/android/support/v4/view/animation/PathInterpolatorGingerbread.java
@@ -18,11 +18,16 @@
 
 import android.graphics.Path;
 import android.graphics.PathMeasure;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.animation.Interpolator;
 
 /**
  * A path interpolator implementation compatible with API 9+.
  */
+
+@RequiresApi(9)
+@TargetApi(9)
 class PathInterpolatorGingerbread implements Interpolator {
 
     /**
diff --git a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
index 6e3e0e4..0fe01f0 100644
--- a/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/widget/CompoundButtonCompatGingerbread.java
@@ -19,11 +19,15 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 import android.widget.CompoundButton;
 
 import java.lang.reflect.Field;
 
+@RequiresApi(9)
+@TargetApi(9)
 class CompoundButtonCompatGingerbread {
 
     private static final String TAG = "CompoundButtonCompatGingerbread";
diff --git a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java b/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
index 14d27b0..79edf2c 100644
--- a/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
+++ b/compat/gingerbread/android/support/v4/widget/ListViewCompatGingerbread.java
@@ -16,10 +16,13 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
-import android.widget.AbsListView;
 import android.widget.ListView;
 
+@RequiresApi(9)
+@TargetApi(9)
 class ListViewCompatGingerbread {
     static void scrollListBy(final ListView listView, int y) {
         final int firstPosition = listView.getFirstVisiblePosition();
diff --git a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
index 615bc44..e5f3bbf 100644
--- a/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/app/ActivityCompatHoneycomb.java
@@ -17,6 +17,8 @@
 package android.support.v4.app;
 
 import android.app.Activity;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -24,6 +26,9 @@
 /**
  * Implementation of activity compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class ActivityCompatHoneycomb {
     static void invalidateOptionsMenu(Activity activity) {
         activity.invalidateOptionsMenu();
diff --git a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java b/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
index 49495b2..2d364e1 100644
--- a/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
+++ b/compat/honeycomb/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
@@ -16,7 +16,9 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
@@ -28,6 +30,8 @@
  *
  * @hide
  */
+@RequiresApi(11)
+@TargetApi(11)
 @RestrictTo(GROUP_ID)
 public interface NotificationBuilderWithBuilderAccessor {
     public Notification.Builder getBuilder();
diff --git a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java b/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
index 3048e91..44ef21a 100644
--- a/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/app/NotificationCompatHoneycomb.java
@@ -20,8 +20,12 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
+@RequiresApi(11)
+@TargetApi(11)
 class NotificationCompatHoneycomb {
     static Notification add(Context context, Notification n,
             CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
diff --git a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
index 9abc97d..6b804ad 100644
--- a/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/ContextCompatHoneycomb.java
@@ -18,12 +18,17 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.File;
 
 /**
  * Implementation of context compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class ContextCompatHoneycomb {
 
     static void startActivities(Context context, Intent[] intents) {
diff --git a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
index f0a9b66..0dcd0d7 100644
--- a/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/ExecutorCompatHoneycomb.java
@@ -17,12 +17,17 @@
 package android.support.v4.content;
 
 import android.os.AsyncTask;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.util.concurrent.Executor;
 
 /**
  * Implementation of parallel executor compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class ExecutorCompatHoneycomb {
     public static Executor getParallelExecutor() {
         return AsyncTask.THREAD_POOL_EXECUTOR;
diff --git a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java b/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
index 6356898..81ada48 100644
--- a/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/content/IntentCompatHoneycomb.java
@@ -18,7 +18,11 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(11)
+@TargetApi(11)
 class IntentCompatHoneycomb {
     public static Intent makeMainActivity(ComponentName mainActivity) {
         return Intent.makeMainActivity(mainActivity);
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
index 08066a4..e19f8a8 100644
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableCompatHoneycomb.java
@@ -16,13 +16,16 @@
 
 package android.support.v4.graphics.drawable;
 
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of drawable compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class DrawableCompatHoneycomb {
 
     public static void jumpToCurrentState(Drawable drawable) {
diff --git a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
index 4c494dd..1bd6355 100644
--- a/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/graphics/drawable/DrawableWrapperHoneycomb.java
@@ -20,7 +20,11 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(11)
+@TargetApi(11)
 class DrawableWrapperHoneycomb extends DrawableWrapperGingerbread {
 
     DrawableWrapperHoneycomb(Drawable drawable) {
diff --git a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java b/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
index 5b05a0e..1b3836e 100644
--- a/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/os/AsyncTaskCompatHoneycomb.java
@@ -17,10 +17,15 @@
 package android.support.v4.os;
 
 import android.os.AsyncTask;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of AsyncTask compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class AsyncTaskCompatHoneycomb {
 
     static <Params, Progress, Result> void executeParallel(
diff --git a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
index 6cd185b..80425d8 100644
--- a/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/KeyEventCompatHoneycomb.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.KeyEvent;
 
 /**
  * Implementation of key event compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class KeyEventCompatHoneycomb {
     public static int normalizeMetaState(int metaState) {
         return KeyEvent.normalizeMetaState(metaState);
diff --git a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java b/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
index 06e72f4..7eea934 100644
--- a/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/LayoutInflaterCompatHC.java
@@ -17,14 +17,17 @@
 package android.support.v4.view;
 
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import java.lang.reflect.Field;
-import java.util.ArrayList;
 
+@RequiresApi(11)
+@TargetApi(11)
 class LayoutInflaterCompatHC {
     private static final String TAG = "LayoutInflaterCompatHC";
 
diff --git a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
index 1a0e513..0b267d2 100644
--- a/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/MenuItemCompatHoneycomb.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.MenuItem;
 import android.view.View;
 
 /**
  * Implementation of menu compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class MenuItemCompatHoneycomb {
     public static void setShowAsAction(MenuItem item, int actionEnum) {
         item.setShowAsAction(actionEnum);
diff --git a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java b/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
index 4f9d326..189dc03 100644
--- a/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/view/VelocityTrackerCompatHoneycomb.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.VelocityTracker;
 
 /**
  * Implementation of velocity tracker compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class VelocityTrackerCompatHoneycomb {
     public static float getXVelocity(VelocityTracker tracker, int pointerId) {
         return tracker.getXVelocity(pointerId);
diff --git a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
index 5d0467c..607175b 100644
--- a/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/ViewCompatHC.java
@@ -19,9 +19,13 @@
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.ViewParent;
 
+@RequiresApi(11)
+@TargetApi(11)
 class ViewCompatHC {
     static long getFrameTime() {
         return ValueAnimator.getFrameDelay();
diff --git a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
index 96349ed..3b31adf 100644
--- a/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
+++ b/compat/honeycomb/android/support/v4/view/ViewGroupCompatHC.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ViewGroup;
 
+@RequiresApi(11)
+@TargetApi(11)
 class ViewGroupCompatHC {
     private ViewGroupCompatHC() {
     }
diff --git a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
index 9143214..01867d8 100644
--- a/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
+++ b/compat/honeycomb/android/support/v4/widget/SearchViewCompatHoneycomb.java
@@ -19,6 +19,8 @@
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.widget.SearchView;
 import android.widget.SearchView.OnCloseListener;
@@ -27,6 +29,9 @@
 /**
  * Implementation of SearchView compatibility that can call Honeycomb APIs.
  */
+
+@RequiresApi(11)
+@TargetApi(11)
 class SearchViewCompatHoneycomb {
 
     public static void checkIfLegalArg(View searchView) {
diff --git a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
index 5133a8a..9aaae0b 100644
--- a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
+++ b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
@@ -17,17 +17,20 @@
 package android.support.v4.animation;
 
 import android.animation.Animator;
-import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
-import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
 /**
  * Uses framework Animators to provide ValueAnimatorCompat interface.
- * <p>
+ * <p/>
  * This is not a fully implemented API which is why it is not public.
  */
+
+@RequiresApi(12)
+@TargetApi(12)
 class HoneycombMr1AnimatorCompatProvider implements AnimatorProvider {
 
     private TimeInterpolator mDefaultInterpolator;
diff --git a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
index 94358d5..4266460 100644
--- a/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
+++ b/compat/honeycomb_mr1/android/support/v4/graphics/BitmapCompatHoneycombMr1.java
@@ -16,10 +16,15 @@
 package android.support.v4.graphics;
 
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of BitmapCompat that can use Honeycomb MR1 APIs.
  */
+
+@RequiresApi(12)
+@TargetApi(12)
 class BitmapCompatHoneycombMr1 {
 
     static int getAllocationByteCount(Bitmap bitmap) {
diff --git a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java b/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
index 406fcf3..f14e77d 100644
--- a/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
+++ b/compat/honeycomb_mr1/android/support/v4/view/MotionEventCompatHoneycombMr1.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.MotionEvent;
 
 /**
  * Motion event compatibility class for API 12+.
  */
+
+@RequiresApi(12)
+@TargetApi(12)
 class MotionEventCompatHoneycombMr1 {
     static float getAxisValue(MotionEvent event, int axis) {
         return event.getAxisValue(axis);
diff --git a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java b/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
index 62eba95..aa3aaef 100644
--- a/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
+++ b/compat/honeycomb_mr2/android/support/v4/content/res/ConfigurationHelperHoneycombMr2.java
@@ -16,10 +16,13 @@
 
 package android.support.v4.content.res;
 
-import android.content.Context;
 import android.content.res.Resources;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(13)
+@TargetApi(13)
 class ConfigurationHelperHoneycombMr2 {
 
     static int getScreenHeightDp(@NonNull Resources resources) {
diff --git a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
index 619c1af..a631941 100644
--- a/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
+++ b/compat/honeycomb_mr2/android/support/v4/net/ConnectivityManagerCompatHoneycombMR2.java
@@ -16,6 +16,11 @@
 
 package android.support.v4.net;
 
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -26,12 +31,12 @@
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
 
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
 /**
  * Implementation of ConnectivityManagerCompat that can use Honeycomb MR2 APIs.
  */
+
+@RequiresApi(13)
+@TargetApi(13)
 class ConnectivityManagerCompatHoneycombMR2 {
     public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
         final NetworkInfo info = cm.getActiveNetworkInfo();
diff --git a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
index 08acb55..fe754c4 100644
--- a/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
+++ b/compat/honeycomb_mr2/android/support/v4/os/ParcelableCompatHoneycombMR2.java
@@ -18,13 +18,19 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(13)
+@TargetApi(13)
 class ParcelableCompatCreatorHoneycombMR2Stub {
     static <T> Parcelable.Creator<T> instantiate(ParcelableCompatCreatorCallbacks<T> callbacks) {
         return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
     }
 }
 
+@RequiresApi(13)
+@TargetApi(13)
 class ParcelableCompatCreatorHoneycombMR2<T> implements Parcelable.ClassLoaderCreator<T> {
     private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
 
diff --git a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
index 0fa2c43..be17cd6 100644
--- a/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/content/IntentCompatIcsMr1.java
@@ -17,7 +17,11 @@
 package android.support.v4.content;
 
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(15)
+@TargetApi(15)
 class IntentCompatIcsMr1 {
 
     public static Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) {
diff --git a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
index 8e14256..be229b5 100644
--- a/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/content/res/ResourcesCompatIcsMr1.java
@@ -18,9 +18,12 @@
 
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(15)
+@TargetApi(15)
 class ResourcesCompatIcsMr1 {
     public static Drawable getDrawableForDensity(Resources res, int id, int density)
             throws NotFoundException {
diff --git a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java b/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
index 780345c..3cf4e5e 100644
--- a/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
+++ b/compat/ics-mr1/android/support/v4/view/ViewCompatICSMr1.java
@@ -16,15 +16,16 @@
 
 package android.support.v4.view;
 
-import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * Helper for accessing newer features in View introduced in ICS Mr1.
  */
+
+@RequiresApi(15)
+@TargetApi(15)
 class ViewCompatICSMr1 {
     public static boolean hasOnClickListeners(View v) {
         return v.hasOnClickListeners();
diff --git a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java b/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
index 94164d7..f249bdd 100644
--- a/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
+++ b/compat/ics-mr1/android/support/v4/view/accessibility/AccessibilityRecordCompatIcsMr1.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityRecord;
 
 /**
  * ICS MR1 specific AccessibilityRecord API implementation.
  */
+
+@RequiresApi(15)
+@TargetApi(15)
 class AccessibilityRecordCompatIcsMr1 {
 
     public static int getMaxScrollX(Object record) {
diff --git a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java b/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
index ffbea0a..21e797d 100644
--- a/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
+++ b/compat/ics/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatIcs.java
@@ -19,10 +19,15 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.pm.ResolveInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * ICS implementation of the new APIs in AccessibilityServiceInfo.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityServiceInfoCompatIcs {
 
     public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
diff --git a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
index 0842451..d2e0e44 100644
--- a/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
+++ b/compat/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
@@ -20,8 +20,12 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.RemoteViews;
 
+@RequiresApi(14)
+@TargetApi(14)
 class NotificationCompatIceCreamSandwich {
 
     public static class Builder implements NotificationBuilderWithBuilderAccessor {
diff --git a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java b/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
index f088179..4fcf2b1 100644
--- a/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
+++ b/compat/ics/android/support/v4/app/NotificationManagerCompatIceCreamSandwich.java
@@ -17,7 +17,11 @@
 package android.support.v4.app;
 
 import android.app.Service;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(14)
+@TargetApi(14)
 class NotificationManagerCompatIceCreamSandwich {
     static final int SIDE_CHANNEL_BIND_FLAGS = Service.BIND_AUTO_CREATE
             | Service.BIND_WAIVE_PRIORITY;
diff --git a/compat/ics/android/support/v4/app/ShareCompatICS.java b/compat/ics/android/support/v4/app/ShareCompatICS.java
index 0f2ff35..a6d1e92 100644
--- a/compat/ics/android/support/v4/app/ShareCompatICS.java
+++ b/compat/ics/android/support/v4/app/ShareCompatICS.java
@@ -18,10 +18,14 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ActionProvider;
 import android.view.MenuItem;
 import android.widget.ShareActionProvider;
 
+@RequiresApi(14)
+@TargetApi(14)
 class ShareCompatICS {
     private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
 
diff --git a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java b/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
index e3f3fab..724c34e 100644
--- a/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
+++ b/compat/ics/android/support/v4/net/TrafficStatsCompatIcs.java
@@ -18,6 +18,8 @@
 
 import android.net.TrafficStats;
 import android.os.ParcelFileDescriptor;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.net.DatagramSocket;
 import java.net.Socket;
@@ -26,6 +28,9 @@
 /**
  * Implementation of TrafficStatsCompat that can call ICS APIs.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class TrafficStatsCompatIcs {
     public static void clearThreadStatsTag() {
         TrafficStats.clearThreadStatsTag();
diff --git a/compat/ics/android/support/v4/text/ICUCompatIcs.java b/compat/ics/android/support/v4/text/ICUCompatIcs.java
index dfb9e7e..4baafd6 100644
--- a/compat/ics/android/support/v4/text/ICUCompatIcs.java
+++ b/compat/ics/android/support/v4/text/ICUCompatIcs.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.text;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Locale;
 
+@RequiresApi(14)
+@TargetApi(14)
 class ICUCompatIcs {
 
     private static final String TAG = "ICUCompatIcs";
diff --git a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java b/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
index 4adeb0c..fee33d7 100644
--- a/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
+++ b/compat/ics/android/support/v4/view/AccessibilityDelegateCompatIcs.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
@@ -25,6 +27,9 @@
 /**
  * ICS specific AccessibilityDelegate API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityDelegateCompatIcs {
 
     public interface AccessibilityDelegateBridge {
diff --git a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java b/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
index 8e65079..4dbea9a 100644
--- a/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
+++ b/compat/ics/android/support/v4/view/MenuItemCompatIcs.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.MenuItem;
 
+@RequiresApi(14)
+@TargetApi(14)
 class MenuItemCompatIcs {
     public static boolean expandActionView(MenuItem item) {
         return item.expandActionView();
diff --git a/compat/ics/android/support/v4/view/MotionEventCompatICS.java b/compat/ics/android/support/v4/view/MotionEventCompatICS.java
index e7979de..e8f9d49 100644
--- a/compat/ics/android/support/v4/view/MotionEventCompatICS.java
+++ b/compat/ics/android/support/v4/view/MotionEventCompatICS.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.MotionEvent;
 
+@RequiresApi(14)
+@TargetApi(14)
 class MotionEventCompatICS {
     public static int getButtonState(MotionEvent event) {
         return event.getButtonState();
diff --git a/compat/ics/android/support/v4/view/ViewCompatICS.java b/compat/ics/android/support/v4/view/ViewCompatICS.java
index 742c47c..338b009 100644
--- a/compat/ics/android/support/v4/view/ViewCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewCompatICS.java
@@ -17,6 +17,8 @@
 package android.support.v4.view;
 
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.accessibility.AccessibilityEvent;
@@ -25,6 +27,9 @@
 /**
  * Helper for accessing newer features in View introduced in ICS.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class ViewCompatICS {
 
     public static boolean canScrollHorizontally(View v, int direction) {
diff --git a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java b/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
index 13a9647..19a7174 100644
--- a/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewConfigurationCompatICS.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ViewConfiguration;
 
 /**
  * Implementation of menu compatibility that can call ICS APIs.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class ViewConfigurationCompatICS {
     static boolean hasPermanentMenuKey(ViewConfiguration config) {
         return config.hasPermanentMenuKey();
diff --git a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
index 73de780..bb03e7d 100644
--- a/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
+++ b/compat/ics/android/support/v4/view/ViewGroupCompatIcs.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -23,6 +25,9 @@
 /**
  * ICS specific ViewGroup API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class ViewGroupCompatIcs {
     public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,
             AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/ViewParentCompatICS.java b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
index f9fc5a5..693aa40 100644
--- a/compat/ics/android/support/v4/view/ViewParentCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewParentCompatICS.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -23,6 +25,9 @@
 /**
  * ICS-specific ViewParent API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class ViewParentCompatICS {
     public static boolean requestSendAccessibilityEvent(
             ViewParent parent, View child, AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java b/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
index 1fd7f3e..9cc5583 100644
--- a/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
+++ b/compat/ics/android/support/v4/view/ViewPropertyAnimatorCompatICS.java
@@ -17,9 +17,13 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.animation.Interpolator;
 
+@RequiresApi(14)
+@TargetApi(14)
 class ViewPropertyAnimatorCompatICS {
 
     public static void setDuration(View view, long value) {
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
index 632c4d1..0d5196f 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityEventCompatIcs.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityRecord;
 
 /**
  * ICS specific AccessibilityEvent API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityEventCompatIcs {
 
     public static int getRecordCount(AccessibilityEvent event) {
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
index 3b5be26..4af6aa3 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityManagerCompatIcs.java
@@ -17,6 +17,8 @@
 package android.support.v4.view.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 
@@ -25,6 +27,9 @@
 /**
  * ICS specific AccessibilityManager API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityManagerCompatIcs {
 
     public static class AccessibilityStateChangeListenerWrapper
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
index 17f9aa8..51faa89 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatIcs.java
@@ -16,7 +16,9 @@
 
 package android.support.v4.view.accessibility;
 
+import android.annotation.TargetApi;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -25,6 +27,9 @@
 /**
  * ICS specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityNodeInfoCompatIcs {
     public static Object obtain() {
         return AccessibilityNodeInfo.obtain();
diff --git a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java b/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
index 0ebcc6a..f6e078f 100644
--- a/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
+++ b/compat/ics/android/support/v4/view/accessibility/AccessibilityRecordCompatIcs.java
@@ -17,6 +17,8 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityRecord;
 
@@ -25,6 +27,9 @@
 /**
  * ICS specific AccessibilityRecord API implementation.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class AccessibilityRecordCompatIcs {
 
     public static Object obtain() {
diff --git a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
index c02eeb4..1f75b4a 100644
--- a/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/EdgeEffectCompatIcs.java
@@ -17,14 +17,19 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 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)
+@TargetApi(14)
 class EdgeEffectCompatIcs {
     public static Object newEdgeEffect(Context context) {
         return new EdgeEffect(context);
diff --git a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java b/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
index 76b9a2b..be7a07e 100644
--- a/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/ScrollerCompatIcs.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.OverScroller;
 
 /**
  * ICS API access for ScrollerCompat
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class ScrollerCompatIcs {
     public static float getCurrVelocity(Object scroller) {
         return ((OverScroller) scroller).getCurrVelocity();
diff --git a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java b/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
index b8d719c..3938081 100644
--- a/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
+++ b/compat/ics/android/support/v4/widget/SearchViewCompatIcs.java
@@ -17,12 +17,17 @@
 package android.support.v4.widget;
 
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.widget.SearchView;
 
 /**
  * Implementation of SearchView compatibility that can call ICS APIs.
  */
+
+@RequiresApi(14)
+@TargetApi(14)
 class SearchViewCompatIcs {
 
     public static class MySearchView extends SearchView {
diff --git a/compat/java/android/support/v4/app/ActivityOptionsCompat.java b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
index 68fc441..57ca1a4 100644
--- a/compat/java/android/support/v4/app/ActivityOptionsCompat.java
+++ b/compat/java/android/support/v4/app/ActivityOptionsCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.util.Pair;
 import android.view.View;
 
@@ -306,6 +308,8 @@
         return new ActivityOptionsCompat();
     }
 
+    @RequiresApi(16)
+    @TargetApi(16)
     private static class ActivityOptionsImplJB extends ActivityOptionsCompat {
         private final ActivityOptionsCompatJB mImpl;
 
@@ -327,6 +331,8 @@
         }
     }
 
+    @RequiresApi(21)
+    @TargetApi(21)
     private static class ActivityOptionsImpl21 extends ActivityOptionsCompat {
         private final ActivityOptionsCompat21 mImpl;
 
@@ -349,6 +355,8 @@
         }
     }
 
+    @RequiresApi(23)
+    @TargetApi(23)
     private static class ActivityOptionsImpl23 extends ActivityOptionsCompat {
         private final ActivityOptionsCompat23 mImpl;
 
@@ -376,6 +384,8 @@
         }
     }
 
+    @RequiresApi(24)
+    @TargetApi(24)
     private static class ActivityOptionsImpl24 extends ActivityOptionsCompat {
         private final ActivityOptionsCompat24 mImpl;
 
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index 4409f92..41f4832 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -36,6 +36,7 @@
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -2505,7 +2506,7 @@
              * @param intent the {@link PendingIntent} to fire when users trigger this action
              */
             public Builder(int icon, CharSequence title, PendingIntent intent) {
-                this(icon, title, intent, new Bundle());
+                this(icon, title, intent, new Bundle(), null, false);
             }
 
             /**
@@ -2514,14 +2515,19 @@
              * @param action the action to read fields from.
              */
             public Builder(Action action) {
-                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
+                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
+                        action.getRemoteInputs(), action.getAllowGeneratedReplies());
             }
 
-            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
+            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
+                    RemoteInput[] remoteInputs, boolean allowGeneratedReplies) {
                 mIcon = icon;
                 mTitle = NotificationCompat.Builder.limitCharSequenceLength(title);
                 mIntent = intent;
                 mExtras = extras;
+                mRemoteInputs = remoteInputs == null ? null : new ArrayList<>(
+                        Arrays.asList(remoteInputs));
+                mAllowGeneratedReplies = allowGeneratedReplies;
             }
 
             /**
diff --git a/compat/java/android/support/v4/util/ArraySet.java b/compat/java/android/support/v4/util/ArraySet.java
new file mode 100644
index 0000000..d03dfd1
--- /dev/null
+++ b/compat/java/android/support/v4/util/ArraySet.java
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.util;
+
+import android.util.Log;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ArraySet is a generic set data structure that is designed to be more memory efficient than a
+ * traditional {@link java.util.HashSet}.  The design is very similar to
+ * {@link ArrayMap}, with all of the caveats described there.  This implementation is
+ * separate from ArrayMap, however, so the Object array contains only one item for each
+ * entry in the set (instead of a pair for a mapping).
+ *
+ * <p>Note that this implementation is not intended to be appropriate for data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashSet, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
+ *
+ * <p>Because this container is intended to better balance memory use, unlike most other
+ * standard Java containers it will shrink its array as items are removed from it.  Currently
+ * you have no control over this shrinking -- if you set a capacity and then remove an
+ * item, it may reduce the capacity to better match the current size.  In the future an
+ * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ */
+public final class ArraySet<E> implements Collection<E>, Set<E> {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "ArraySet";
+    private static final int[] INT = new int[0];
+    private static final Object[] OBJECT = new Object[0];
+
+    /**
+     * The minimum amount by which the capacity of a ArraySet will increase.
+     * This is tuned to be relatively space-efficient.
+     */
+    private static final int BASE_SIZE = 4;
+
+    /**
+     * Maximum number of entries to have in array caches.
+     */
+    private static final int CACHE_SIZE = 10;
+
+    /**
+     * Caches of small array objects to avoid spamming garbage.  The cache
+     * Object[] variable is a pointer to a linked list of array objects.
+     * The first entry in the array is a pointer to the next array in the
+     * list; the second entry is a pointer to the int[] hash code array for it.
+     */
+    static Object[] sBaseCache;
+    static int sBaseCacheSize;
+    static Object[] sTwiceBaseCache;
+    static int sTwiceBaseCacheSize;
+
+    final boolean mIdentityHashCode;
+    int[] mHashes;
+    Object[] mArray;
+    int mSize;
+    MapCollections<E, E> mCollections;
+
+    private int indexOf(Object key, int hash) {
+        final int N = mSize;
+
+        // Important fast case: if nothing is in here, nothing to look for.
+        if (N == 0) {
+            return ~0;
+        }
+
+        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
+
+        // If the hash code wasn't found, then we have no entry for this key.
+        if (index < 0) {
+            return index;
+        }
+
+        // If the key at the returned index matches, that's what we want.
+        if (key.equals(mArray[index])) {
+            return index;
+        }
+
+        // Search for a matching key after the index.
+        int end;
+        for (end = index + 1; end < N && mHashes[end] == hash; end++) {
+            if (key.equals(mArray[end])) return end;
+        }
+
+        // Search for a matching key before the index.
+        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
+            if (key.equals(mArray[i])) return i;
+        }
+
+        // Key not found -- return negative value indicating where a
+        // new entry for this key should go.  We use the end of the
+        // hash chain to reduce the number of array entries that will
+        // need to be copied when inserting.
+        return ~end;
+    }
+
+    private int indexOfNull() {
+        final int N = mSize;
+
+        // Important fast case: if nothing is in here, nothing to look for.
+        if (N == 0) {
+            return ~0;
+        }
+
+        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
+
+        // If the hash code wasn't found, then we have no entry for this key.
+        if (index < 0) {
+            return index;
+        }
+
+        // If the key at the returned index matches, that's what we want.
+        if (null == mArray[index]) {
+            return index;
+        }
+
+        // Search for a matching key after the index.
+        int end;
+        for (end = index + 1; end < N && mHashes[end] == 0; end++) {
+            if (null == mArray[end]) return end;
+        }
+
+        // Search for a matching key before the index.
+        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {
+            if (null == mArray[i]) return i;
+        }
+
+        // Key not found -- return negative value indicating where a
+        // new entry for this key should go.  We use the end of the
+        // hash chain to reduce the number of array entries that will
+        // need to be copied when inserting.
+        return ~end;
+    }
+
+    private void allocArrays(final int size) {
+        if (size == (BASE_SIZE * 2)) {
+            synchronized (ArraySet.class) {
+                if (sTwiceBaseCache != null) {
+                    final Object[] array = sTwiceBaseCache;
+                    mArray = array;
+                    sTwiceBaseCache = (Object[]) array[0];
+                    mHashes = (int[]) array[1];
+                    array[0] = array[1] = null;
+                    sTwiceBaseCacheSize--;
+                    if (DEBUG) {
+                        Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
+                                + sTwiceBaseCacheSize + " entries");
+                    }
+                    return;
+                }
+            }
+        } else if (size == BASE_SIZE) {
+            synchronized (ArraySet.class) {
+                if (sBaseCache != null) {
+                    final Object[] array = sBaseCache;
+                    mArray = array;
+                    sBaseCache = (Object[]) array[0];
+                    mHashes = (int[]) array[1];
+                    array[0] = array[1] = null;
+                    sBaseCacheSize--;
+                    if (DEBUG) {
+                        Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have " + sBaseCacheSize
+                                + " entries");
+                    }
+                    return;
+                }
+            }
+        }
+
+        mHashes = new int[size];
+        mArray = new Object[size];
+    }
+
+    private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
+        if (hashes.length == (BASE_SIZE * 2)) {
+            synchronized (ArraySet.class) {
+                if (sTwiceBaseCacheSize < CACHE_SIZE) {
+                    array[0] = sTwiceBaseCache;
+                    array[1] = hashes;
+                    for (int i = size - 1; i >= 2; i--) {
+                        array[i] = null;
+                    }
+                    sTwiceBaseCache = array;
+                    sTwiceBaseCacheSize++;
+                    if (DEBUG) {
+                        Log.d(TAG, "Storing 2x cache " + array + " now have " + sTwiceBaseCacheSize
+                                + " entries");
+                    }
+                }
+            }
+        } else if (hashes.length == BASE_SIZE) {
+            synchronized (ArraySet.class) {
+                if (sBaseCacheSize < CACHE_SIZE) {
+                    array[0] = sBaseCache;
+                    array[1] = hashes;
+                    for (int i = size - 1; i >= 2; i--) {
+                        array[i] = null;
+                    }
+                    sBaseCache = array;
+                    sBaseCacheSize++;
+                    if (DEBUG) {
+                        Log.d(TAG, "Storing 1x cache " + array + " now have "
+                                + sBaseCacheSize + " entries");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new empty ArraySet.  The default capacity of an array map is 0, and
+     * will grow once items are added to it.
+     */
+    public ArraySet() {
+        this(0, false);
+    }
+
+    /**
+     * Create a new ArraySet with a given initial capacity.
+     */
+    public ArraySet(int capacity) {
+        this(capacity, false);
+    }
+
+    /** {@hide} */
+    public ArraySet(int capacity, boolean identityHashCode) {
+        mIdentityHashCode = identityHashCode;
+        if (capacity == 0) {
+            mHashes = INT;
+            mArray = OBJECT;
+        } else {
+            allocArrays(capacity);
+        }
+        mSize = 0;
+    }
+
+    /**
+     * Create a new ArraySet with the mappings from the given ArraySet.
+     */
+    public ArraySet(ArraySet<E> set) {
+        this();
+        if (set != null) {
+            addAll(set);
+        }
+    }
+
+    /** {@hide} */
+    public ArraySet(Collection<E> set) {
+        this();
+        if (set != null) {
+            addAll(set);
+        }
+    }
+
+    /**
+     * Make the array map empty.  All storage is released.
+     */
+    @Override
+    public void clear() {
+        if (mSize != 0) {
+            freeArrays(mHashes, mArray, mSize);
+            mHashes = INT;
+            mArray = OBJECT;
+            mSize = 0;
+        }
+    }
+
+    /**
+     * Ensure the array map can hold at least <var>minimumCapacity</var>
+     * items.
+     */
+    public void ensureCapacity(int minimumCapacity) {
+        if (mHashes.length < minimumCapacity) {
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            allocArrays(minimumCapacity);
+            if (mSize > 0) {
+                System.arraycopy(ohashes, 0, mHashes, 0, mSize);
+                System.arraycopy(oarray, 0, mArray, 0, mSize);
+            }
+            freeArrays(ohashes, oarray, mSize);
+        }
+    }
+
+    /**
+     * Check whether a value exists in the set.
+     *
+     * @param key The value to search for.
+     * @return Returns true if the value exists, else false.
+     */
+    @Override
+    public boolean contains(Object key) {
+        return indexOf(key) >= 0;
+    }
+
+    /**
+     * Returns the index of a value in the set.
+     *
+     * @param key The value to search for.
+     * @return Returns the index of the value if it exists, else a negative integer.
+     */
+    public int indexOf(Object key) {
+        return key == null ? indexOfNull()
+                : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
+    }
+
+    /**
+     * Return the value at the given index in the array.
+     * @param index The desired index, must be between 0 and {@link #size()}-1.
+     * @return Returns the value stored at the given index.
+     */
+    public E valueAt(int index) {
+        return (E) mArray[index];
+    }
+
+    /**
+     * Return true if the array map contains no items.
+     */
+    @Override
+    public boolean isEmpty() {
+        return mSize <= 0;
+    }
+
+    /**
+     * Adds the specified object to this set. The set is not modified if it
+     * already contains the object.
+     *
+     * @param value the object to add.
+     * @return {@code true} if this set is modified, {@code false} otherwise.
+     * @throws ClassCastException
+     *             when the class of the object is inappropriate for this set.
+     */
+    @Override
+    public boolean add(E value) {
+        final int hash;
+        int index;
+        if (value == null) {
+            hash = 0;
+            index = indexOfNull();
+        } else {
+            hash = mIdentityHashCode ? System.identityHashCode(value) : value.hashCode();
+            index = indexOf(value, hash);
+        }
+        if (index >= 0) {
+            return false;
+        }
+
+        index = ~index;
+        if (mSize >= mHashes.length) {
+            final int n = mSize >= (BASE_SIZE * 2) ? (mSize + (mSize >> 1))
+                    : (mSize >= BASE_SIZE ? (BASE_SIZE * 2) : BASE_SIZE);
+
+            if (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n);
+
+            final int[] ohashes = mHashes;
+            final Object[] oarray = mArray;
+            allocArrays(n);
+
+            if (mHashes.length > 0) {
+                if (DEBUG) Log.d(TAG, "add: copy 0-" + mSize + " to 0");
+                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
+                System.arraycopy(oarray, 0, mArray, 0, oarray.length);
+            }
+
+            freeArrays(ohashes, oarray, mSize);
+        }
+
+        if (index < mSize) {
+            if (DEBUG) {
+                Log.d(TAG, "add: move " + index + "-" + (mSize - index) + " to " + (index + 1));
+            }
+            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
+            System.arraycopy(mArray, index, mArray, index + 1, mSize - index);
+        }
+
+        mHashes[index] = hash;
+        mArray[index] = value;
+        mSize++;
+        return true;
+    }
+
+    /**
+     * Special fast path for appending items to the end of the array without validation.
+     * The array must already be large enough to contain the item.
+     * @hide
+     */
+    public void append(E value) {
+        final int index = mSize;
+        final int hash = value == null ? 0
+                : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
+        if (index >= mHashes.length) {
+            throw new IllegalStateException("Array is full");
+        }
+        if (index > 0 && mHashes[index - 1] > hash) {
+            // Cannot optimize since it would break the sorted order - fallback to add()
+            if (DEBUG) {
+                RuntimeException e = new RuntimeException("here");
+                e.fillInStackTrace();
+                Log.w(TAG, "New hash " + hash
+                        + " is before end of array hash " + mHashes[index - 1]
+                        + " at index " + index, e);
+            }
+            add(value);
+            return;
+        }
+        mSize = index + 1;
+        mHashes[index] = hash;
+        mArray[index] = value;
+    }
+
+    /**
+     * Perform a {@link #add(Object)} of all values in <var>array</var>
+     * @param array The array whose contents are to be retrieved.
+     */
+    public void addAll(ArraySet<? extends E> array) {
+        final int N = array.mSize;
+        ensureCapacity(mSize + N);
+        if (mSize == 0) {
+            if (N > 0) {
+                System.arraycopy(array.mHashes, 0, mHashes, 0, N);
+                System.arraycopy(array.mArray, 0, mArray, 0, N);
+                mSize = N;
+            }
+        } else {
+            for (int i = 0; i < N; i++) {
+                add(array.valueAt(i));
+            }
+        }
+    }
+
+    /**
+     * Removes the specified object from this set.
+     *
+     * @param object the object to remove.
+     * @return {@code true} if this set was modified, {@code false} otherwise.
+     */
+    @Override
+    public boolean remove(Object object) {
+        final int index = indexOf(object);
+        if (index >= 0) {
+            removeAt(index);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove the key/value mapping at the given index.
+     * @param index The desired index, must be between 0 and {@link #size()}-1.
+     * @return Returns the value that was stored at this index.
+     */
+    public E removeAt(int index) {
+        final Object old = mArray[index];
+        if (mSize <= 1) {
+            // Now empty.
+            if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
+            freeArrays(mHashes, mArray, mSize);
+            mHashes = INT;
+            mArray = OBJECT;
+            mSize = 0;
+        } else {
+            if (mHashes.length > (BASE_SIZE * 2) && mSize < mHashes.length / 3) {
+                // Shrunk enough to reduce size of arrays.  We don't allow it to
+                // shrink smaller than (BASE_SIZE*2) to avoid flapping between
+                // that and BASE_SIZE.
+                final int n = mSize > (BASE_SIZE * 2) ? (mSize + (mSize >> 1)) : (BASE_SIZE * 2);
+
+                if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
+
+                final int[] ohashes = mHashes;
+                final Object[] oarray = mArray;
+                allocArrays(n);
+
+                mSize--;
+                if (index > 0) {
+                    if (DEBUG) Log.d(TAG, "remove: copy from 0-" + index + " to 0");
+                    System.arraycopy(ohashes, 0, mHashes, 0, index);
+                    System.arraycopy(oarray, 0, mArray, 0, index);
+                }
+                if (index < mSize) {
+                    if (DEBUG) {
+                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + mSize
+                                + " to " + index);
+                    }
+                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
+                    System.arraycopy(oarray, index + 1, mArray, index, mSize - index);
+                }
+            } else {
+                mSize--;
+                if (index < mSize) {
+                    if (DEBUG) {
+                        Log.d(TAG, "remove: move " + (index + 1) + "-" + mSize + " to " + index);
+                    }
+                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
+                    System.arraycopy(mArray, index + 1, mArray, index, mSize - index);
+                }
+                mArray[mSize] = null;
+            }
+        }
+        return (E) old;
+    }
+
+    /**
+     * Perform a {@link #remove(Object)} of all values in <var>array</var>
+     * @param array The array whose contents are to be removed.
+     */
+    public boolean removeAll(ArraySet<? extends E> array) {
+        // TODO: If array is sufficiently large, a marking approach might be beneficial. In a first
+        //       pass, use the property that the sets are sorted by hash to make this linear passes
+        //       (except for hash collisions, which means worst case still n*m), then do one
+        //       collection pass into a new array. This avoids binary searches and excessive memcpy.
+        final int N = array.mSize;
+
+        // Note: ArraySet does not make thread-safety guarantees. So instead of OR-ing together all
+        //       the single results, compare size before and after.
+        final int originalSize = mSize;
+        for (int i = 0; i < N; i++) {
+            remove(array.valueAt(i));
+        }
+        return originalSize != mSize;
+    }
+
+    /**
+     * Return the number of items in this array map.
+     */
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    public Object[] toArray() {
+        Object[] result = new Object[mSize];
+        System.arraycopy(mArray, 0, result, 0, mSize);
+        return result;
+    }
+
+    @Override
+    public <T> T[] toArray(T[] array) {
+        if (array.length < mSize) {
+            @SuppressWarnings("unchecked") T[] newArray =
+                    (T[]) Array.newInstance(array.getClass().getComponentType(), mSize);
+            array = newArray;
+        }
+        System.arraycopy(mArray, 0, array, 0, mSize);
+        if (array.length > mSize) {
+            array[mSize] = null;
+        }
+        return array;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation returns false if the object is not a set, or
+     * if the sets have different sizes.  Otherwise, for each value in this
+     * set, it checks to make sure the value also exists in the other set.
+     * If any value doesn't exist, the method returns false; otherwise, it
+     * returns true.
+     */
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (object instanceof Set) {
+            Set<?> set = (Set<?>) object;
+            if (size() != set.size()) {
+                return false;
+            }
+
+            try {
+                for (int i = 0; i < mSize; i++) {
+                    E mine = valueAt(i);
+                    if (!set.contains(mine)) {
+                        return false;
+                    }
+                }
+            } catch (NullPointerException ignored) {
+                return false;
+            } catch (ClassCastException ignored) {
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int[] hashes = mHashes;
+        int result = 0;
+        for (int i = 0, s = mSize; i < s; i++) {
+            result += hashes[i];
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its values. If
+     * this set contains itself as a value, the string "(this Set)"
+     * will appear in its place.
+     */
+    @Override
+    public String toString() {
+        if (isEmpty()) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 14);
+        buffer.append('{');
+        for (int i = 0; i < mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            Object value = valueAt(i);
+            if (value != this) {
+                buffer.append(value);
+            } else {
+                buffer.append("(this Set)");
+            }
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+    // ------------------------------------------------------------------------
+    // Interop with traditional Java containers.  Not as efficient as using
+    // specialized collection APIs.
+    // ------------------------------------------------------------------------
+
+    private MapCollections<E, E> getCollection() {
+        if (mCollections == null) {
+            mCollections = new MapCollections<E, E>() {
+                @Override
+                protected int colGetSize() {
+                    return mSize;
+                }
+
+                @Override
+                protected Object colGetEntry(int index, int offset) {
+                    return mArray[index];
+                }
+
+                @Override
+                protected int colIndexOfKey(Object key) {
+                    return indexOf(key);
+                }
+
+                @Override
+                protected int colIndexOfValue(Object value) {
+                    return indexOf(value);
+                }
+
+                @Override
+                protected Map<E, E> colGetMap() {
+                    throw new UnsupportedOperationException("not a map");
+                }
+
+                @Override
+                protected void colPut(E key, E value) {
+                    add(key);
+                }
+
+                @Override
+                protected E colSetValue(int index, E value) {
+                    throw new UnsupportedOperationException("not a map");
+                }
+
+                @Override
+                protected void colRemoveAt(int index) {
+                    removeAt(index);
+                }
+
+                @Override
+                protected void colClear() {
+                    clear();
+                }
+            };
+        }
+        return mCollections;
+    }
+
+    /**
+     * Return an {@link java.util.Iterator} over all values in the set.
+     *
+     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
+     * requires generating a number of temporary objects and allocates additional state
+     * information associated with the container that will remain for the life of the container.</p>
+     */
+    @Override
+    public Iterator<E> iterator() {
+        return getCollection().getKeySet().iterator();
+    }
+
+    /**
+     * Determine if the array set contains all of the values in the given collection.
+     * @param collection The collection whose contents are to be checked against.
+     * @return Returns true if this array set contains a value for every entry
+     * in <var>collection</var>, else returns false.
+     */
+    @Override
+    public boolean containsAll(Collection<?> collection) {
+        Iterator<?> it = collection.iterator();
+        while (it.hasNext()) {
+            if (!contains(it.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Perform an {@link #add(Object)} of all values in <var>collection</var>
+     * @param collection The collection whose contents are to be retrieved.
+     */
+    @Override
+    public boolean addAll(Collection<? extends E> collection) {
+        ensureCapacity(mSize + collection.size());
+        boolean added = false;
+        for (E value : collection) {
+            added |= add(value);
+        }
+        return added;
+    }
+
+    /**
+     * Remove all values in the array set that exist in the given collection.
+     * @param collection The collection whose contents are to be used to remove values.
+     * @return Returns true if any values were removed from the array set, else false.
+     */
+    @Override
+    public boolean removeAll(Collection<?> collection) {
+        boolean removed = false;
+        for (Object value : collection) {
+            removed |= remove(value);
+        }
+        return removed;
+    }
+
+    /**
+     * Remove all values in the array set that do <b>not</b> exist in the given collection.
+     * @param collection The collection whose contents are to be used to determine which
+     * values to keep.
+     * @return Returns true if any values were removed from the array set, else false.
+     */
+    @Override
+    public boolean retainAll(Collection<?> collection) {
+        boolean removed = false;
+        for (int i = mSize - 1; i >= 0; i--) {
+            if (!collection.contains(mArray[i])) {
+                removeAt(i);
+                removed = true;
+            }
+        }
+        return removed;
+    }
+}
diff --git a/compat/java/android/support/v4/view/MenuItemCompat.java b/compat/java/android/support/v4/view/MenuItemCompat.java
index 1467754..7b57e26 100644
--- a/compat/java/android/support/v4/view/MenuItemCompat.java
+++ b/compat/java/android/support/v4/view/MenuItemCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.view;
 
+import android.os.Build;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.util.Log;
 import android.view.MenuItem;
@@ -244,10 +245,9 @@
      */
     static final MenuVersionImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 14) {
+        if (Build.VERSION.SDK_INT >= 14) {
             IMPL = new IcsMenuVersionImpl();
-        } else if (version >= 11) {
+        } else if (Build.VERSION.SDK_INT >= 11) {
             IMPL = new HoneycombMenuVersionImpl();
         } else {
             IMPL = new BaseMenuVersionImpl();
diff --git a/compat/java/android/support/v4/view/ViewCompat.java b/compat/java/android/support/v4/view/ViewCompat.java
index 9637e65..af621f5 100644
--- a/compat/java/android/support/v4/view/ViewCompat.java
+++ b/compat/java/android/support/v4/view/ViewCompat.java
@@ -1810,7 +1810,8 @@
     static class Api24ViewCompatImpl extends MarshmallowViewCompatImpl {
         @Override
         public void setPointerIcon(View view, PointerIconCompat pointerIconCompat) {
-            ViewCompatApi24.setPointerIcon(view, pointerIconCompat.getPointerIcon());
+            ViewCompatApi24.setPointerIcon(view,
+                    pointerIconCompat != null ? pointerIconCompat.getPointerIcon() : null);
         }
     }
 
diff --git a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
index faa0939..656fae9 100644
--- a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
+++ b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
@@ -23,6 +23,11 @@
 
 import java.lang.reflect.Field;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
+@RequiresApi(9)
+@TargetApi(9)
 class TextViewCompatGingerbread {
 
     private static final String LOG_TAG = "TextViewCompatGingerbread";
diff --git a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
index b16db18..eecc581 100644
--- a/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/content/res/ConfigurationHelperJellybeanMr1.java
@@ -16,10 +16,13 @@
 
 package android.support.v4.content.res;
 
-import android.content.Context;
 import android.content.res.Resources;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(17)
+@TargetApi(17)
 class ConfigurationHelperJellybeanMr1 {
     static int getDensityDpi(@NonNull Resources resources) {
         return resources.getConfiguration().densityDpi;
diff --git a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
index 09891c9..d8da519 100644
--- a/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/graphics/drawable/DrawableCompatJellybeanMr1.java
@@ -17,16 +17,18 @@
 package android.support.v4.graphics.drawable;
 
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
-import android.widget.CompoundButton;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 /**
  * Implementation of drawable compatibility that can call Jellybean MR1 APIs.
  */
+
+@RequiresApi(17)
+@TargetApi(17)
 class DrawableCompatJellybeanMr1 {
 
     private static final String TAG = "DrawableCompatJellybeanMr1";
diff --git a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
index b44a2e8..9cb5637 100644
--- a/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/hardware/display/DisplayManagerJellybeanMr1.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.hardware.display;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.Display;
 
+@RequiresApi(17)
+@TargetApi(17)
 final class DisplayManagerJellybeanMr1 {
     public static Object getDisplayManager(Context context) {
         return context.getSystemService(Context.DISPLAY_SERVICE);
diff --git a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
index d5b675b..ad354e7 100644
--- a/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/text/TextUtilsCompatJellybeanMr1.java
@@ -18,6 +18,8 @@
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.text.TextUtils;
 
 import java.util.Locale;
@@ -25,6 +27,9 @@
 /**
  * Jellybean MR1 - specific TextUtils API access.
  */
+
+@RequiresApi(17)
+@TargetApi(17)
 class TextUtilsCompatJellybeanMr1 {
     @NonNull
     public static String htmlEncode(@NonNull String s) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
index 1e6077d..efb1d77 100644
--- a/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/GravityCompatJellybeanMr1.java
@@ -18,8 +18,12 @@
 package android.support.v4.view;
 
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.Gravity;
 
+@RequiresApi(17)
+@TargetApi(17)
 class GravityCompatJellybeanMr1 {
 
     public static int getAbsoluteGravity(int gravity, int layoutDirection) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
index 2fe9bc8..c446abd 100644
--- a/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/MarginLayoutParamsCompatJellybeanMr1.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ViewGroup;
 
+@RequiresApi(17)
+@TargetApi(17)
 class MarginLayoutParamsCompatJellybeanMr1 {
     public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {
         return lp.getMarginStart();
diff --git a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
index c39ef2a..79b5ce2 100644
--- a/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/ViewCompatJellybeanMr1.java
@@ -16,13 +16,18 @@
 
 package android.support.v4.view;
 
+import android.annotation.TargetApi;
 import android.graphics.Paint;
+import android.support.annotation.RequiresApi;
 import android.view.Display;
 import android.view.View;
 
 /**
  * Jellybean MR1 - specific View API access.
  */
+
+@RequiresApi(17)
+@TargetApi(17)
 class ViewCompatJellybeanMr1 {
 
     public static int getLabelFor(View view) {
diff --git a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java b/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
index a4b9677..aa20646 100644
--- a/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr1.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+@RequiresApi(17)
+@TargetApi(17)
 class AccessibilityNodeInfoCompatJellybeanMr1 {
 
     public static void setLabelFor(Object info, View labeled) {
diff --git a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
index fc088ed..34cd6e4 100644
--- a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.widget.TextView;
 
+@RequiresApi(17)
+@TargetApi(17)
 class TextViewCompatJbMr1 {
 
     public static void setCompoundDrawablesRelative(@NonNull TextView textView,
diff --git a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java b/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
index 3a41b7a..acc72b1 100644
--- a/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBeanMr2.java
@@ -17,11 +17,15 @@
 package android.support.v4.accessibilityservice;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.ResolveInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * ICS implementation of the new APIs in AccessibilityServiceInfo.
  */
+
+@RequiresApi(18)
+@TargetApi(18)
 class AccessibilityServiceInfoCompatJellyBeanMr2 {
 
     public static int getCapabilities(AccessibilityServiceInfo info) {
diff --git a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
index bafefba..598ff31 100644
--- a/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/app/BundleCompatJellybeanMR2.java
@@ -18,7 +18,14 @@
 
 import android.os.Bundle;
 import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+/**
+ * @hide
+ */
+@RequiresApi(18)
+@TargetApi(18)
 class BundleCompatJellybeanMR2 {
     public static IBinder getBinder(Bundle bundle, String key) {
         return bundle.getBinder(key);
diff --git a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
index 21f0d49..20739d1 100644
--- a/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
@@ -16,7 +16,11 @@
 package android.support.v4.graphics;
 
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(18)
+@TargetApi(18)
 class BitmapCompatJellybeanMR2 {
     public static boolean hasMipMap(Bitmap bitmap) {
         return bitmap.hasMipMap();
diff --git a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
index 6116947..a41816d 100644
--- a/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/os/TraceJellybeanMR2.java
@@ -14,7 +14,11 @@
 package android.support.v4.os;
 
 import android.os.Trace;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(18)
+@TargetApi(18)
 class TraceJellybeanMR2 {
     public static void beginSection(String section) {
         Trace.beginSection(section);
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
index b8fcd9d..46c5d4e 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewCompatJellybeanMr2.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view;
 
-import android.view.View;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+import android.view.View;
 
 /**
  * Jellybean MR2 - specific View API access.
  */
+
+@RequiresApi(18)
+@TargetApi(18)
 class ViewCompatJellybeanMr2 {
 
     public static Rect getClipBounds(View view) {
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
index 086b070..e1c8532 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewGroupCompatJellybeanMR2.java
@@ -17,8 +17,12 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ViewGroup;
 
+@RequiresApi(18)
+@TargetApi(18)
 class ViewGroupCompatJellybeanMR2 {
     public static int getLayoutMode(ViewGroup group) {
         return group.getLayoutMode();
diff --git a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
index e9a29ee..14e76a9 100644
--- a/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/ViewPropertyAnimatorCompatJellybeanMr2.java
@@ -15,9 +15,13 @@
  */
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.animation.Interpolator;
 
+@RequiresApi(18)
+@TargetApi(18)
 class ViewPropertyAnimatorCompatJellybeanMr2 {
     public static Interpolator getInterpolator(View view) {
         return (Interpolator) view.animate().getInterpolator();
diff --git a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java b/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
index e48d9f7..82bfa11 100644
--- a/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellybeanMr2.java
@@ -16,10 +16,14 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.util.List;
 
+@RequiresApi(18)
+@TargetApi(18)
 class AccessibilityNodeInfoCompatJellybeanMr2 {
 
     public static void setViewIdResourceName(Object info, String viewId) {
diff --git a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java b/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
index 73f9666..2dea37d 100644
--- a/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
+++ b/compat/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.widget.TextView;
 
+@RequiresApi(18)
+@TargetApi(18)
 class TextViewCompatJbMr2 {
 
     public static void setCompoundDrawablesRelative(@NonNull TextView textView,
diff --git a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
index f0f8d0c..d42cefc 100644
--- a/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompatJellyBean.java
@@ -18,10 +18,15 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.pm.PackageManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * JB implementation of the new APIs in AccessibilityServiceInfo.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityServiceInfoCompatJellyBean {
 
     public static String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
diff --git a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
index 13e6e4e..ad1c9aa 100644
--- a/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ActivityCompatJB.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ActivityCompatJB {
     public static void startActivityForResult(Activity activity, Intent intent, int requestCode, Bundle options) {
         activity.startActivityForResult(intent, requestCode, options);
diff --git a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java b/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
index df9d987..1655c4b 100644
--- a/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ActivityOptionsCompatJB.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ActivityOptionsCompatJB {
 
     public static ActivityOptionsCompatJB makeCustomAnimation(Context context,
diff --git a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
index 261d8c4..bb94873 100644
--- a/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/NotificationCompatJellybean.java
@@ -22,6 +22,8 @@
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.Log;
 import android.util.SparseArray;
 import android.widget.RemoteViews;
@@ -30,6 +32,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(16)
+@TargetApi(16)
 class NotificationCompatJellybean {
     public static final String TAG = "NotificationCompat";
 
diff --git a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
index 36f5981..2fa9adc 100644
--- a/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/app/RemoteInputCompatJellybean.java
@@ -20,7 +20,11 @@
 import android.content.ClipDescription;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class RemoteInputCompatJellybean {
     /** Label used to denote the clip data type used for remote input transport */
     public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
diff --git a/compat/jellybean/android/support/v4/app/ShareCompatJB.java b/compat/jellybean/android/support/v4/app/ShareCompatJB.java
index 8a4de97..58eaa23 100644
--- a/compat/jellybean/android/support/v4/app/ShareCompatJB.java
+++ b/compat/jellybean/android/support/v4/app/ShareCompatJB.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.text.Html;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ShareCompatJB {
     public static String escapeHtml(CharSequence html) {
         return Html.escapeHtml(html);
diff --git a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
index dcd695e..ea4d610 100644
--- a/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/content/ContentResolverCompatJellybean.java
@@ -20,7 +20,11 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.OperationCanceledException;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ContentResolverCompatJellybean {
 
     public static Cursor query(ContentResolver resolver, Uri uri, String[] projection,
diff --git a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java b/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
index 5e9f910..c00a971 100644
--- a/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/content/ContextCompatJellybean.java
@@ -19,7 +19,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ContextCompatJellybean {
 
     public static void startActivities(Context context, Intent[] intents, Bundle options) {
diff --git a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java b/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
index 0795fdd..64272b8 100644
--- a/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/net/ConnectivityManagerCompatJellyBean.java
@@ -17,10 +17,15 @@
 package android.support.v4.net;
 
 import android.net.ConnectivityManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of ConnectivityManagerCompat that can use Jellybean APIs.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class ConnectivityManagerCompatJellyBean {
     public static boolean isActiveNetworkMetered(ConnectivityManager cm) {
         return cm.isActiveNetworkMetered();
diff --git a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java b/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
index 6029286..127fdbf 100644
--- a/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
+++ b/compat/jellybean/android/support/v4/os/CancellationSignalCompatJellybean.java
@@ -16,6 +16,11 @@
 
 package android.support.v4.os;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
+
+@RequiresApi(16)
+@TargetApi(16)
 class CancellationSignalCompatJellybean {
     public static Object create() {
         return new android.os.CancellationSignal();
diff --git a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java b/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
index 6651b62..e588892 100644
--- a/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/AccessibilityDelegateCompatJellyBean.java
@@ -17,6 +17,8 @@
 package android.support.v4.view;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
@@ -27,6 +29,9 @@
 /**
  * JellyBean specific AccessibilityDelegate API implementation.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityDelegateCompatJellyBean {
 
     public interface AccessibilityDelegateBridgeJellyBean {
diff --git a/compat/jellybean/android/support/v4/view/ViewCompatJB.java b/compat/jellybean/android/support/v4/view/ViewCompatJB.java
index 3d64da1..ccf34ba 100644
--- a/compat/jellybean/android/support/v4/view/ViewCompatJB.java
+++ b/compat/jellybean/android/support/v4/view/ViewCompatJB.java
@@ -16,14 +16,18 @@
 
 package android.support.v4.view;
 
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewParent;
 
 /**
  * Jellybean-specific View API access
  */
+@RequiresApi(16)
+@TargetApi(16)
 class ViewCompatJB {
 
     public static boolean hasTransientState(View view) {
diff --git a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java b/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
index 6107c42..8e327fe 100644
--- a/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
+++ b/compat/jellybean/android/support/v4/view/ViewPropertyAnimatorCompatJB.java
@@ -17,8 +17,12 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(16)
+@TargetApi(16)
 class ViewPropertyAnimatorCompatJB {
 
     public static void withStartAction(View view, Runnable runnable) {
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
index e557650..9c9ef09 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityEventCompatJellyBean.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityEvent;
 
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityEventCompatJellyBean {
     public static void setMovementGranularity(AccessibilityEvent event, int granularity) {
         event.setMovementGranularity(granularity);
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
index c2f4d93..a095b10 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatJellyBean.java
@@ -17,12 +17,17 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * JellyBean specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityNodeInfoCompatJellyBean {
 
     public static void addChild(Object info, View child, int virtualDescendantId) {
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
index cb8a3b4..195e2f3 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatJellyBean.java
@@ -17,6 +17,8 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
@@ -25,6 +27,9 @@
 /**
  * JellyBean specific AccessibilityNodeProvider API implementation.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityNodeProviderCompatJellyBean {
     interface AccessibilityNodeInfoBridge {
         public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
index dc9d518..e5a51b7 100644
--- a/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
+++ b/compat/jellybean/android/support/v4/view/accessibility/AccessibilityRecordCompatJellyBean.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityRecord;
 
 /**
  * JellyBean specific AccessibilityRecord API implementation.
  */
+
+@RequiresApi(16)
+@TargetApi(16)
 class AccessibilityRecordCompatJellyBean {
 
     public static void setSource(Object record, View root, int virtualDescendantId) {
diff --git a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java b/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
index 1658874..4fd4c4e 100644
--- a/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
+++ b/compat/jellybean/android/support/v4/widget/TextViewCompatJb.java
@@ -16,10 +16,12 @@
 
 package android.support.v4.widget;
 
-import android.graphics.drawable.Drawable;
-import android.view.View;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.widget.TextView;
 
+@RequiresApi(16)
+@TargetApi(16)
 class TextViewCompatJb {
     static int getMaxLines(TextView textView) {
         return textView.getMaxLines();
diff --git a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
index 3f889f6..f14b553 100644
--- a/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/ActivityManagerCompatKitKat.java
@@ -17,7 +17,11 @@
 package android.support.v4.app;
 
 import android.app.ActivityManager;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(19)
+@TargetApi(19)
 class ActivityManagerCompatKitKat {
     public static boolean isLowRamDevice(ActivityManager am) {
         return am.isLowRamDevice();
diff --git a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
index 3039458..5b11daf 100644
--- a/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationCompatKitKat.java
@@ -21,12 +21,16 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.util.SparseArray;
 import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(19)
+@TargetApi(19)
 class NotificationCompatKitKat {
     public static class Builder implements NotificationBuilderWithBuilderAccessor,
             NotificationBuilderWithActions {
diff --git a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java b/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
index baf58a9..24bacba 100644
--- a/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/app/NotificationManagerCompatKitKat.java
@@ -18,11 +18,15 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+@RequiresApi(19)
+@TargetApi(19)
 class NotificationManagerCompatKitKat {
     private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
     private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
diff --git a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java b/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
index 6b58827..6c1bc91 100644
--- a/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/content/ContextCompatKitKat.java
@@ -17,9 +17,13 @@
 package android.support.v4.content;
 
 import android.content.Context;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.File;
 
+@RequiresApi(19)
+@TargetApi(19)
 class ContextCompatKitKat {
     public static File[] getExternalCacheDirs(Context context) {
         return context.getExternalCacheDirs();
diff --git a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
index 2d4a66e..ba05a32 100644
--- a/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/BitmapCompatKitKat.java
@@ -16,10 +16,15 @@
 package android.support.v4.graphics;
 
 import android.graphics.Bitmap;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of BitmapCompat that can use KitKat APIs.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class BitmapCompatKitKat {
 
     static int getAllocationByteCount(Bitmap bitmap) {
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
index d9cf352..b63ea3f 100644
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/drawable/DrawableCompatKitKat.java
@@ -16,13 +16,16 @@
 
 package android.support.v4.graphics.drawable;
 
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 /**
  * Implementation of drawable compatibility that can call KitKat APIs.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class DrawableCompatKitKat {
     public static void setAutoMirrored(Drawable drawable, boolean mirrored) {
         drawable.setAutoMirrored(mirrored);
diff --git a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
index 3cfb9e1..b758563 100644
--- a/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
+++ b/compat/kitkat/android/support/v4/graphics/drawable/DrawableWrapperKitKat.java
@@ -20,7 +20,11 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
+@RequiresApi(19)
+@TargetApi(19)
 class DrawableWrapperKitKat extends DrawableWrapperHoneycomb {
 
     DrawableWrapperKitKat(Drawable drawable) {
diff --git a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java b/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
index 9a803db..b835950 100644
--- a/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/os/EnvironmentCompatKitKat.java
@@ -17,9 +17,13 @@
 package android.support.v4.os;
 
 import android.os.Environment;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 
 import java.io.File;
 
+@RequiresApi(19)
+@TargetApi(19)
 class EnvironmentCompatKitKat {
     public static String getStorageState(File path) {
         return Environment.getStorageState(path);
diff --git a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java b/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
index d3f8eb0..7e873e4 100644
--- a/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ScaleGestureDetectorCompatKitKat.java
@@ -16,14 +16,17 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.ScaleGestureDetector;
-import android.view.MotionEvent;
-import android.content.Context;
 
 /**
  * Implementation of ScaleGestureDetector compatibility that can call KitKat APIs. This class is an
  * implementation detail for ScaleGestureDetectorCompat and should not be used directly.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class ScaleGestureDetectorCompatKitKat {
 
     private ScaleGestureDetectorCompatKitKat() {
diff --git a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
index fd1b327..d864e7b 100644
--- a/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ViewCompatKitKat.java
@@ -16,11 +16,16 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 
 /**
  * KitKat-specific View API implementation.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class ViewCompatKitKat {
     public static int getAccessibilityLiveRegion(View view) {
         return view.getAccessibilityLiveRegion();
diff --git a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
index 5e60a9b..5a00d0c 100644
--- a/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/ViewParentCompatKitKat.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.ViewParent;
 
+@RequiresApi(19)
+@TargetApi(19)
 class ViewParentCompatKitKat {
     public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
             View source, int changeType) {
diff --git a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java b/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
index 64f1969..f507a89 100644
--- a/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
+++ b/compat/kitkat/android/support/v4/view/ViewPropertyAnimatorCompatKK.java
@@ -16,8 +16,12 @@
 package android.support.v4.view;
 
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(19)
+@TargetApi(19)
 class ViewPropertyAnimatorCompatKK {
 
     public static void setUpdateListener(final View view,
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
index 1a2cd94..55e2faa 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityEventCompatKitKat.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityEvent;
 
+@RequiresApi(19)
+@TargetApi(19)
 class AccessibilityEventCompatKitKat {
     public static  void setContentChangeTypes(AccessibilityEvent event, int changeTypes) {
         event.setContentChangeTypes(changeTypes);
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
index 1cdedc0..cc94249 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityManagerCompatKitKat.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.view.accessibility;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 
 /**
  * KitKat-specific AccessibilityManager API implementation.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class AccessibilityManagerCompatKitKat {
 
     public static class TouchExplorationStateChangeListenerWrapper
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
index 1efc7e9..7bb8b1e 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
@@ -17,11 +17,16 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * KitKat-specific AccessibilityNodeInfo API implementation.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class AccessibilityNodeInfoCompatKitKat {
     private static final byte TRAIT_UNSET = -1;
     private static final String TRAITS_KEY =
diff --git a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
index 3aa475d..a2bbbdc 100644
--- a/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/view/accessibility/AccessibilityNodeProviderCompatKitKat.java
@@ -17,6 +17,8 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 
@@ -25,6 +27,9 @@
 /**
  * KitKat-specific AccessibilityNodeProvider API implementation.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class AccessibilityNodeProviderCompatKitKat {
     interface AccessibilityNodeInfoBridge {
         public Object createAccessibilityNodeInfo(int virtualViewId);
diff --git a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
index 7b8d8dd..9678dba 100644
--- a/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/ListPopupWindowCompatKitKat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.widget.ListPopupWindow;
@@ -23,6 +25,9 @@
 /**
  * Implementation of ListPopupWindow compatibility that can call KitKat APIs.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class ListPopupWindowCompatKitKat {
     public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
         return ((ListPopupWindow) listPopupWindow).createDragToOpenListener(src);
diff --git a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java b/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
index f1a66dc..ab2ff53 100644
--- a/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/ListViewCompatKitKat.java
@@ -16,9 +16,12 @@
 
 package android.support.v4.widget;
 
-import android.widget.AbsListView;
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.widget.ListView;
 
+@RequiresApi(19)
+@TargetApi(19)
 class ListViewCompatKitKat {
     static void scrollListBy(final ListView listView, int y) {
         listView.scrollListBy(y);
diff --git a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
index 4558798..da7bc7e 100644
--- a/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/PopupMenuCompatKitKat.java
@@ -16,12 +16,17 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View.OnTouchListener;
 import android.widget.PopupMenu;
 
 /**
  * Implementation of PopupMenu compatibility that can call KitKat APIs.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class PopupMenuCompatKitKat {
     public static OnTouchListener getDragToOpenListener(Object popupMenu) {
         return ((PopupMenu) popupMenu).getDragToOpenListener();
diff --git a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
index 4333f4a..20b0af4 100644
--- a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
+++ b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
@@ -16,14 +16,17 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.annotation.TargetApi;
 import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.ListPopupWindow;
 import android.widget.PopupWindow;
 
 /**
  * Implementation of PopupWindow compatibility that can call KitKat APIs.
  */
+
+@RequiresApi(19)
+@TargetApi(19)
 class PopupWindowCompatKitKat {
     public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
             int gravity) {
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
new file mode 100644
index 0000000..12b3ab0
--- /dev/null
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.support.v4.app;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.*;
+
+@RunWith(AndroidJUnit4.class)
+public class NotificationCompatTest {
+
+    @SmallTest
+    @Test
+    public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable {
+        NotificationCompat.Action a = newActionBuilder()
+                .addRemoteInput(new RemoteInput("a", "b", null, false, null)).build();
+
+        NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
+
+        assertSame(a.getRemoteInputs()[0], aCopy.getRemoteInputs()[0]);
+    }
+
+    @SmallTest
+    @Test
+    public void testNotificationActionBuilder_copiesAllowGeneratedReplies() throws Throwable {
+        NotificationCompat.Action a = newActionBuilder()
+                .setAllowGeneratedReplies(true).build();
+
+        NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build();
+
+        assertEquals(a.getAllowGeneratedReplies(), aCopy.getAllowGeneratedReplies());
+    }
+
+    private NotificationCompat.Action.Builder newActionBuilder() {
+        return new NotificationCompat.Action.Builder(0, "title", null);
+    }
+}
\ No newline at end of file
diff --git a/compat/tests/java/android/support/v4/content/ContextCompatTest.java b/compat/tests/java/android/support/v4/content/ContextCompatTest.java
index edcdcd2..31d6b2c 100644
--- a/compat/tests/java/android/support/v4/content/ContextCompatTest.java
+++ b/compat/tests/java/android/support/v4/content/ContextCompatTest.java
@@ -15,8 +15,7 @@
  */
 package android.support.v4.content;
 
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -24,13 +23,14 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.compat.test.R;
+import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.ThemedYellowActivity;
 import android.support.v4.testutils.TestUtils;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.DisplayMetrics;
 
-import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
 
 @SmallTest
 public class ContextCompatTest extends BaseInstrumentationTestCase<ThemedYellowActivity> {
diff --git a/compat/tests/java/android/support/v4/content/ModernAsyncTaskTest.java b/compat/tests/java/android/support/v4/content/ModernAsyncTaskTest.java
index 3e13f44..0697950 100644
--- a/compat/tests/java/android/support/v4/content/ModernAsyncTaskTest.java
+++ b/compat/tests/java/android/support/v4/content/ModernAsyncTaskTest.java
@@ -20,8 +20,8 @@
 
 import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
index 716796e..e1180a0 100644
--- a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
+++ b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
@@ -15,20 +15,21 @@
  */
 package android.support.v4.content.res;
 
+import static org.junit.Assert.assertEquals;
+
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.compat.test.R;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.v4.testutils.TestUtils;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.DisplayMetrics;
+
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-
 @SmallTest
 public class ResourcesCompatTest {
     private Resources mResources;
diff --git a/compat/tests/java/android/support/v4/graphics/DrawableCompatTest.java b/compat/tests/java/android/support/v4/graphics/DrawableCompatTest.java
index 669f979..f909d6d 100644
--- a/compat/tests/java/android/support/v4/graphics/DrawableCompatTest.java
+++ b/compat/tests/java/android/support/v4/graphics/DrawableCompatTest.java
@@ -16,6 +16,14 @@
 
 package android.support.v4.graphics;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
@@ -23,15 +31,13 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Build;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.graphics.drawable.DrawableCompat;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DrawableCompatTest {
diff --git a/compat/tests/java/android/support/v4/text/IcuCompatTest.java b/compat/tests/java/android/support/v4/text/IcuCompatTest.java
index 20aaa9b..41fc81e 100644
--- a/compat/tests/java/android/support/v4/text/IcuCompatTest.java
+++ b/compat/tests/java/android/support/v4/text/IcuCompatTest.java
@@ -17,7 +17,7 @@
 package android.support.v4.text;
 
 import android.os.Build;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import junit.framework.TestCase;
 
diff --git a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
index 45779c7..b30ded8 100644
--- a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
+++ b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
@@ -22,9 +22,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.util.PatternsCompat;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.style.URLSpan;
@@ -39,8 +39,11 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+/**
+ * Test {@link LinkifyCompat}.
+ */
+@MediumTest
 @RunWith(AndroidJUnit4.class)
-@SmallTest
 public class LinkifyCompatTest {
     private static final Pattern LINKIFY_TEST_PATTERN = Pattern.compile(
             "(test:)?[a-zA-Z0-9]+(\\.pattern)?");
@@ -77,7 +80,7 @@
     };
 
     @Test
-    public void testAddLinks1() {
+    public void testAddLinksToSpannable() {
         // Verify URLs including the ones that have new gTLDs, and the
         // ones that look like gTLDs (and so are accepted by linkify)
         // and the ones that should not be linkified due to non-compliant
@@ -115,7 +118,7 @@
     }
 
     @Test
-    public void testAddLinks2() {
+    public void testAddLinksToSpannableWithScheme() {
         String text = "google.pattern, test:AZ0101.pattern";
 
         SpannableString spannable = new SpannableString(text);
@@ -126,7 +129,7 @@
         assertEquals("test:AZ0101.pattern", spans[1].getURL());
 
         try {
-            LinkifyCompat.addLinks((Spannable)null, LINKIFY_TEST_PATTERN, "Test:");
+            LinkifyCompat.addLinks((Spannable) null, LINKIFY_TEST_PATTERN, "Test:");
             fail("Should throw NullPointerException!");
         } catch (NullPointerException e) {
         }
@@ -198,7 +201,7 @@
     }
 
     @Test
-    public void testAddLinks4() {
+    public void testAddLinksPhoneNumbers() {
         String numbersInvalid = "123456789 not a phone number";
         String numbersUKLocal = "tel:(0812)1234560 (0812)1234561";
         String numbersUSLocal = "tel:(812)1234562 (812)123.4563 "
@@ -294,181 +297,180 @@
     // WEB_URLS Related Tests
 
     @Test
-    public void testAddLinks_doesNotAddLinksForUrlWithoutProtocolAndWithoutKnownTld()
-            throws Exception {
+    public void testAddLinks_doesNotAddLinksForUrlWithoutProtocolAndWithoutKnownTld() {
         Spannable spannable = new SpannableString("hey man.its me");
         boolean linksAdded = LinkifyCompat.addLinks(spannable, Linkify.ALL);
         assertFalse("Should not add link with unknown TLD", linksAdded);
     }
 
     @Test
-    public void testAddLinks_shouldNotAddEmailAddressAsUrl() throws Exception {
+    public void testAddLinks_shouldNotAddEmailAddressAsUrl() {
         String url = "name@gmail.com";
-        assertAddLinksWithWebUrlFails("Should not recognize email address as URL", url);
+        verifyAddLinksWithWebUrlFails("Should not recognize email address as URL", url);
     }
 
-    public void testAddLinks_acceptsUrlsWithCommasInRequestParameterValues() throws Exception {
+    @Test
+    public void testAddLinks_acceptsUrlsWithCommasInRequestParameterValues() {
         String url = "https://android.com/path?ll=37.4221,-122.0836&z=17&pll=37.4221,-122.0836";
-        assertAddLinksWithWebUrlSucceeds("Should accept commas", url);
+        verifyAddLinksWithWebUrlSucceeds("Should accept commas", url);
     }
 
     @Test
-    public void testAddLinks_addsLinksForUrlWithProtocolWithoutTld() throws Exception {
+    public void testAddLinks_addsLinksForUrlWithProtocolWithoutTld() {
         String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2";
-        assertAddLinksWithWebUrlSucceeds("Should accept URL starting with protocol but does not" +
-                " have TLD", url);
+        verifyAddLinksWithWebUrlSucceeds("Should accept URL starting with protocol but does not"
+                + " have TLD", url);
     }
 
     @Test
-    public void testAddLinks_matchesProtocolCaseInsensitive() throws Exception {
+    public void testAddLinks_matchesProtocolCaseInsensitive() {
         String url = "hTtP://android.com";
-        assertAddLinksWithWebUrlSucceeds("Protocol matching should be case insensitive", url);
+        verifyAddLinksWithWebUrlSucceeds("Protocol matching should be case insensitive", url);
     }
 
     @Test
-    public void testAddLinks_matchesValidUrlWithSchemeAndHostname() throws Exception {
+    public void testAddLinks_matchesValidUrlWithSchemeAndHostname() {
         String url = "http://www.android.com";
-        assertAddLinksWithWebUrlSucceeds("Should match valid URL with scheme and hostname", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match valid URL with scheme and hostname", url);
     }
 
     @Test
-    public void testAddLinks_matchesValidUrlWithSchemeHostnameAndNewTld() throws Exception {
+    public void testAddLinks_matchesValidUrlWithSchemeHostnameAndNewTld() {
         String url = "http://www.android.me";
-        assertAddLinksWithWebUrlSucceeds("Should match valid URL with scheme hostname and new TLD",
+        verifyAddLinksWithWebUrlSucceeds("Should match valid URL with scheme hostname and new TLD",
                 url);
     }
 
     @Test
-    public void testAddLinks_matchesValidUrlWithHostnameAndNewTld() throws Exception {
+    public void testAddLinks_matchesValidUrlWithHostnameAndNewTld() {
         String url = "android.camera";
-        assertAddLinksWithWebUrlSucceeds("Should match valid URL with hostname and new TLD", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match valid URL with hostname and new TLD", url);
     }
 
     @Test
-    public void testAddLinks_matchesPunycodeUrl() throws Exception {
+    public void testAddLinks_matchesPunycodeUrl() {
         String url = "http://xn--fsqu00a.xn--unup4y";
-        assertAddLinksWithWebUrlSucceeds("Should match Punycode URL", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match Punycode URL", url);
     }
 
     @Test
-    public void testAddLinks_matchesPunycodeUrlWithoutProtocol() throws Exception {
+    public void testAddLinks_matchesPunycodeUrlWithoutProtocol() {
         String url = "xn--fsqu00a.xn--unup4y";
-        assertAddLinksWithWebUrlSucceeds("Should match Punycode URL without protocol", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match Punycode URL without protocol", url);
     }
 
     @Test
-    public void testAddLinks_doesNotMatchPunycodeTldThatStartsWithDash() throws Exception {
+    public void testAddLinks_doesNotMatchPunycodeTldThatStartsWithDash() {
         String url = "xn--fsqu00a.-xn--unup4y";
-        assertAddLinksWithWebUrlFails("Should not match Punycode TLD that starts with dash", url);
+        verifyAddLinksWithWebUrlFails("Should not match Punycode TLD that starts with dash", url);
     }
 
     @Test
-    public void testAddLinks_partiallyMatchesPunycodeTldThatEndsWithDash() throws Exception {
+    public void testAddLinks_partiallyMatchesPunycodeTldThatEndsWithDash() {
         String url = "http://xn--fsqu00a.xn--unup4y-";
-        assertAddLinksWithWebUrlPartiallyMatches("Should partially match Punycode TLD that ends " +
-                "with dash", "http://xn--fsqu00a.xn--unup4y", url);
+        verifyAddLinksWithWebUrlPartiallyMatches("Should partially match Punycode TLD that ends "
+                + "with dash", "http://xn--fsqu00a.xn--unup4y", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithUnicodeDomainName() throws Exception {
+    public void testAddLinks_matchesUrlWithUnicodeDomainName() {
         String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithUnicodeDomainNameWithoutProtocol() throws Exception {
+    public void testAddLinks_matchesUrlWithUnicodeDomainNameWithoutProtocol() {
         String url = "\uD604\uAE08\uC601\uC218\uC99D.kr";
-        assertAddLinksWithWebUrlSucceeds("Should match URL without protocol and with Unicode " +
-                "domain name", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL without protocol and with Unicode "
+                + "domain name", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithUnicodeDomainNameAndTld() throws Exception {
+    public void testAddLinks_matchesUrlWithUnicodeDomainNameAndTld() {
         String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name and TLD", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode domain name and TLD", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithUnicodePath() throws Exception {
+    public void testAddLinks_matchesUrlWithUnicodePath() {
         String url = "http://android.com/\u2019/a";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with Unicode path", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with Unicode path", url);
     }
 
     @Test
-    public void testAddLinks_matchesValidUrlWithPort() throws Exception {
+    public void testAddLinks_matchesValidUrlWithPort() {
         String url = "http://www.example.com:8080";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with port", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with port", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithPortAndQuery() throws Exception {
+    public void testAddLinks_matchesUrlWithPortAndQuery() {
         String url = "http://www.example.com:8080/?foo=bar";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with port and query", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with port and query", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlWithTilde() throws Exception {
+    public void testAddLinks_matchesUrlWithTilde() {
         String url = "http://www.example.com:8080/~user/?foo=bar";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with tilde", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with tilde", url);
     }
 
     @Test
-    public void testAddLinks_matchesUrlStartingWithHttpAndDoesNotHaveTld() throws Exception {
+    public void testAddLinks_matchesUrlStartingWithHttpAndDoesNotHaveTld() {
         String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2";
-        assertAddLinksWithWebUrlSucceeds("Should match URL without a TLD and starting with http",
+        verifyAddLinksWithWebUrlSucceeds("Should match URL without a TLD and starting with http",
                 url);
     }
 
     @Test
-    public void testAddLinks_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld() throws Exception {
+    public void testAddLinks_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld() {
         String url = "thank.you";
-        assertAddLinksWithWebUrlFails("Should not match URL that does not start with a protocol " +
-                "and does not contain a known TLD", url);
+        verifyAddLinksWithWebUrlFails("Should not match URL that does not start with a protocol "
+                + "and does not contain a known TLD", url);
     }
 
     @Test
-    public void testAddLinks_matchesValidUrlWithEmoji() throws Exception {
+    public void testAddLinks_matchesValidUrlWithEmoji() {
         String url = "Thank\u263A.com";
-        assertAddLinksWithWebUrlSucceeds("Should match URL with emoji", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match URL with emoji", url);
     }
 
     @Test
-    public void testAddLinks_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld()
-            throws Exception {
+    public void testAddLinks_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld() {
         String url = "Thank\u263A.you";
-        assertAddLinksWithWebUrlFails("Should not match URLs containing emoji and with unknown " +
-                "TLD", url);
+        verifyAddLinksWithWebUrlFails("Should not match URLs containing emoji and with unknown "
+                + "TLD", url);
     }
 
     @Test
-    public void testAddLinks_matchesDomainNameWithSurrogatePairs() throws Exception {
+    public void testAddLinks_matchesDomainNameWithSurrogatePairs() {
         String url = "android\uD83C\uDF38.com";
-        assertAddLinksWithWebUrlSucceeds("Should match domain name with Unicode surrogate pairs",
+        verifyAddLinksWithWebUrlSucceeds("Should match domain name with Unicode surrogate pairs",
                 url);
     }
 
     @Test
-    public void testAddLinks_matchesTldWithSurrogatePairs() throws Exception {
+    public void testAddLinks_matchesTldWithSurrogatePairs() {
         String url = "http://android.\uD83C\uDF38com";
-        assertAddLinksWithWebUrlSucceeds("Should match TLD with Unicode surrogate pairs", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match TLD with Unicode surrogate pairs", url);
     }
 
     @Test
-    public void testAddLinks_doesNotMatchUrlWithExcludedSurrogate() throws Exception {
+    public void testAddLinks_doesNotMatchUrlWithExcludedSurrogate() {
         String url = "android\uD83F\uDFFE.com";
-        assertAddLinksWithWebUrlFails("Should not match URL with excluded Unicode surrogate" +
-                " pair",  url);
+        verifyAddLinksWithWebUrlFails("Should not match URL with excluded Unicode surrogate"
+                + " pair",  url);
     }
 
     @Test
-    public void testAddLinks_matchesPathWithSurrogatePairs() throws Exception {
+    public void testAddLinks_matchesPathWithSurrogatePairs() {
         String url = "http://android.com/path-with-\uD83C\uDF38?v=\uD83C\uDF38f";
-        assertAddLinksWithWebUrlSucceeds("Should match path and query with Unicode surrogate pairs",
+        verifyAddLinksWithWebUrlSucceeds("Should match path and query with Unicode surrogate pairs",
                 url);
     }
 
     @Test
-    public void testAddLinks__doesNotMatchUnicodeSpaces() throws Exception {
+    public void testAddLinks__doesNotMatchUnicodeSpaces() {
         String part1 = "http://and";
         String part2 = "roid.com";
         String[] emptySpaces = new String[]{
@@ -492,269 +494,260 @@
 
         for (String emptySpace : emptySpaces) {
             String url = part1 + emptySpace + part2;
-            assertAddLinksWithWebUrlPartiallyMatches("Should not include empty space with code: " +
-                    emptySpace.codePointAt(0), part1, url);
+            verifyAddLinksWithWebUrlPartiallyMatches("Should not include empty space with code: "
+                    + emptySpace.codePointAt(0), part1, url);
         }
     }
 
     @Test
-    public void testAddLinks_matchesDomainNameWithDash() throws Exception {
+    public void testAddLinks_matchesDomainNameWithDash() {
         String url = "http://a-nd.r-oid.com";
-        assertAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
 
         url = "a-nd.r-oid.com";
-        assertAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match domain name with '-'", url);
     }
 
     @Test
-    public void testAddLinks_matchesDomainNameWithUnderscore() throws Exception {
+    public void testAddLinks_matchesDomainNameWithUnderscore() {
         String url = "http://a_nd.r_oid.com";
-        assertAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
 
         url = "a_nd.r_oid.com";
-        assertAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match domain name with '_'", url);
     }
 
     @Test
-    public void testAddLinks_matchesPathAndQueryWithDollarSign() throws Exception {
+    public void testAddLinks_matchesPathAndQueryWithDollarSign() {
         String url = "http://android.com/path$?v=$val";
-        assertAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
 
         url = "android.com/path$?v=$val";
-        assertAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match path and query with '$'", url);
     }
 
     @Test
-    public void testAddLinks_matchesEmptyPathWithQueryParams() throws Exception {
+    public void testAddLinks_matchesEmptyPathWithQueryParams() {
         String url = "http://android.com?q=v";
-        assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
 
         url = "android.com?q=v";
-        assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
 
         url = "http://android.com/?q=v";
-        assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
 
         url = "android.com/?q=v";
-        assertAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
+        verifyAddLinksWithWebUrlSucceeds("Should match empty path with query params", url);
     }
 
     // EMAIL_ADDRESSES Related Tests
 
     @Test
-    public void testAddLinks_email_matchesShortValidEmail() throws Exception {
+    public void testAddLinks_email_matchesShortValidEmail() {
         String email = "a@a.co";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesRegularEmail() throws Exception {
+    public void testAddLinks_email_matchesRegularEmail() {
         String email = "email@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesEmailWithMultipleSubdomains() throws Exception {
+    public void testAddLinks_email_matchesEmailWithMultipleSubdomains() {
         String email = "email@e.somelongdomainnameforandroid.abc.uk";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithDot() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithDot() {
         String email = "e.mail@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithPlus() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithPlus() {
         String email = "e+mail@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithUnderscore() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithUnderscore() {
         String email = "e_mail@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithDash() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithDash() {
         String email = "e-mail@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithApostrophe() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithApostrophe() {
         String email = "e'mail@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithDigits() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithDigits() {
         String email = "123@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesUnicodeLocalPart() throws Exception {
+    public void testAddLinks_email_matchesUnicodeLocalPart() {
         String email = "\uD604\uAE08\uC601\uC218\uC99D@android.kr";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithEmoji() throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithEmoji() {
         String email = "smiley\u263A@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartWithSurrogatePairs()
-            throws Exception {
+    public void testAddLinks_email_matchesLocalPartWithSurrogatePairs() {
         String email = "a\uD83C\uDF38a@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesDomainWithDash() throws Exception {
+    public void testAddLinks_email_matchesDomainWithDash() {
         String email = "email@an-droid.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesUnicodeDomain() throws Exception {
+    public void testAddLinks_email_matchesUnicodeDomain() {
         String email = "email@\uD604\uAE08\uC601\uC218\uC99D.kr";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesUnicodeLocalPartAndDomain()
-            throws Exception {
+    public void testAddLinks_email_matchesUnicodeLocalPartAndDomain() {
         String email = "\uD604\uAE08\uC601\uC218\uC99D@\uD604\uAE08\uC601\uC218\uC99D.kr";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesDomainWithEmoji() throws Exception {
+    public void testAddLinks_email_matchesDomainWithEmoji() {
         String email = "smiley@\u263Aandroid.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesDomainWithSurrogatePairs()
-            throws Exception {
+    public void testAddLinks_email_matchesDomainWithSurrogatePairs() {
         String email = "email@\uD83C\uDF38android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartAndDomainWithSurrogatePairs()
-            throws Exception {
+    public void testAddLinks_email_matchesLocalPartAndDomainWithSurrogatePairs() {
         String email = "a\uD83C\uDF38a@\uD83C\uDF38android.com";
-        assertAddLinksWithEmailSucceeds("Should match email: " + email, email);
+        verifyAddLinksWithEmailSucceeds("Should match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_partiallyMatchesEmailEndingWithDot() throws Exception {
+    public void testAddLinks_partiallyMatchesEmailEndingWithDot() {
         String email = "email@android.co.uk.";
-        assertAddLinksWithEmailPartiallyMatches("Should partially match email ending with dot",
+        verifyAddLinksWithEmailPartiallyMatches("Should partially match email ending with dot",
                 "mailto:email@android.co.uk", email);
     }
 
     @Test
-    public void testAddLinks_email_partiallyMatchesLocalPartStartingWithDot()
-            throws Exception {
+    public void testAddLinks_email_partiallyMatchesLocalPartStartingWithDot() {
         String email = ".email@android.com";
-        assertAddLinksWithEmailPartiallyMatches("Should partially match email starting " +
-                "with dot", "mailto:email@android.com", email);
+        verifyAddLinksWithEmailPartiallyMatches("Should partially match email starting "
+                + "with dot", "mailto:email@android.com", email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchStringWithoutAtSign() throws Exception {
+    public void testAddLinks_email_doesNotMatchStringWithoutAtSign() {
         String email = "android.com";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchPlainString() throws Exception {
+    public void testAddLinks_email_doesNotMatchPlainString() {
         String email = "email";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchEmailWithoutTld() throws Exception {
+    public void testAddLinks_email_doesNotMatchEmailWithoutTld() {
         String email = "email@android";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchLocalPartEndingWithDot()
-            throws Exception {
+    public void testAddLinks_email_doesNotMatchLocalPartEndingWithDot() {
         String email = "email.@android.com";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchDomainStartingWithDash()
-            throws Exception {
+    public void testAddLinks_email_doesNotMatchDomainStartingWithDash() {
         String email = "email@-android.com";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchDomainWithConsecutiveDots()
-            throws Exception {
+    public void testAddLinks_email_doesNotMatchDomainWithConsecutiveDots() {
         String email = "email@android..com";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchEmailWithIp() throws Exception {
+    public void testAddLinks_email_doesNotMatchEmailWithIp() {
         String email = "email@127.0.0.1";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_doesNotMatchEmailWithInvalidTld()
-            throws Exception {
+    public void testAddLinks_email_doesNotMatchEmailWithInvalidTld() {
         String email = "email@android.c";
-        assertAddLinksWithEmailFails("Should not match email: " + email, email);
+        verifyAddLinksWithEmailFails("Should not match email: " + email, email);
     }
 
     @Test
-    public void testAddLinks_email_matchesLocalPartUpTo64Chars() throws Exception {
+    public void testAddLinks_email_matchesLocalPartUpTo64Chars() {
         String localPart = "";
         for (int i = 0; i < 64; i++) {
             localPart += "a";
         }
         String email = localPart + "@android.com";
-        assertAddLinksWithEmailSucceeds("Should match email local part of length: " +
-                localPart.length(), email);
+        verifyAddLinksWithEmailSucceeds("Should match email local part of length: "
+                + localPart.length(), email);
 
         email = localPart + "a@android.com";
-        assertAddLinksWithEmailFails("Should not match email local part of length:" +
-                localPart.length(), email);
+        verifyAddLinksWithEmailFails("Should not match email local part of length:"
+                + localPart.length(), email);
     }
 
     @Test
-    public void testAddLinks_email_matchesSubdomainUpTo63Chars() throws Exception {
+    public void testAddLinks_email_matchesSubdomainUpTo63Chars() {
         String subdomain = "";
         for (int i = 0; i < 63; i++) {
             subdomain += "a";
         }
         String email = "email@" + subdomain + ".com";
 
-        assertAddLinksWithEmailSucceeds("Should match email subdomain of length: " +
-                subdomain.length(), email);
+        verifyAddLinksWithEmailSucceeds("Should match email subdomain of length: "
+                + subdomain.length(), email);
 
         subdomain += "a";
         email = "email@" + subdomain + ".com";
 
-        assertAddLinksWithEmailFails("Should not match email subdomain of length:" +
-                subdomain.length(), email);
+        verifyAddLinksWithEmailFails("Should not match email subdomain of length:"
+                + subdomain.length(), email);
     }
 
     @Test
-    public void testAddLinks_email_matchesDomainUpTo255Chars() throws Exception {
+    public void testAddLinks_email_matchesDomainUpTo255Chars() {
         String domain = "";
         while (domain.length() <= 250) {
             domain += "d.";
@@ -762,42 +755,42 @@
         domain += "com";
         assertEquals(255, domain.length());
         String email = "a@" + domain;
-        assertAddLinksWithEmailSucceeds("Should match email domain of length: " +
-                domain.length(), email);
+        verifyAddLinksWithEmailSucceeds("Should match email domain of length: "
+                + domain.length(), email);
 
         email = email + "m";
-        assertAddLinksWithEmailFails("Should not match email domain of length:" +
-                domain.length(), email);
+        verifyAddLinksWithEmailFails("Should not match email domain of length:"
+                + domain.length(), email);
     }
 
     // Utility functions
-    private static void assertAddLinksWithWebUrlSucceeds(String msg, String url) {
-        assertAddLinksSucceeds(msg, url, Linkify.WEB_URLS);
+    private static void verifyAddLinksWithWebUrlSucceeds(String msg, String url) {
+        verifyAddLinksSucceeds(msg, url, Linkify.WEB_URLS);
     }
 
-    private static void assertAddLinksWithWebUrlFails(String msg, String url) {
-        assertAddLinksFails(msg, url, Linkify.WEB_URLS);
+    private static void verifyAddLinksWithWebUrlFails(String msg, String url) {
+        verifyAddLinksFails(msg, url, Linkify.WEB_URLS);
     }
 
-    private static void assertAddLinksWithWebUrlPartiallyMatches(String msg, String expected,
+    private static void verifyAddLinksWithWebUrlPartiallyMatches(String msg, String expected,
             String url) {
-        assertAddLinksPartiallyMatches(msg, expected, url, Linkify.WEB_URLS);
+        verifyAddLinksPartiallyMatches(msg, expected, url, Linkify.WEB_URLS);
     }
 
-    private static void assertAddLinksWithEmailSucceeds(String msg, String url) {
-        assertAddLinksSucceeds(msg, url, Linkify.EMAIL_ADDRESSES);
+    private static void verifyAddLinksWithEmailSucceeds(String msg, String url) {
+        verifyAddLinksSucceeds(msg, url, Linkify.EMAIL_ADDRESSES);
     }
 
-    private static void assertAddLinksWithEmailFails(String msg, String url) {
-        assertAddLinksFails(msg, url, Linkify.EMAIL_ADDRESSES);
+    private static void verifyAddLinksWithEmailFails(String msg, String url) {
+        verifyAddLinksFails(msg, url, Linkify.EMAIL_ADDRESSES);
     }
 
-    private static void assertAddLinksWithEmailPartiallyMatches(String msg, String expected,
+    private static void verifyAddLinksWithEmailPartiallyMatches(String msg, String expected,
             String url) {
-        assertAddLinksPartiallyMatches(msg, expected, url, Linkify.EMAIL_ADDRESSES);
+        verifyAddLinksPartiallyMatches(msg, expected, url, Linkify.EMAIL_ADDRESSES);
     }
 
-    private static void assertAddLinksSucceeds(String msg, String string, int type) {
+    private static void verifyAddLinksSucceeds(String msg, String string, int type) {
         String str = "start " + string + " end";
         Spannable spannable = new SpannableString(str);
 
@@ -811,13 +804,13 @@
                 str.length() - " end".length(), spannable.getSpanEnd(spans[0]));
     }
 
-    private static void assertAddLinksFails(String msg, String string, int type) {
+    private static void verifyAddLinksFails(String msg, String string, int type) {
         Spannable spannable = new SpannableString("start " + string + " end");
         boolean linksAdded = LinkifyCompat.addLinks(spannable, type);
         assertFalse(msg, linksAdded);
     }
 
-    private static void assertAddLinksPartiallyMatches(String msg, String expected,
+    private static void verifyAddLinksPartiallyMatches(String msg, String expected,
             String string, int type) {
         Spannable spannable = new SpannableString("start " + string + " end");
         boolean linksAdded = LinkifyCompat.addLinks(spannable, type);
@@ -825,4 +818,4 @@
         assertTrue(msg, linksAdded);
         assertEquals(msg, expected, spans[0].getURL().toString());
     }
-}
\ No newline at end of file
+}
diff --git a/compat/tests/java/android/support/v4/util/PatternsCompatTest.java b/compat/tests/java/android/support/v4/util/PatternsCompatTest.java
index ab6ad2a..53ae425 100644
--- a/compat/tests/java/android/support/v4/util/PatternsCompatTest.java
+++ b/compat/tests/java/android/support/v4/util/PatternsCompatTest.java
@@ -15,18 +15,17 @@
  */
 package android.support.v4.util;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
+import java.util.regex.Pattern;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/compat/tests/java/android/support/v4/view/GravityCompatTest.java b/compat/tests/java/android/support/v4/view/GravityCompatTest.java
index 36e557a..f83fd69 100644
--- a/compat/tests/java/android/support/v4/view/GravityCompatTest.java
+++ b/compat/tests/java/android/support/v4/view/GravityCompatTest.java
@@ -15,18 +15,18 @@
  */
 package android.support.v4.view;
 
+import static org.junit.Assert.assertEquals;
+
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.testutils.TestUtils;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.Gravity;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class GravityCompatTest {
diff --git a/compat/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java b/compat/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
index 48d6dab..2d4977c 100644
--- a/compat/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
+++ b/compat/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
@@ -15,16 +15,16 @@
  */
 package android.support.v4.view;
 
+import static org.junit.Assert.assertEquals;
+
 import android.os.Build;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ViewGroup;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MarginLayoutParamsCompatTest {
diff --git a/compat/tests/java/android/support/v4/view/ViewCompatTest.java b/compat/tests/java/android/support/v4/view/ViewCompatTest.java
index 0b102a6..fc614fc 100644
--- a/compat/tests/java/android/support/v4/view/ViewCompatTest.java
+++ b/compat/tests/java/android/support/v4/view/ViewCompatTest.java
@@ -15,22 +15,22 @@
  */
 package android.support.v4.view;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
 import android.app.Activity;
+import android.support.compat.test.R;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.v4.BaseInstrumentationTestCase;
 import android.view.Display;
 import android.view.View;
-import android.support.v4.BaseInstrumentationTestCase;
-import android.support.compat.test.R;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
diff --git a/compat/tests/java/android/support/v4/view/ViewPropertyAnimatorCompatTest.java b/compat/tests/java/android/support/v4/view/ViewPropertyAnimatorCompatTest.java
index b04c195..a3d9877 100644
--- a/compat/tests/java/android/support/v4/view/ViewPropertyAnimatorCompatTest.java
+++ b/compat/tests/java/android/support/v4/view/ViewPropertyAnimatorCompatTest.java
@@ -16,20 +16,19 @@
 
 package android.support.v4.view;
 
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.support.compat.test.R;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.v4.BaseInstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 import org.junit.Before;
 import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @MediumTest
 public class ViewPropertyAnimatorCompatTest extends BaseInstrumentationTestCase<VpaActivity> {
@@ -52,7 +51,7 @@
     @Test
     public void testWithEndAction() throws Throwable {
         final CountDownLatch latch1 = new CountDownLatch(1);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.animate(mView).alpha(0).setDuration(100).withEndAction(new Runnable() {
@@ -68,7 +67,7 @@
         // This test ensures that the endAction listener will be called exactly once
         mNumListenerCalls = 0;
         final CountDownLatch latch2 = new CountDownLatch(1);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.animate(mView).alpha(0).setDuration(50).withEndAction(new Runnable() {
@@ -88,7 +87,7 @@
     @Test
     public void testWithStartAction() throws Throwable {
         final CountDownLatch latch1 = new CountDownLatch(1);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.animate(mView).alpha(0).setDuration(100).withStartAction(new Runnable() {
@@ -104,7 +103,7 @@
         // This test ensures that the startAction listener will be called exactly once
         mNumListenerCalls = 0;
         final CountDownLatch latch2 = new CountDownLatch(1);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.animate(mView).alpha(0).setDuration(50).withStartAction(new Runnable() {
diff --git a/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java b/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
index 1f27813..ac6cdd0 100644
--- a/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
+++ b/compat/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.animation.Interpolator;
 
diff --git a/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java b/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
index 19242cb..98d9498 100644
--- a/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
+++ b/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
@@ -17,26 +17,35 @@
 
 package android.support.v4.widget;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.v4.testutils.LayoutDirectionActions.setLayoutDirection;
+import static android.support.v4.testutils.TextViewActions.setCompoundDrawablesRelative;
+import static android.support.v4.testutils.TextViewActions.setCompoundDrawablesRelativeWithIntrinsicBounds;
+import static android.support.v4.testutils.TextViewActions.setMaxLines;
+import static android.support.v4.testutils.TextViewActions.setMinLines;
+import static android.support.v4.testutils.TextViewActions.setText;
+import static android.support.v4.testutils.TextViewActions.setTextAppearance;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.content.res.Resources;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.compat.test.R;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.testutils.TestUtils;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.TextView;
+
 import org.junit.Before;
 import org.junit.Test;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.v4.testutils.LayoutDirectionActions.setLayoutDirection;
-import static android.support.v4.testutils.TextViewActions.*;
-import static org.junit.Assert.*;
-
 public class TextViewCompatTest extends BaseInstrumentationTestCase<TextViewTestActivity> {
     private static final String TAG = "TextViewCompatTest";
 
diff --git a/core-ui/Android.mk b/core-ui/Android.mk
index 3765b04..bdad9f9 100644
--- a/core-ui/Android.mk
+++ b/core-ui/Android.mk
@@ -14,63 +14,29 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-ui-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-core-ui \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-core-ui
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,honeycomb) \
+    $(call all-java-files-under,ics) \
+    $(call all-java-files-under,jellybean-mr2) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-ui-api21
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java b/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
index 07cc3fa..ff2e93d 100644
--- a/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
+++ b/core-ui/api21/android/support/v4/widget/DrawerLayoutCompatApi21.java
@@ -17,9 +17,11 @@
 
 package android.support.v4.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,6 +30,8 @@
 /**
  * Provides functionality for DrawerLayout unique to API 21
  */
+@RequiresApi(21)
+@TargetApi(21)
 class DrawerLayoutCompatApi21 {
 
     private static final int[] THEME_ATTRS = {
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 5ae0655..56afd5e 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -1,9 +1,8 @@
 apply plugin: 'com.android.library'
 archivesBaseName = 'support-core-ui'
 
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.coreui.apiTargets)
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-compat')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -19,22 +18,24 @@
 
 sourceCompatibility = JavaVersion.VERSION_1_7
 targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.coreui.dependencies)
 
 android {
-    compileSdkVersion 9
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
-
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
+        main.java.srcDirs = [
+                'honeycomb',
+                'ics',
+                'jellybean-mr2',
+                'api21',
+                'java'
+        ]
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/java'
@@ -93,11 +94,6 @@
         exclude('android/service/media/**')
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java b/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
index bd27954..bd4fd97 100644
--- a/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
+++ b/core-ui/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
@@ -18,12 +18,13 @@
 package android.support.v4.app;
 
 import android.R;
+import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
-import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -38,6 +39,8 @@
  * in an action bar without some really gross hacks. Since the MR2 SDK is not published as of
  * this writing, the new API is accessed via reflection here if available.
  */
+@RequiresApi(11)
+@TargetApi(11)
 class ActionBarDrawerToggleHoneycomb {
     private static final String TAG = "ActionBarDrawerToggleHoneycomb";
 
diff --git a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java b/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
index 99cc867..c8e70bd 100644
--- a/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
+++ b/core-ui/ics/android/support/v4/view/PagerTitleStripIcs.java
@@ -16,13 +16,17 @@
 
 package android.support.v4.view;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.text.method.SingleLineTransformationMethod;
 import android.view.View;
 import android.widget.TextView;
 
 import java.util.Locale;
 
+@RequiresApi(14)
+@TargetApi(14)
 class PagerTitleStripIcs {
     public static void setSingleLineAllCaps(TextView text) {
         text.setTransformationMethod(new SingleLineAllCapsTransform(text.getContext()));
diff --git a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
index 197f96e..4c99341 100644
--- a/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
+++ b/core-ui/java/android/support/v4/app/ActionBarDrawerToggle.java
@@ -17,6 +17,7 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -27,6 +28,7 @@
 import android.os.Build;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.StringRes;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.GravityCompat;
@@ -132,6 +134,8 @@
         }
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     private static class ActionBarDrawerToggleImplHC implements ActionBarDrawerToggleImpl {
         ActionBarDrawerToggleImplHC() {
         }
@@ -155,6 +159,8 @@
         }
     }
 
+    @RequiresApi(18)
+    @TargetApi(18)
     private static class ActionBarDrawerToggleImplJellybeanMR2
             implements ActionBarDrawerToggleImpl {
         ActionBarDrawerToggleImplJellybeanMR2() {
diff --git a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java b/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
index 5d98d88..f4dba39 100644
--- a/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
+++ b/core-ui/jellybean-mr2/android/support/v4/app/ActionBarDrawerToggleJellybeanMR2.java
@@ -18,13 +18,16 @@
 package android.support.v4.app;
 
 import android.R;
+import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(18)
+@TargetApi(18)
 class ActionBarDrawerToggleJellybeanMR2 {
     private static final String TAG = "ActionBarDrawerToggleImplJellybeanMR2";
 
diff --git a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
index aba2512..0631299 100644
--- a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
+++ b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
@@ -61,10 +61,10 @@
 import android.graphics.Color;
 import android.support.coreui.test.R;
 import android.support.test.espresso.ViewAction;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.testutils.TestUtilsMatchers;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.View;
diff --git a/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java b/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
index 836dd5c..b875c37 100644
--- a/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
+++ b/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
@@ -16,26 +16,28 @@
 
 package android.support.v4.widget;
 
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.coreui.test.R;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.List;
 
-import static junit.framework.Assert.assertFalse;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeTrue;
-
 @SmallTest
 public class ExploreByTouchHelperTest extends BaseInstrumentationTestCase<ExploreByTouchHelperTestActivity> {
     private View mHost;
diff --git a/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java b/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
index 0d0ac22..4a10559 100644
--- a/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
+++ b/core-ui/tests/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
@@ -25,15 +25,19 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-import android.app.Activity;
-import android.support.test.espresso.action.ViewActions;
 import android.support.coreui.test.R;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.support.v4.testutils.PollingCheck;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 
 import org.junit.Before;
@@ -83,12 +87,12 @@
                 }
             });
 
-            new PollingCheck(TIMEOUT) {
+            PollingCheck.waitFor(TIMEOUT, new PollingCheck.PollingCheckCondition() {
                 @Override
-                protected boolean check() {
-                    return mSwipeRefresh.isRefreshing();
+                public boolean canProceed() {
+                    return !mSwipeRefresh.isRefreshing();
                 }
-            }.run();
+            });
         }
         verify(mockListener, times(0)).onRefresh();
     }
diff --git a/core-utils/Android.mk b/core-utils/Android.mk
index 3b64d2b..d3f113e 100644
--- a/core-utils/Android.mk
+++ b/core-utils/Android.mk
@@ -14,107 +14,33 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-honeycomb
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V20 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api20
-LOCAL_SDK_VERSION := 20
-LOCAL_SRC_FILES := $(call all-java-files-under, api20)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api20
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-core-utils-api24
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-core-utils-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-core-utils \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-core-utils
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,gingerbread) \
+    $(call all-java-files-under,honeycomb) \
+    $(call all-java-files-under,jellybean) \
+    $(call all-java-files-under,kitkat) \
+    $(call all-java-files-under,api20) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,api23) \
+    $(call all-java-files-under,api24) \
+    $(call all-java-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-core-utils-api24
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
index ce62106..831e9dd 100644
--- a/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
+++ b/core-utils/api20/android/support/v4/print/PrintHelperApi20.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.print;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 
 /**
  * Api20 specific PrintManager API implementation.
  */
+@RequiresApi(20)
+@TargetApi(20)
 class PrintHelperApi20 extends PrintHelperKitkat {
     PrintHelperApi20(Context context) {
         super(context);
diff --git a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
index 081feea..d521293 100644
--- a/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
+++ b/core-utils/api21/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
@@ -16,13 +16,17 @@
 
 package android.support.v4.graphics.drawable;
 
+import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 import android.view.Gravity;
 import android.view.View;
 
+@RequiresApi(21)
+@TargetApi(21)
 class RoundedBitmapDrawable21 extends RoundedBitmapDrawable {
     protected RoundedBitmapDrawable21(Resources res, Bitmap bitmap) {
         super(res, bitmap);
diff --git a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
index b583a0d..03667b3 100644
--- a/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
+++ b/core-utils/api21/android/support/v4/provider/DocumentsContractApi21.java
@@ -16,15 +16,19 @@
 
 package android.support.v4.provider;
 
+import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.DocumentsContract;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 
 import java.util.ArrayList;
 
+@RequiresApi(21)
+@TargetApi(21)
 class DocumentsContractApi21 {
     private static final String TAG = "DocumentFile";
 
diff --git a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
index ba646e3..e2f6d69 100644
--- a/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
+++ b/core-utils/api23/android/support/v4/print/PrintHelperApi23.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.print;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.print.PrintAttributes;
+import android.support.annotation.RequiresApi;
 
 /**
  * Api23 specific PrintManager API implementation.
  */
+@RequiresApi(23)
+@TargetApi(23)
 class PrintHelperApi23 extends PrintHelperApi20 {
     @Override
     protected PrintAttributes.Builder copyAttributes(PrintAttributes other) {
diff --git a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
index 3815941..36edfbd 100644
--- a/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
+++ b/core-utils/api24/android/support/v4/print/PrintHelperApi24.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.print;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 
 /**
  * Api24 specific PrintManager API implementation.
  */
+@RequiresApi(24)
+@TargetApi(24)
 class PrintHelperApi24 extends PrintHelperApi23 {
     PrintHelperApi24(Context context) {
         super(context);
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index f6ceb14..97325a1 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -1,9 +1,8 @@
 apply plugin: 'com.android.library'
 archivesBaseName = 'support-core-utils'
 
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.coreutils.apiTargets)
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-compat')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -17,24 +16,28 @@
     testCompile 'junit:junit:4.12'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.coreutils.dependencies)
-
 android {
-    compileSdkVersion 9
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
 
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
+        main.java.srcDirs = [
+                'gingerbread',
+                'honeycomb',
+                'jellybean',
+                'kitkat',
+                'api20',
+                'api21',
+                'api23',
+                'api24',
+                'java'
+        ]
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/java'
@@ -88,11 +91,6 @@
         exclude('android/service/media/**')
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
index 973d8dc..72b6abb 100644
--- a/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
+++ b/core-utils/gingerbread/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
@@ -15,6 +15,7 @@
  */
 package android.support.v4.graphics.drawable;
 
+import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
@@ -27,6 +28,7 @@
 import android.graphics.RectF;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
 
@@ -40,6 +42,8 @@
  * {@link android.graphics.Canvas}.
  * </p>
  */
+@RequiresApi(9)
+@TargetApi(9)
 public abstract class RoundedBitmapDrawable extends Drawable {
     private static final int DEFAULT_PAINT_FLAGS =
             Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
diff --git a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java b/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
index 8bb602e..d970019 100644
--- a/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
+++ b/core-utils/honeycomb/android/support/v4/app/TaskStackBuilderHoneycomb.java
@@ -16,13 +16,17 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
 
 /**
  * Implementation of TaskStackBuilder that can call Honeycomb APIs.
  */
+@RequiresApi(11)
+@TargetApi(11)
 class TaskStackBuilderHoneycomb {
     public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
             Intent[] intents, int flags) {
diff --git a/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java b/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java
index 5d2a2ad..894c708 100644
--- a/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java
+++ b/core-utils/java/android/support/v4/content/res/TypedArrayUtils.java
@@ -68,6 +68,15 @@
         return val;
     }
 
+    public static CharSequence getText(TypedArray a, @StyleableRes int index,
+            @StyleableRes int fallbackIndex) {
+        CharSequence val = a.getText(index);
+        if (val == null) {
+            val = a.getText(fallbackIndex);
+        }
+        return val;
+    }
+
     public static CharSequence[] getTextArray(TypedArray a, @StyleableRes int index,
             @StyleableRes int fallbackIndex) {
         CharSequence[] val = a.getTextArray(index);
diff --git a/core-utils/java/android/support/v4/print/PrintHelper.java b/core-utils/java/android/support/v4/print/PrintHelper.java
index 88d5387..87899e2 100644
--- a/core-utils/java/android/support/v4/print/PrintHelper.java
+++ b/core-utils/java/android/support/v4/print/PrintHelper.java
@@ -195,9 +195,9 @@
         @Override
         public void printBitmap(String jobName, Bitmap bitmap,
                 final OnPrintFinishCallback callback) {
-            RealHelper.OnPrintFinishCallback delegateCallback = null;
+            PrintHelperKitkat.OnPrintFinishCallback delegateCallback = null;
             if (callback != null) {
-                delegateCallback = new RealHelper.OnPrintFinishCallback() {
+                delegateCallback = new PrintHelperKitkat.OnPrintFinishCallback() {
                     @Override
                     public void onFinish() {
                         callback.onFinish();
@@ -210,9 +210,9 @@
         @Override
         public void printBitmap(String jobName, Uri imageFile,
                 final OnPrintFinishCallback callback) throws FileNotFoundException {
-            RealHelper.OnPrintFinishCallback delegateCallback = null;
+            PrintHelperKitkat.OnPrintFinishCallback delegateCallback = null;
             if (callback != null) {
-                delegateCallback = new RealHelper.OnPrintFinishCallback() {
+                delegateCallback = new PrintHelperKitkat.OnPrintFinishCallback() {
                     @Override
                     public void onFinish() {
                         callback.onFinish();
diff --git a/core-utils/java/android/support/v4/provider/DocumentFile.java b/core-utils/java/android/support/v4/provider/DocumentFile.java
index 95e9117..c573db0 100644
--- a/core-utils/java/android/support/v4/provider/DocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/DocumentFile.java
@@ -235,6 +235,14 @@
     public abstract boolean isFile();
 
     /**
+     * Indicates if this file represents a <em>virtual</em> document.
+     *
+     * @return {@code true} if this file is a virtual document.
+     * @see android.provider.DocumentsContract.Document#FLAG_VIRTUAL_DOCUMENT
+     */
+    public abstract boolean isVirtual();
+
+    /**
      * Returns the time when this file was last modified, measured in
      * milliseconds since January 1st, 1970, midnight. Returns 0 if the file
      * does not exist, or if the modified time is unknown.
diff --git a/core-utils/java/android/support/v4/provider/RawDocumentFile.java b/core-utils/java/android/support/v4/provider/RawDocumentFile.java
index 9dc97c6..f6ca912 100644
--- a/core-utils/java/android/support/v4/provider/RawDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/RawDocumentFile.java
@@ -89,6 +89,11 @@
     }
 
     @Override
+    public boolean isVirtual() {
+        return false;
+    }
+
+    @Override
     public long lastModified() {
         return mFile.lastModified();
     }
diff --git a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
index 673d1dd..3a4ccf2 100644
--- a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
@@ -66,6 +66,11 @@
     }
 
     @Override
+    public boolean isVirtual() {
+        return DocumentsContractApi19.isVirtual(mContext, mUri);
+    }
+
+    @Override
     public long lastModified() {
         return DocumentsContractApi19.lastModified(mContext, mUri);
     }
diff --git a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
index 96983c0..02975bd 100644
--- a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
@@ -67,6 +67,11 @@
     }
 
     @Override
+    public boolean isVirtual() {
+        return DocumentsContractApi19.isVirtual(mContext, mUri);
+    }
+
+    @Override
     public long lastModified() {
         return DocumentsContractApi19.lastModified(mContext, mUri);
     }
diff --git a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java b/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
index a9a76c2..86a28b3 100644
--- a/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
+++ b/core-utils/jellybean/android/support/v4/app/NavUtilsJB.java
@@ -16,10 +16,14 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class NavUtilsJB {
     public static Intent getParentActivityIntent(Activity activity) {
         return activity.getParentActivityIntent();
diff --git a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java b/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
index 8b79b1b..96c3dda 100644
--- a/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
+++ b/core-utils/jellybean/android/support/v4/app/TaskStackBuilderJellybean.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(16)
+@TargetApi(16)
 class TaskStackBuilderJellybean {
 
     public static PendingIntent getActivitiesPendingIntent(Context context, int requestCode,
diff --git a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
index 6e2149e..08a41b2 100644
--- a/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
+++ b/core-utils/kitkat/android/support/v4/print/PrintHelperKitkat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.print;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
@@ -34,11 +35,12 @@
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
 import android.print.PrintDocumentAdapter;
 import android.print.PrintDocumentInfo;
 import android.print.PrintManager;
-import android.print.PrintAttributes.MediaSize;
 import android.print.pdf.PrintedPdfDocument;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 
 import java.io.FileNotFoundException;
@@ -49,6 +51,8 @@
 /**
  * Kitkat specific PrintManager API implementation.
  */
+@RequiresApi(19)
+@TargetApi(19)
 class PrintHelperKitkat {
     private static final String LOG_TAG = "PrintHelperKitkat";
     // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
diff --git a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
index 3905e39..f164f17 100644
--- a/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
+++ b/core-utils/kitkat/android/support/v4/provider/DocumentsContractApi19.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.provider;
 
+import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -23,16 +24,30 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.DocumentsContract;
+import android.support.annotation.RequiresApi;
 import android.text.TextUtils;
 import android.util.Log;
 
+@RequiresApi(19)
+@TargetApi(19)
 class DocumentsContractApi19 {
     private static final String TAG = "DocumentFile";
 
+    // DocumentsContract API level 24.
+    private static final int FLAG_VIRTUAL_DOCUMENT = 1 << 9;
+
     public static boolean isDocumentUri(Context context, Uri self) {
         return DocumentsContract.isDocumentUri(context, self);
     }
 
+    public static boolean isVirtual(Context context, Uri self) {
+        if (!isDocumentUri(context, self)) {
+            return false;
+        }
+
+        return (getFlags(context, self) & FLAG_VIRTUAL_DOCUMENT) != 0;
+    }
+
     public static String getName(Context context, Uri self) {
         return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null);
     }
@@ -50,6 +65,10 @@
         }
     }
 
+    public static long getFlags(Context context, Uri self) {
+        return queryForLong(context, self, DocumentsContract.Document.COLUMN_FLAGS, 0);
+    }
+
     public static boolean isDirectory(Context context, Uri self) {
         return DocumentsContract.Document.MIME_TYPE_DIR.equals(getRawType(context, self));
     }
diff --git a/core-utils/tests/java/android/support/v4/content/FileProviderTest.java b/core-utils/tests/java/android/support/v4/content/FileProviderTest.java
index ec472b7..32357cb 100644
--- a/core-utils/tests/java/android/support/v4/content/FileProviderTest.java
+++ b/core-utils/tests/java/android/support/v4/content/FileProviderTest.java
@@ -19,16 +19,25 @@
 import static android.provider.OpenableColumns.DISPLAY_NAME;
 import static android.provider.OpenableColumns.SIZE;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Environment;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.content.FileProvider.SimplePathStrategy;
-import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -39,7 +48,9 @@
 /**
  * Tests for {@link FileProvider}
  */
-public class FileProviderTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FileProviderTest {
     private static final String TEST_AUTHORITY = "moocow";
 
     private static final String TEST_FILE = "file.test";
@@ -47,14 +58,15 @@
     private static final byte[] TEST_DATA_ALT = new byte[] { (byte) 0x33, 0x66 };
 
     private ContentResolver mResolver;
+    private Context mContext;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mResolver = getContext().getContentResolver();
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mResolver = mContext.getContentResolver();
     }
 
+    @Test
     public void testStrategyUriSimple() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -75,6 +87,7 @@
         }
     }
 
+    @Test
     public void testStrategyUriJumpOutside() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -87,6 +100,7 @@
         }
     }
 
+    @Test
     public void testStrategyUriShortestRoot() throws Exception {
         SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag1", mContext.getFilesDir());
@@ -105,6 +119,7 @@
                 strat.getUriForFile(file).toString());
     }
 
+    @Test
     public void testStrategyFileSimple() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -119,6 +134,7 @@
                 Uri.parse("content://authority/tag/subdir/file.test")).getPath());
     }
 
+    @Test
     public void testStrategyFileJumpOutside() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -130,6 +146,7 @@
         }
     }
 
+    @Test
     public void testStrategyEscaping() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("t/g", mContext.getFilesDir());
@@ -144,6 +161,7 @@
                 strat.getFileForUri(Uri.parse(expected)).getPath());
     }
 
+    @Test
     public void testStrategyExtraParams() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -154,6 +172,7 @@
                 Uri.parse("content://authority/tag/file.txt?extra=foo")).getPath());
     }
 
+    @Test
     public void testStrategyExtraSeparators() throws Exception {
         final SimplePathStrategy strat = new SimplePathStrategy("authority");
         strat.addRoot("tag", mContext.getFilesDir());
@@ -170,6 +189,7 @@
                 strat.getFileForUri(Uri.parse(expected)).getPath());
     }
 
+    @Test
     public void testQueryProjectionNull() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -186,6 +206,7 @@
         }
     }
 
+    @Test
     public void testQueryProjectionOrder() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -214,6 +235,7 @@
         }
     }
 
+    @Test
     public void testQueryExtraColumn() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -231,6 +253,7 @@
         }
     }
 
+    @Test
     public void testReadFile() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -238,6 +261,7 @@
         assertContentsEquals(TEST_DATA, uri);
     }
 
+    @Test
     public void testWriteFile() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -254,6 +278,7 @@
         assertContentsEquals(TEST_DATA_ALT, uri);
     }
 
+    @Test
     public void testWriteMissingFile() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, null);
@@ -274,6 +299,7 @@
         assertContentsEquals(TEST_DATA_ALT, uri);
     }
 
+    @Test
     public void testDelete() throws Exception {
         final File file = new File(mContext.getFilesDir(), TEST_FILE);
         final Uri uri = stageFileAndGetUri(file, TEST_DATA);
@@ -290,6 +316,7 @@
         }
     }
 
+    @Test
     public void testMetaDataTargets() {
         Uri actual;
 
diff --git a/core-utils/tests/java/android/support/v4/graphics/ColorUtilsTest.java b/core-utils/tests/java/android/support/v4/graphics/ColorUtilsTest.java
index 985f874..5a78c92 100644
--- a/core-utils/tests/java/android/support/v4/graphics/ColorUtilsTest.java
+++ b/core-utils/tests/java/android/support/v4/graphics/ColorUtilsTest.java
@@ -16,18 +16,18 @@
 
 package android.support.v4.graphics;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.graphics.Color;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.lang.Integer;
 import java.util.ArrayList;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ColorUtilsTest {
diff --git a/core-utils/tests/java/android/support/v4/provider/DocumentFileTest.java b/core-utils/tests/java/android/support/v4/provider/DocumentFileTest.java
index 9dd5a51..bbbcbf5 100644
--- a/core-utils/tests/java/android/support/v4/provider/DocumentFileTest.java
+++ b/core-utils/tests/java/android/support/v4/provider/DocumentFileTest.java
@@ -21,8 +21,8 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.Suppress;
 
 import java.io.DataInputStream;
@@ -122,6 +122,7 @@
                 resetRoot();
                 assertTrue("isDirectory", doc.isDirectory());
                 assertFalse("isFile", doc.isFile());
+                assertFalse("isVirtual", doc.isVirtual());
                 assertTrue("canRead", doc.canRead());
                 assertTrue("canWrite", doc.canWrite());
                 assertTrue("exists", doc.exists());
diff --git a/core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java b/core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java
index e6e35fc..dab413a 100644
--- a/core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java
+++ b/core-utils/tests/java/android/support/v4/text/BidiFormatterTest.java
@@ -16,19 +16,20 @@
 
 package android.support.v4.text;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.style.RelativeSizeSpan;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Locale;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class BidiFormatterTest {
diff --git a/customtabs/Android.mk b/customtabs/Android.mk
index 50e6dbc..cfd9971 100644
--- a/customtabs/Android.mk
+++ b/customtabs/Android.mk
@@ -15,16 +15,25 @@
 LOCAL_PATH := $(call my-dir)
 
 # Here is the final static library that apps can link against.
-# The R class is automatically excluded from the generated library.
-# Applications that use this library must specify LOCAL_RESOURCE_DIR
-# in their makefiles to include the resources in their package.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-customtabs \
+#       android-support-compat
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-customtabs
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_AIDL_INCLUDES := $LOCAL_PATH/src
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    $(call all-Iaidl-files-under, src)
-LOCAL_JAVA_LIBRARIES := android-support-annotations \
-android-support-compat
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,src) \
+    $(call all-Iaidl-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
+    android-support-compat
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java b/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
index 2431245..0fc69f9 100644
--- a/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
+++ b/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
@@ -16,18 +16,23 @@
 
 package android.support.customtabs;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Intent;
 import android.graphics.Color;
 import android.os.Build;
 import android.support.annotation.ColorRes;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.*;
-
 /**
  * Tests for CustomTabsIntent.
  */
diff --git a/design/Android.mk b/design/Android.mk
index 38ca592..b3bc846 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -14,118 +14,12 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Android libraries referenced by this module's resources.
-resource_libs := \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview
-
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.  The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-design-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := $(resource_libs)
-LOCAL_AAPT_FLAGS := --no-version-vectors
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between src and the platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-base
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Gingerbread APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-base
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Honeycomb APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-gingerbread
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Honeycomb MR1 APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-honeycomb-mr1
-LOCAL_SDK_VERSION := 12
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of ICS APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-honeycomb-mr1
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview \
-    android-support-transition
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Lollipop APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-design-lollipop
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, lollipop)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-ics
-LOCAL_JAVA_LIBRARIES := \
-    android-support-design-res \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview \
-    android-support-transition
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
 #   LOCAL_STATIC_ANDROID_LIBRARIES := \
 #       android-support-design \
+#       android-support-transition \
 #       android-support-v7-appcompat \
 #       android-support-v7-recyclerview \
 #       android-support-v4
@@ -135,11 +29,23 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-design
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-design-lollipop
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-design-res
-LOCAL_SHARED_ANDROID_LIBRARIES := $(resource_libs) android-support-v4
-LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,base) \
+    $(call all-java-files-under,gingerbread) \
+    $(call all-java-files-under,honeycomb) \
+    $(call all-java-files-under,honeycomb-mr1) \
+    $(call all-java-files-under,ics) \
+    $(call all-java-files-under,lollipop) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-transition \
+    android-support-v7-appcompat \
+    android-support-v7-recyclerview \
+    android-support-v4 \
+    android-support-annotations
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_AAPT_FLAGS := \
+    --no-version-vectors \
+    --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/build.gradle b/design/build.gradle
index 8ae45d7..1f35a60 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -18,6 +18,9 @@
     androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
     androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
     testCompile 'junit:junit:4.12'
+    testCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+        exclude module: 'support-annotations'
+    }
 }
 
 android {
@@ -32,9 +35,19 @@
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['base', 'gingerbread', 'honeycomb', 'honeycomb-mr1', 'ics', 'lollipop', 'src']
-        main.res.srcDirs 'res', 'res-public'
-        main.assets.srcDir 'assets'
+        main.java.srcDirs = [
+                'base',
+                'gingerbread',
+                'honeycomb',
+                'honeycomb-mr1',
+                'ics',
+                'lollipop',
+                'src'
+        ]
+        main.res.srcDirs = [
+                'res',
+                'res-public'
+        ]
         main.resources.srcDir 'src'
 
         androidTest.setRoot('tests')
diff --git a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
index 1cb1cfe..e1f287b 100644
--- a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
+++ b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
@@ -19,8 +19,12 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.animation.Interpolator;
 
+@RequiresApi(12)
+@TargetApi(12)
 class ValueAnimatorCompatImplHoneycombMr1 extends ValueAnimatorCompat.Impl {
 
     private final ValueAnimator mValueAnimator;
diff --git a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
index 96b15a8..49a07cd 100644
--- a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
+++ b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
@@ -16,13 +16,17 @@
 
 package android.support.design.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
+@RequiresApi(11)
+@TargetApi(11)
 class ViewGroupUtilsHoneycomb {
     private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
     private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
diff --git a/design/ics/android/support/design/internal/TextScale.java b/design/ics/android/support/design/internal/TextScale.java
index 219353e..c017223 100644
--- a/design/ics/android/support/design/internal/TextScale.java
+++ b/design/ics/android/support/design/internal/TextScale.java
@@ -18,6 +18,8 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.transition.Transition;
 import android.support.transition.TransitionValues;
 import android.view.ViewGroup;
@@ -28,6 +30,8 @@
 /**
  * @hide
  */
+@RequiresApi(14)
+@TargetApi(14)
 public class TextScale extends Transition {
     private static final String PROPNAME_SCALE = "android:textscale:scale";
 
diff --git a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
index 25f1f70..4825124 100644
--- a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
+++ b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
@@ -18,11 +18,15 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.annotation.TargetApi;
 import android.os.Build;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
 
+@RequiresApi(14)
+@TargetApi(14)
 class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
 
     private float mRotation;
diff --git a/design/jvm-tests/src/android/support/design/widget/DirectedAcyclicGraphTest.java b/design/jvm-tests/src/android/support/design/widget/DirectedAcyclicGraphTest.java
index 58501be..4a5ffc5 100644
--- a/design/jvm-tests/src/android/support/design/widget/DirectedAcyclicGraphTest.java
+++ b/design/jvm-tests/src/android/support/design/widget/DirectedAcyclicGraphTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.support.annotation.NonNull;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
index 8b90361..ed03d35 100644
--- a/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
+++ b/design/lollipop/android/support/design/widget/CircularBorderDrawableLollipop.java
@@ -16,11 +16,15 @@
 
 package android.support.design.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
 
 /**
  * Lollipop version of {@link CircularBorderDrawable}.
  */
+@RequiresApi(21)
+@TargetApi(21)
 class CircularBorderDrawableLollipop extends CircularBorderDrawable {
 
     @Override
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index d5fe912..1d62c67 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -16,7 +16,6 @@
 
 package android.support.design.widget;
 
-import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.StateListAnimator;
@@ -28,13 +27,12 @@
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.RippleDrawable;
-import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@RequiresApi(21)
+@TargetApi(21)
 class FloatingActionButtonLollipop extends FloatingActionButtonIcs {
 
     private InsetDrawable mInsetDrawable;
diff --git a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
index 047a828..8dfa926 100644
--- a/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
+++ b/design/lollipop/android/support/design/widget/ViewUtilsLollipop.java
@@ -19,13 +19,17 @@
 import android.animation.AnimatorInflater;
 import android.animation.ObjectAnimator;
 import android.animation.StateListAnimator;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.support.annotation.RequiresApi;
 import android.support.design.R;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ViewUtilsLollipop {
 
     private static final int[] STATE_LIST_ANIM_ATTRS = new int[] {android.R.attr.stateListAnimator};
diff --git a/design/res/anim-v21/design_appbar_state_list_animator.xml b/design/res/animator-v21/design_appbar_state_list_animator.xml
similarity index 100%
rename from design/res/anim-v21/design_appbar_state_list_animator.xml
rename to design/res/animator-v21/design_appbar_state_list_animator.xml
diff --git a/design/res/layout/design_layout_snackbar_include.xml b/design/res/layout/design_layout_snackbar_include.xml
index 14620d3..fe11d8e 100644
--- a/design/res/layout/design_layout_snackbar_include.xml
+++ b/design/res/layout/design_layout_snackbar_include.xml
@@ -15,33 +15,39 @@
   ~ limitations under the License.
 -->
 
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<view
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    class="android.support.design.internal.SnackbarContentLayout"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom">
 
     <TextView
-            android:id="@+id/snackbar_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingTop="@dimen/design_snackbar_padding_vertical"
-            android:paddingBottom="@dimen/design_snackbar_padding_vertical"
-            android:paddingLeft="@dimen/design_snackbar_padding_horizontal"
-            android:paddingRight="@dimen/design_snackbar_padding_horizontal"
-            android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
-            android:maxLines="@integer/design_snackbar_text_max_lines"
-            android:layout_gravity="center_vertical|left|start"
-            android:ellipsize="end"
-            android:textAlignment="viewStart"/>
+        android:id="@+id/snackbar_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="@dimen/design_snackbar_padding_vertical"
+        android:paddingBottom="@dimen/design_snackbar_padding_vertical"
+        android:paddingLeft="@dimen/design_snackbar_padding_horizontal"
+        android:paddingRight="@dimen/design_snackbar_padding_horizontal"
+        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+        android:maxLines="@integer/design_snackbar_text_max_lines"
+        android:layout_gravity="center_vertical|left|start"
+        android:ellipsize="end"
+        android:textAlignment="viewStart"/>
 
     <Button
-            android:id="@+id/snackbar_action"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/design_snackbar_extra_spacing_horizontal"
-            android:layout_marginStart="@dimen/design_snackbar_extra_spacing_horizontal"
-            android:layout_gravity="center_vertical|right|end"
-            android:minWidth="48dp"
-            android:visibility="gone"
-            android:textColor="?attr/colorAccent"
-            style="?attr/borderlessButtonStyle"/>
+        android:id="@+id/snackbar_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/design_snackbar_extra_spacing_horizontal"
+        android:layout_marginStart="@dimen/design_snackbar_extra_spacing_horizontal"
+        android:layout_gravity="center_vertical|right|end"
+        android:minWidth="48dp"
+        android:visibility="gone"
+        android:textColor="?attr/colorAccent"
+        style="?attr/borderlessButtonStyle"/>
 
-</merge>
\ No newline at end of file
+</view>
\ No newline at end of file
diff --git a/design/res/values-v21/styles.xml b/design/res/values-v21/styles.xml
index 76bde78..9886c15 100644
--- a/design/res/values-v21/styles.xml
+++ b/design/res/values-v21/styles.xml
@@ -17,7 +17,7 @@
 <resources>
 
     <style name="Widget.Design.AppBarLayout" parent="Base.Widget.Design.AppBarLayout">
-        <item name="android:stateListAnimator">@anim/design_appbar_state_list_animator</item>
+        <item name="android:stateListAnimator">@animator/design_appbar_state_list_animator</item>
     </style>
 
 </resources>
diff --git a/design/src/android/support/design/internal/BottomNavigationItemView.java b/design/src/android/support/design/internal/BottomNavigationItemView.java
index ea22399..05a9b89 100644
--- a/design/src/android/support/design/internal/BottomNavigationItemView.java
+++ b/design/src/android/support/design/internal/BottomNavigationItemView.java
@@ -25,6 +25,7 @@
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.PointerIconCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.view.menu.MenuItemImpl;
 import android.support.v7.view.menu.MenuView;
@@ -87,7 +88,6 @@
         mIcon = (ImageView) findViewById(R.id.icon);
         mSmallLabel = (TextView) findViewById(R.id.smallLabel);
         mLargeLabel = (TextView) findViewById(R.id.largeLabel);
-
     }
 
     @Override
@@ -193,6 +193,14 @@
         mSmallLabel.setEnabled(enabled);
         mLargeLabel.setEnabled(enabled);
         mIcon.setEnabled(enabled);
+
+        if (enabled) {
+            ViewCompat.setPointerIcon(this,
+                    PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));
+        } else {
+            ViewCompat.setPointerIcon(this, null);
+        }
+
     }
 
     @Override
diff --git a/design/src/android/support/design/internal/ForegroundLinearLayout.java b/design/src/android/support/design/internal/ForegroundLinearLayout.java
index 48a04a6..9e690d2 100644
--- a/design/src/android/support/design/internal/ForegroundLinearLayout.java
+++ b/design/src/android/support/design/internal/ForegroundLinearLayout.java
@@ -16,12 +16,14 @@
 
 package android.support.design.internal;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.design.R;
 import android.support.v7.widget.LinearLayoutCompat;
@@ -118,6 +120,8 @@
         return super.verifyDrawable(who) || (who == mForeground);
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
@@ -222,6 +226,8 @@
         }
     }
 
+    @RequiresApi(21)
+    @TargetApi(21)
     @Override
     public void drawableHotspotChanged(float x, float y) {
         super.drawableHotspotChanged(x, y);
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index d1f0c22..9f9aaca 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -16,6 +16,8 @@
 
 package android.support.design.internal;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -47,8 +49,6 @@
 
 import java.util.ArrayList;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * @hide
  */
@@ -57,6 +57,7 @@
 
     private static final String STATE_HIERARCHY = "android:menu:list";
     private static final String STATE_ADAPTER = "android:menu:adapter";
+    private static final String STATE_HEADER = "android:menu:header";
 
     private NavigationMenuView mMenuView;
     LinearLayout mHeaderLayout;
@@ -173,6 +174,11 @@
             if (mAdapter != null) {
                 state.putBundle(STATE_ADAPTER, mAdapter.createInstanceState());
             }
+            if (mHeaderLayout != null) {
+                SparseArray<Parcelable> header = new SparseArray<>();
+                mHeaderLayout.saveHierarchyState(header);
+                state.putSparseParcelableArray(STATE_HEADER, header);
+            }
             return state;
         }
         return null;
@@ -190,6 +196,10 @@
             if (adapterState != null) {
                 mAdapter.restoreInstanceState(adapterState);
             }
+            SparseArray<Parcelable> header = state.getSparseParcelableArray(STATE_HEADER);
+            if (header != null) {
+                mHeaderLayout.restoreHierarchyState(header);
+            }
         }
     }
 
diff --git a/design/src/android/support/design/internal/SnackbarContentLayout.java b/design/src/android/support/design/internal/SnackbarContentLayout.java
new file mode 100644
index 0000000..43a2763
--- /dev/null
+++ b/design/src/android/support/design/internal/SnackbarContentLayout.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.design.internal;
+
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.RestrictTo;
+import android.support.design.R;
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+@RestrictTo(GROUP_ID)
+public class SnackbarContentLayout extends LinearLayout implements
+        BaseTransientBottomBar.ContentViewCallback {
+    private TextView mMessageView;
+    private Button mActionView;
+
+    private int mMaxWidth;
+    private int mMaxInlineActionWidth;
+
+    public SnackbarContentLayout(Context context) {
+        this(context, null);
+    }
+
+    public SnackbarContentLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
+        mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
+        mMaxInlineActionWidth = a.getDimensionPixelSize(
+                R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
+        a.recycle();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mMessageView = (TextView) findViewById(R.id.snackbar_text);
+        mActionView = (Button) findViewById(R.id.snackbar_action);
+    }
+
+    public TextView getMessageView() {
+        return mMessageView;
+    }
+
+    public Button getActionView() {
+        return mActionView;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
+            widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        final int multiLineVPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_snackbar_padding_vertical_2lines);
+        final int singleLineVPadding = getResources().getDimensionPixelSize(
+                R.dimen.design_snackbar_padding_vertical);
+        final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
+
+        boolean remeasure = false;
+        if (isMultiLine && mMaxInlineActionWidth > 0
+                && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
+            if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
+                    multiLineVPadding - singleLineVPadding)) {
+                remeasure = true;
+            }
+        } else {
+            final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
+            if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
+                remeasure = true;
+            }
+        }
+
+        if (remeasure) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    private boolean updateViewsWithinLayout(final int orientation,
+            final int messagePadTop, final int messagePadBottom) {
+        boolean changed = false;
+        if (orientation != getOrientation()) {
+            setOrientation(orientation);
+            changed = true;
+        }
+        if (mMessageView.getPaddingTop() != messagePadTop
+                || mMessageView.getPaddingBottom() != messagePadBottom) {
+            updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
+            changed = true;
+        }
+        return changed;
+    }
+
+    private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
+        if (ViewCompat.isPaddingRelative(view)) {
+            ViewCompat.setPaddingRelative(view,
+                    ViewCompat.getPaddingStart(view), topPadding,
+                    ViewCompat.getPaddingEnd(view), bottomPadding);
+        } else {
+            view.setPadding(view.getPaddingLeft(), topPadding,
+                    view.getPaddingRight(), bottomPadding);
+        }
+    }
+
+    @Override
+    public void animateContentIn(int delay, int duration) {
+        ViewCompat.setAlpha(mMessageView, 0f);
+        ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
+                .setStartDelay(delay).start();
+
+        if (mActionView.getVisibility() == VISIBLE) {
+            ViewCompat.setAlpha(mActionView, 0f);
+            ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
+                    .setStartDelay(delay).start();
+        }
+    }
+
+    @Override
+    public void animateContentOut(int delay, int duration) {
+        ViewCompat.setAlpha(mMessageView, 1f);
+        ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
+                .setStartDelay(delay).start();
+
+        if (mActionView.getVisibility() == VISIBLE) {
+            ViewCompat.setAlpha(mActionView, 1f);
+            ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
+                    .setStartDelay(delay).start();
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index aa76a6b..4a1993d 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -16,6 +16,7 @@
 
 package android.support.design.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -25,6 +26,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.support.design.R;
@@ -319,7 +321,7 @@
 
     @Override
     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (p instanceof LinearLayout.LayoutParams) {
+        if (Build.VERSION.SDK_INT >= 19 && p instanceof LinearLayout.LayoutParams) {
             return new LayoutParams((LinearLayout.LayoutParams) p);
         } else if (p instanceof MarginLayoutParams) {
             return new LayoutParams((MarginLayoutParams) p);
@@ -681,11 +683,17 @@
             super(source);
         }
 
+        @RequiresApi(19)
+        @TargetApi(19)
         public LayoutParams(LinearLayout.LayoutParams source) {
+            // The copy constructor called here only exists on API 19+.
             super(source);
         }
 
+        @RequiresApi(19)
+        @TargetApi(19)
         public LayoutParams(LayoutParams source) {
+            // The copy constructor called here only exists on API 19+.
             super(source);
             mScrollFlags = source.mScrollFlags;
             mScrollInterpolator = source.mScrollInterpolator;
diff --git a/design/src/android/support/design/widget/BaseTransientBottomBar.java b/design/src/android/support/design/widget/BaseTransientBottomBar.java
new file mode 100644
index 0000000..d47e08b
--- /dev/null
+++ b/design/src/android/support/design/widget/BaseTransientBottomBar.java
@@ -0,0 +1,738 @@
+/*
+ * 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.design.widget;
+
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.design.R;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
+import android.support.v4.view.WindowInsetsCompat;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class for lightweight transient bars that are displayed along the bottom edge of the
+ * application window.
+ *
+ * @param <B> The transient bottom bar subclass.
+ */
+public abstract class BaseTransientBottomBar<B extends BaseTransientBottomBar<B>> {
+    /**
+     * Base class for {@link BaseTransientBottomBar} callbacks.
+     *
+     * @param <B> The transient bottom bar subclass.
+     * @see BaseTransientBottomBar#setCallback(BaseCallback)
+     */
+    public abstract static class BaseCallback<B> {
+        /** Indicates that the Snackbar was dismissed via a swipe.*/
+        public static final int DISMISS_EVENT_SWIPE = 0;
+        /** Indicates that the Snackbar was dismissed via an action click.*/
+        public static final int DISMISS_EVENT_ACTION = 1;
+        /** Indicates that the Snackbar was dismissed via a timeout.*/
+        public static final int DISMISS_EVENT_TIMEOUT = 2;
+        /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
+        public static final int DISMISS_EVENT_MANUAL = 3;
+        /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
+        public static final int DISMISS_EVENT_CONSECUTIVE = 4;
+
+        /** @hide */
+        @RestrictTo(GROUP_ID)
+        @IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
+                DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface DismissEvent {}
+
+        /**
+         * Called when the given {@link BaseTransientBottomBar} has been dismissed, either
+         * through a time-out, having been manually dismissed, or an action being clicked.
+         *
+         * @param transientBottomBar The transient bottom bar which has been dismissed.
+         * @param event The event which caused the dismissal. One of either:
+         *              {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
+         *              {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
+         *              {@link #DISMISS_EVENT_CONSECUTIVE}.
+         *
+         * @see BaseTransientBottomBar#dismiss()
+         */
+        public void onDismissed(B transientBottomBar, @DismissEvent int event) {
+            // empty
+        }
+
+        /**
+         * Called when the given {@link BaseTransientBottomBar} is visible.
+         *
+         * @param transientBottomBar The transient bottom bar which is now visible.
+         * @see BaseTransientBottomBar#show()
+         */
+        public void onShown(B transientBottomBar) {
+            // empty
+        }
+    }
+
+    /**
+     * Interface that defines the behavior of the main content of a transient bottom bar.
+     */
+    public interface ContentViewCallback {
+        /**
+         * Animates the content of the transient bottom bar in.
+         *
+         * @param delay Animation delay.
+         * @param duration Animation duration.
+         */
+        void animateContentIn(int delay, int duration);
+
+        /**
+         * Animates the content of the transient bottom bar out.
+         *
+         * @param delay Animation delay.
+         * @param duration Animation duration.
+         */
+        void animateContentOut(int delay, int duration);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(GROUP_ID)
+    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
+    @IntRange(from = 1)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Duration {}
+
+    /**
+     * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
+     * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
+     *
+     * @see #setDuration
+     */
+    public static final int LENGTH_INDEFINITE = -2;
+
+    /**
+     * Show the Snackbar for a short period of time.
+     *
+     * @see #setDuration
+     */
+    public static final int LENGTH_SHORT = -1;
+
+    /**
+     * Show the Snackbar for a long period of time.
+     *
+     * @see #setDuration
+     */
+    public static final int LENGTH_LONG = 0;
+
+    static final int ANIMATION_DURATION = 250;
+    static final int ANIMATION_FADE_DURATION = 180;
+
+    static final Handler sHandler;
+    static final int MSG_SHOW = 0;
+    static final int MSG_DISMISS = 1;
+
+    static {
+        sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
+            @Override
+            public boolean handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_SHOW:
+                        ((BaseTransientBottomBar) message.obj).showView();
+                        return true;
+                    case MSG_DISMISS:
+                        ((BaseTransientBottomBar) message.obj).hideView(message.arg1);
+                        return true;
+                }
+                return false;
+            }
+        });
+    }
+
+    private final ViewGroup mTargetParent;
+    private final Context mContext;
+    final SnackbarBaseLayout mView;
+    private final ContentViewCallback mContentViewCallback;
+    private int mDuration;
+
+    @Nullable private BaseCallback mCallback;
+    private List<BaseCallback> mCallbacks;
+
+    private final AccessibilityManager mAccessibilityManager;
+
+    /**
+     * @hide
+     */
+    @RestrictTo(GROUP_ID)
+    interface OnLayoutChangeListener {
+        void onLayoutChange(View view, int left, int top, int right, int bottom);
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(GROUP_ID)
+    interface OnAttachStateChangeListener {
+        void onViewAttachedToWindow(View v);
+        void onViewDetachedFromWindow(View v);
+    }
+
+    /**
+     * Constructor for the transient bottom bar.
+     *
+     * @param parent The parent for this transient bottom bar.
+     * @param content The content view for this transient bottom bar.
+     * @param contentViewCallback The content view callback for this transient bottom bar.
+     */
+    protected BaseTransientBottomBar(@NonNull ViewGroup parent, @NonNull View content,
+            @NonNull ContentViewCallback contentViewCallback) {
+        if (parent == null) {
+            throw new IllegalArgumentException("Transient bottom bar must have non-null parent");
+        }
+        if (content == null) {
+            throw new IllegalArgumentException("Transient bottom bar must have non-null content");
+        }
+        if (contentViewCallback == null) {
+            throw new IllegalArgumentException("Transient bottom bar must have non-null callback");
+        }
+
+        mTargetParent = parent;
+        mContentViewCallback = contentViewCallback;
+        mContext = parent.getContext();
+
+        ThemeUtils.checkAppCompatTheme(mContext);
+
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        // Note that for backwards compatibility reasons we inflate a layout that is defined
+        // in the extending Snackbar class. This is to prevent breakage of apps that have custom
+        // coordinator layout behaviors that depend on that layout.
+        mView = (SnackbarBaseLayout) inflater.inflate(
+                R.layout.design_layout_snackbar, mTargetParent, false);
+        mView.addView(content);
+
+        ViewCompat.setAccessibilityLiveRegion(mView,
+                ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
+        ViewCompat.setImportantForAccessibility(mView,
+                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+        // Make sure that we fit system windows and have a listener to apply any insets
+        ViewCompat.setFitsSystemWindows(mView, true);
+        ViewCompat.setOnApplyWindowInsetsListener(mView,
+                new android.support.v4.view.OnApplyWindowInsetsListener() {
+                    @Override
+                    public WindowInsetsCompat onApplyWindowInsets(View v,
+                            WindowInsetsCompat insets) {
+                        // Copy over the bottom inset as padding so that we're displayed
+                        // above the navigation bar
+                        v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
+                                v.getPaddingRight(), insets.getSystemWindowInsetBottom());
+                        return insets;
+                    }
+                });
+
+        mAccessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    /**
+     * Set how long to show the view for.
+     *
+     * @param duration either be one of the predefined lengths:
+     *                 {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
+     *                 in milliseconds.
+     */
+    @NonNull
+    public B setDuration(@Duration int duration) {
+        mDuration = duration;
+        return (B) this;
+    }
+
+    /**
+     * Return the duration.
+     *
+     * @see #setDuration
+     */
+    @Duration
+    public int getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Returns the {@link BaseTransientBottomBar}'s context.
+     */
+    @NonNull
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the {@link BaseTransientBottomBar}'s view.
+     */
+    @NonNull
+    public View getView() {
+        return mView;
+    }
+
+    /**
+     * Show the {@link BaseTransientBottomBar}.
+     */
+    public void show() {
+        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
+    }
+
+    /**
+     * Dismiss the {@link BaseTransientBottomBar}.
+     */
+    public void dismiss() {
+        dispatchDismiss(BaseCallback.DISMISS_EVENT_MANUAL);
+    }
+
+    void dispatchDismiss(@BaseCallback.DismissEvent int event) {
+        SnackbarManager.getInstance().dismiss(mManagerCallback, event);
+    }
+
+    /**
+     * Set a callback to be called when this the visibility of this {@link BaseTransientBottomBar}
+     * changes. Note that this method is deprecated
+     * and you should use {@link #addCallback(BaseCallback)} to add a callback and
+     * {@link #removeCallback(BaseCallback)} to remove a registered callback.
+     *
+     * @param callback Callback to notify when transient bottom bar events occur.
+     * @deprecated Use {@link #addCallback(BaseCallback)}
+     * @see BaseCallback
+     * @see #addCallback(BaseCallback)
+     * @see #removeCallback(BaseCallback)
+     */
+    @Deprecated
+    @NonNull
+    public B setCallback(BaseCallback callback) {
+        // The logic in this method emulates what we had before support for multiple
+        // registered callbacks.
+        if (mCallback != null) {
+            removeCallback(mCallback);
+        }
+        if (callback != null) {
+            addCallback(callback);
+        }
+        // Update the deprecated field so that we can remove the passed callback the next
+        // time we're called
+        mCallback = callback;
+        return (B) this;
+    }
+
+    /**
+     * Adds the specified callback to the list of callbacks that will be notified of transient
+     * bottom bar events.
+     *
+     * @param callback Callback to notify when transient bottom bar events occur.
+     * @see #removeCallback(BaseCallback)
+     */
+    @NonNull
+    public B addCallback(@NonNull BaseCallback callback) {
+        if (callback == null) {
+            return (B) this;
+        }
+        if (mCallbacks == null) {
+            mCallbacks = new ArrayList<BaseCallback>();
+        }
+        mCallbacks.add(callback);
+        return (B) this;
+    }
+
+    /**
+     * Removes the specified callback from the list of callbacks that will be notified of transient
+     * bottom bar events.
+     *
+     * @param callback Callback to remove from being notified of transient bottom bar events
+     * @see #addCallback(BaseCallback)
+     */
+    @NonNull
+    public B removeCallback(@NonNull BaseCallback callback) {
+        if (callback == null) {
+            return (B) this;
+        }
+        if (mCallbacks == null) {
+            // This can happen if this method is called before the first call to addCallback
+            return (B) this;
+        }
+        mCallbacks.remove(callback);
+        return (B) this;
+    }
+
+    /**
+     * Return whether this {@link BaseTransientBottomBar} is currently being shown.
+     */
+    public boolean isShown() {
+        return SnackbarManager.getInstance().isCurrent(mManagerCallback);
+    }
+
+    /**
+     * Returns whether this {@link BaseTransientBottomBar} is currently being shown, or is queued
+     * to be shown next.
+     */
+    public boolean isShownOrQueued() {
+        return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
+    }
+
+    final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
+        @Override
+        public void show() {
+            sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
+        }
+
+        @Override
+        public void dismiss(int event) {
+            sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
+                    BaseTransientBottomBar.this));
+        }
+    };
+
+    final void showView() {
+        if (mView.getParent() == null) {
+            final ViewGroup.LayoutParams lp = mView.getLayoutParams();
+
+            if (lp instanceof CoordinatorLayout.LayoutParams) {
+                // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
+                final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp;
+
+                final Behavior behavior = new Behavior();
+                behavior.setStartAlphaSwipeDistance(0.1f);
+                behavior.setEndAlphaSwipeDistance(0.6f);
+                behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
+                behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
+                    @Override
+                    public void onDismiss(View view) {
+                        view.setVisibility(View.GONE);
+                        dispatchDismiss(BaseCallback.DISMISS_EVENT_SWIPE);
+                    }
+
+                    @Override
+                    public void onDragStateChanged(int state) {
+                        switch (state) {
+                            case SwipeDismissBehavior.STATE_DRAGGING:
+                            case SwipeDismissBehavior.STATE_SETTLING:
+                                // If the view is being dragged or settling, cancel the timeout
+                                SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
+                                break;
+                            case SwipeDismissBehavior.STATE_IDLE:
+                                // If the view has been released and is idle, restore the timeout
+                                SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
+                                break;
+                        }
+                    }
+                });
+                clp.setBehavior(behavior);
+                // Also set the inset edge so that views can dodge the bar correctly
+                clp.insetEdge = Gravity.BOTTOM;
+            }
+
+            mTargetParent.addView(mView);
+        }
+
+        mView.setOnAttachStateChangeListener(
+                new BaseTransientBottomBar.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {}
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    if (isShownOrQueued()) {
+                        // If we haven't already been dismissed then this event is coming from a
+                        // non-user initiated action. Hence we need to make sure that we callback
+                        // and keep our state up to date. We need to post the call since
+                        // removeView() will call through to onDetachedFromWindow and thus overflow.
+                        sHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                onViewHidden(BaseCallback.DISMISS_EVENT_MANUAL);
+                            }
+                        });
+                    }
+                }
+            });
+
+        if (ViewCompat.isLaidOut(mView)) {
+            if (shouldAnimate()) {
+                // If animations are enabled, animate it in
+                animateViewIn();
+            } else {
+                // Else if anims are disabled just call back now
+                onViewShown();
+            }
+        } else {
+            // Otherwise, add one of our layout change listeners and show it in when laid out
+            mView.setOnLayoutChangeListener(new BaseTransientBottomBar.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View view, int left, int top, int right, int bottom) {
+                    mView.setOnLayoutChangeListener(null);
+
+                    if (shouldAnimate()) {
+                        // If animations are enabled, animate it in
+                        animateViewIn();
+                    } else {
+                        // Else if anims are disabled just call back now
+                        onViewShown();
+                    }
+                }
+            });
+        }
+    }
+
+    void animateViewIn() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            ViewCompat.setTranslationY(mView, mView.getHeight());
+            ViewCompat.animate(mView)
+                    .translationY(0f)
+                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
+                    .setDuration(ANIMATION_DURATION)
+                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(View view) {
+                            mContentViewCallback.animateContentIn(
+                                    ANIMATION_DURATION - ANIMATION_FADE_DURATION,
+                                    ANIMATION_FADE_DURATION);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(View view) {
+                            onViewShown();
+                        }
+                    }).start();
+        } else {
+            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
+                    R.anim.design_snackbar_in);
+            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            anim.setDuration(ANIMATION_DURATION);
+            anim.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    onViewShown();
+                }
+
+                @Override
+                public void onAnimationStart(Animation animation) {}
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {}
+            });
+            mView.startAnimation(anim);
+        }
+    }
+
+    private void animateViewOut(final int event) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            ViewCompat.animate(mView)
+                    .translationY(mView.getHeight())
+                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
+                    .setDuration(ANIMATION_DURATION)
+                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(View view) {
+                            mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
+                        }
+
+                        @Override
+                        public void onAnimationEnd(View view) {
+                            onViewHidden(event);
+                        }
+                    }).start();
+        } else {
+            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
+                    R.anim.design_snackbar_out);
+            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
+            anim.setDuration(ANIMATION_DURATION);
+            anim.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    onViewHidden(event);
+                }
+
+                @Override
+                public void onAnimationStart(Animation animation) {}
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {}
+            });
+            mView.startAnimation(anim);
+        }
+    }
+
+    final void hideView(@BaseCallback.DismissEvent final int event) {
+        if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
+            animateViewOut(event);
+        } else {
+            // If anims are disabled or the view isn't visible, just call back now
+            onViewHidden(event);
+        }
+    }
+
+    void onViewShown() {
+        SnackbarManager.getInstance().onShown(mManagerCallback);
+        if (mCallbacks != null) {
+            // Notify the callbacks. Do that from the end of the list so that if a callback
+            // removes itself as the result of being called, it won't mess up with our iteration
+            int callbackCount = mCallbacks.size();
+            for (int i = callbackCount - 1; i >= 0; i--) {
+                mCallbacks.get(i).onShown(this);
+            }
+        }
+    }
+
+    void onViewHidden(int event) {
+        // First tell the SnackbarManager that it has been dismissed
+        SnackbarManager.getInstance().onDismissed(mManagerCallback);
+        if (mCallbacks != null) {
+            // Notify the callbacks. Do that from the end of the list so that if a callback
+            // removes itself as the result of being called, it won't mess up with our iteration
+            int callbackCount = mCallbacks.size();
+            for (int i = callbackCount - 1; i >= 0; i--) {
+                mCallbacks.get(i).onDismissed(this, event);
+            }
+        }
+        if (Build.VERSION.SDK_INT < 11) {
+            // We need to hide the Snackbar on pre-v11 since it uses an old style Animation.
+            // ViewGroup has special handling in removeView() when getAnimation() != null in
+            // that it waits. This then means that the calculated insets are wrong and the
+            // any dodging views do not return. We workaround it by setting the view to gone while
+            // ViewGroup actually gets around to removing it.
+            mView.setVisibility(View.GONE);
+        }
+        // Lastly, hide and remove the view from the parent (if attached)
+        final ViewParent parent = mView.getParent();
+        if (parent instanceof ViewGroup) {
+            ((ViewGroup) parent).removeView(mView);
+        }
+    }
+
+    /**
+     * Returns true if we should animate the Snackbar view in/out.
+     */
+    boolean shouldAnimate() {
+        return !mAccessibilityManager.isEnabled();
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(GROUP_ID)
+    static class SnackbarBaseLayout extends FrameLayout {
+        private BaseTransientBottomBar.OnLayoutChangeListener mOnLayoutChangeListener;
+        private BaseTransientBottomBar.OnAttachStateChangeListener mOnAttachStateChangeListener;
+
+        SnackbarBaseLayout(Context context) {
+            this(context, null);
+        }
+
+        SnackbarBaseLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
+            if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
+                ViewCompat.setElevation(this, a.getDimensionPixelSize(
+                        R.styleable.SnackbarLayout_elevation, 0));
+            }
+            a.recycle();
+
+            setClickable(true);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            super.onLayout(changed, l, t, r, b);
+            if (mOnLayoutChangeListener != null) {
+                mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
+            }
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            if (mOnAttachStateChangeListener != null) {
+                mOnAttachStateChangeListener.onViewAttachedToWindow(this);
+            }
+
+            ViewCompat.requestApplyInsets(this);
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            if (mOnAttachStateChangeListener != null) {
+                mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
+            }
+        }
+
+        void setOnLayoutChangeListener(
+                BaseTransientBottomBar.OnLayoutChangeListener onLayoutChangeListener) {
+            mOnLayoutChangeListener = onLayoutChangeListener;
+        }
+
+        void setOnAttachStateChangeListener(
+                BaseTransientBottomBar.OnAttachStateChangeListener listener) {
+            mOnAttachStateChangeListener = listener;
+        }
+    }
+
+    final class Behavior extends SwipeDismissBehavior<SnackbarBaseLayout> {
+        @Override
+        public boolean canSwipeDismissView(View child) {
+            return child instanceof SnackbarBaseLayout;
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarBaseLayout child,
+                MotionEvent event) {
+            // We want to make sure that we disable any Snackbar timeouts if the user is
+            // currently touching the Snackbar. We restore the timeout when complete
+            if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
+                switch (event.getActionMasked()) {
+                    case MotionEvent.ACTION_DOWN:
+                        SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
+                        break;
+                    case MotionEvent.ACTION_UP:
+                    case MotionEvent.ACTION_CANCEL:
+                        SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
+                        break;
+                }
+            }
+
+            return super.onInterceptTouchEvent(parent, child, event);
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 39ad03b..40f2d5e 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -16,6 +16,7 @@
 
 package android.support.design.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
@@ -30,6 +31,7 @@
 import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StyleRes;
 import android.support.design.R;
@@ -1183,7 +1185,10 @@
             super(source);
         }
 
+        @RequiresApi(19)
+        @TargetApi(19)
         public LayoutParams(FrameLayout.LayoutParams source) {
+            // The copy constructor called here only exists on API 19+.
             super(source);
         }
 
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 9566934..d4e5a1a 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -16,6 +16,9 @@
 
 package android.support.design.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import static android.support.design.widget.ViewUtils.objectEquals;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -72,9 +75,6 @@
 import java.util.List;
 import java.util.Map;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-import static android.support.design.widget.ViewUtils.objectEquals;
-
 /**
  * CoordinatorLayout is a super-powered {@link android.widget.FrameLayout FrameLayout}.
  *
@@ -1219,6 +1219,10 @@
         for (int i = 0; i < childCount; i++) {
             final View child = mDependencySortedChildren.get(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (type == EVENT_PRE_DRAW && child.getVisibility() == View.GONE) {
+                // Do not try to update GONE child views in pre draw updates.
+                continue;
+            }
 
             // Check child views before for anchor
             for (int j = 0; j < i; j++) {
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index 782cc4f..f29305b 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -16,46 +16,25 @@
 
 package android.support.design.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.support.annotation.ColorInt;
-import android.support.annotation.IntDef;
-import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StringRes;
 import android.support.design.R;
-import android.support.v4.view.OnApplyWindowInsetsListener;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v4.view.WindowInsetsCompat;
+import android.support.design.internal.SnackbarContentLayout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.Button;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
-
 /**
  * Snackbars provide lightweight feedback about an operation. They show a brief message at the
  * bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
@@ -69,136 +48,22 @@
  * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
  * <p>
  * To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
- * via {@link #setCallback(Callback)}.</p>
+ * via {@link BaseTransientBottomBar#setCallback(BaseCallback)}.</p>
  */
-public final class Snackbar {
-
+public final class Snackbar extends BaseTransientBottomBar<Snackbar> {
     /**
      * Callback class for {@link Snackbar} instances.
      *
-     * @see Snackbar#setCallback(Callback)
+     * Note: this class is here to provide backwards-compatible way for apps written before
+     * the existence of the base {@link BaseTransientBottomBar} class.
+     *
+     * @see BaseTransientBottomBar#setCallback(BaseCallback)
      */
-    public static abstract class Callback {
-        /** Indicates that the Snackbar was dismissed via a swipe.*/
-        public static final int DISMISS_EVENT_SWIPE = 0;
-        /** Indicates that the Snackbar was dismissed via an action click.*/
-        public static final int DISMISS_EVENT_ACTION = 1;
-        /** Indicates that the Snackbar was dismissed via a timeout.*/
-        public static final int DISMISS_EVENT_TIMEOUT = 2;
-        /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
-        public static final int DISMISS_EVENT_MANUAL = 3;
-        /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
-        public static final int DISMISS_EVENT_CONSECUTIVE = 4;
-
-        /** @hide */
-        @RestrictTo(GROUP_ID)
-        @IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
-                DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface DismissEvent {}
-
-        /**
-         * Called when the given {@link Snackbar} has been dismissed, either through a time-out,
-         * having been manually dismissed, or an action being clicked.
-         *
-         * @param snackbar The snackbar which has been dismissed.
-         * @param event The event which caused the dismissal. One of either:
-         *              {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
-         *              {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
-         *              {@link #DISMISS_EVENT_CONSECUTIVE}.
-         *
-         * @see Snackbar#dismiss()
-         */
-        public void onDismissed(Snackbar snackbar, @DismissEvent int event) {
-            // empty
-        }
-
-        /**
-         * Called when the given {@link Snackbar} is visible.
-         *
-         * @param snackbar The snackbar which is now visible.
-         * @see Snackbar#show()
-         */
-        public void onShown(Snackbar snackbar) {
-            // empty
-        }
+    public static class Callback extends BaseCallback<Snackbar> {
     }
 
-    /**
-     * @hide
-     */
-    @RestrictTo(GROUP_ID)
-    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
-    @IntRange(from = 1)
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Duration {}
-
-    /**
-     * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
-     * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
-     *
-     * @see #setDuration
-     */
-    public static final int LENGTH_INDEFINITE = -2;
-
-    /**
-     * Show the Snackbar for a short period of time.
-     *
-     * @see #setDuration
-     */
-    public static final int LENGTH_SHORT = -1;
-
-    /**
-     * Show the Snackbar for a long period of time.
-     *
-     * @see #setDuration
-     */
-    public static final int LENGTH_LONG = 0;
-
-    static final int ANIMATION_DURATION = 250;
-    static final int ANIMATION_FADE_DURATION = 180;
-
-    static final Handler sHandler;
-    static final int MSG_SHOW = 0;
-    static final int MSG_DISMISS = 1;
-
-    static {
-        sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
-            @Override
-            public boolean handleMessage(Message message) {
-                switch (message.what) {
-                    case MSG_SHOW:
-                        ((Snackbar) message.obj).showView();
-                        return true;
-                    case MSG_DISMISS:
-                        ((Snackbar) message.obj).hideView(message.arg1);
-                        return true;
-                }
-                return false;
-            }
-        });
-    }
-
-    private final ViewGroup mTargetParent;
-    private final Context mContext;
-    final SnackbarLayout mView;
-    private int mDuration;
-    private Callback mCallback;
-
-    private final AccessibilityManager mAccessibilityManager;
-
-    private Snackbar(ViewGroup parent) {
-        mTargetParent = parent;
-        mContext = parent.getContext();
-
-        ThemeUtils.checkAppCompatTheme(mContext);
-
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        mView = (SnackbarLayout) inflater.inflate(
-                R.layout.design_layout_snackbar, mTargetParent, false);
-
-        mAccessibilityManager = (AccessibilityManager)
-                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    private Snackbar(ViewGroup parent, View content, ContentViewCallback contentViewCallback) {
+        super(parent, content, contentViewCallback);
     }
 
     /**
@@ -221,7 +86,12 @@
     @NonNull
     public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
             @Duration int duration) {
-        Snackbar snackbar = new Snackbar(findSuitableParent(view));
+        final ViewGroup parent = findSuitableParent(view);
+        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        final SnackbarContentLayout content =
+                (SnackbarContentLayout) inflater.inflate(
+                        R.layout.design_layout_snackbar_include, parent, false);
+        final Snackbar snackbar = new Snackbar(parent, content, content);
         snackbar.setText(text);
         snackbar.setDuration(duration);
         return snackbar;
@@ -278,25 +148,49 @@
     }
 
     /**
-     * Set the action to be displayed in this {@link Snackbar}.
+     * Update the text in this {@link Snackbar}.
      *
-     * @param resId    String resource to display
+     * @param message The new text for this {@link BaseTransientBottomBar}.
+     */
+    @NonNull
+    public Snackbar setText(@NonNull CharSequence message) {
+        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
+        final TextView tv = contentLayout.getMessageView();
+        tv.setText(message);
+        return this;
+    }
+
+    /**
+     * Update the text in this {@link Snackbar}.
+     *
+     * @param resId The new text for this {@link BaseTransientBottomBar}.
+     */
+    @NonNull
+    public Snackbar setText(@StringRes int resId) {
+        return setText(getContext().getText(resId));
+    }
+
+    /**
+     * Set the action to be displayed in this {@link BaseTransientBottomBar}.
+     *
+     * @param resId    String resource to display for the action
      * @param listener callback to be invoked when the action is clicked
      */
     @NonNull
     public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
-        return setAction(mContext.getText(resId), listener);
+        return setAction(getContext().getText(resId), listener);
     }
 
     /**
-     * Set the action to be displayed in this {@link Snackbar}.
+     * Set the action to be displayed in this {@link BaseTransientBottomBar}.
      *
-     * @param text     Text to display
+     * @param text     Text to display for the action
      * @param listener callback to be invoked when the action is clicked
      */
     @NonNull
     public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
-        final TextView tv = mView.getActionView();
+        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
+        final TextView tv = contentLayout.getActionView();
 
         if (TextUtils.isEmpty(text) || listener == null) {
             tv.setVisibility(View.GONE);
@@ -309,7 +203,7 @@
                 public void onClick(View view) {
                     listener.onClick(view);
                     // Now dismiss the Snackbar
-                    dispatchDismiss(Callback.DISMISS_EVENT_ACTION);
+                    dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
                 }
             });
         }
@@ -322,7 +216,8 @@
      */
     @NonNull
     public Snackbar setActionTextColor(ColorStateList colors) {
-        final TextView tv = mView.getActionView();
+        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
+        final TextView tv = contentLayout.getActionView();
         tv.setTextColor(colors);
         return this;
     }
@@ -333,555 +228,27 @@
      */
     @NonNull
     public Snackbar setActionTextColor(@ColorInt int color) {
-        final TextView tv = mView.getActionView();
+        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
+        final TextView tv = contentLayout.getActionView();
         tv.setTextColor(color);
         return this;
     }
 
     /**
-     * Update the text in this {@link Snackbar}.
-     *
-     * @param message The new text for the Toast.
-     */
-    @NonNull
-    public Snackbar setText(@NonNull CharSequence message) {
-        final TextView tv = mView.getMessageView();
-        tv.setText(message);
-        return this;
-    }
-
-    /**
-     * Update the text in this {@link Snackbar}.
-     *
-     * @param resId The new text for the Toast.
-     */
-    @NonNull
-    public Snackbar setText(@StringRes int resId) {
-        return setText(mContext.getText(resId));
-    }
-
-    /**
-     * Set how long to show the view for.
-     *
-     * @param duration either be one of the predefined lengths:
-     *                 {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
-     *                 in milliseconds.
-     */
-    @NonNull
-    public Snackbar setDuration(@Duration int duration) {
-        mDuration = duration;
-        return this;
-    }
-
-    /**
-     * Return the duration.
-     *
-     * @see #setDuration
-     */
-    @Duration
-    public int getDuration() {
-        return mDuration;
-    }
-
-    /**
-     * Returns the {@link Snackbar}'s view.
-     */
-    @NonNull
-    public View getView() {
-        return mView;
-    }
-
-    /**
-     * Show the {@link Snackbar}.
-     */
-    public void show() {
-        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
-    }
-
-    /**
-     * Dismiss the {@link Snackbar}.
-     */
-    public void dismiss() {
-        dispatchDismiss(Callback.DISMISS_EVENT_MANUAL);
-    }
-
-    void dispatchDismiss(@Callback.DismissEvent int event) {
-        SnackbarManager.getInstance().dismiss(mManagerCallback, event);
-    }
-
-    /**
-     * Set a callback to be called when this the visibility of this {@link Snackbar} changes.
-     */
-    @NonNull
-    public Snackbar setCallback(Callback callback) {
-        mCallback = callback;
-        return this;
-    }
-
-    /**
-     * Return whether this {@link Snackbar} is currently being shown.
-     */
-    public boolean isShown() {
-        return SnackbarManager.getInstance().isCurrent(mManagerCallback);
-    }
-
-    /**
-     * Returns whether this {@link Snackbar} is currently being shown, or is queued to be
-     * shown next.
-     */
-    public boolean isShownOrQueued() {
-        return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
-    }
-
-    final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
-        @Override
-        public void show() {
-            sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
-        }
-
-        @Override
-        public void dismiss(int event) {
-            sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
-        }
-    };
-
-    final void showView() {
-        if (mView.getParent() == null) {
-            final ViewGroup.LayoutParams lp = mView.getLayoutParams();
-
-            if (lp instanceof CoordinatorLayout.LayoutParams) {
-                // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
-                final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp;
-
-                final Behavior behavior = new Behavior();
-                behavior.setStartAlphaSwipeDistance(0.1f);
-                behavior.setEndAlphaSwipeDistance(0.6f);
-                behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
-                behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
-                    @Override
-                    public void onDismiss(View view) {
-                        view.setVisibility(View.GONE);
-                        dispatchDismiss(Callback.DISMISS_EVENT_SWIPE);
-                    }
-
-                    @Override
-                    public void onDragStateChanged(int state) {
-                        switch (state) {
-                            case SwipeDismissBehavior.STATE_DRAGGING:
-                            case SwipeDismissBehavior.STATE_SETTLING:
-                                // If the view is being dragged or settling, cancel the timeout
-                                SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
-                                break;
-                            case SwipeDismissBehavior.STATE_IDLE:
-                                // If the view has been released and is idle, restore the timeout
-                                SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
-                                break;
-                        }
-                    }
-                });
-                clp.setBehavior(behavior);
-                // Also set the inset edge so that views can dodge the snackbar correctly
-                clp.insetEdge = Gravity.BOTTOM;
-            }
-
-            mTargetParent.addView(mView);
-        }
-
-        mView.setOnAttachStateChangeListener(new SnackbarLayout.OnAttachStateChangeListener() {
-            @Override
-            public void onViewAttachedToWindow(View v) {}
-
-            @Override
-            public void onViewDetachedFromWindow(View v) {
-                if (isShownOrQueued()) {
-                    // If we haven't already been dismissed then this event is coming from a
-                    // non-user initiated action. Hence we need to make sure that we callback
-                    // and keep our state up to date. We need to post the call since removeView()
-                    // will call through to onDetachedFromWindow and thus overflow.
-                    sHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            onViewHidden(Callback.DISMISS_EVENT_MANUAL);
-                        }
-                    });
-                }
-            }
-        });
-
-        if (ViewCompat.isLaidOut(mView)) {
-            if (shouldAnimate()) {
-                // If animations are enabled, animate it in
-                animateViewIn();
-            } else {
-                // Else if anims are disabled just call back now
-                onViewShown();
-            }
-        } else {
-            // Otherwise, add one of our layout change listeners and show it in when laid out
-            mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() {
-                @Override
-                public void onLayoutChange(View view, int left, int top, int right, int bottom) {
-                    mView.setOnLayoutChangeListener(null);
-
-                    if (shouldAnimate()) {
-                        // If animations are enabled, animate it in
-                        animateViewIn();
-                    } else {
-                        // Else if anims are disabled just call back now
-                        onViewShown();
-                    }
-                }
-            });
-        }
-    }
-
-    void animateViewIn() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            ViewCompat.setTranslationY(mView, mView.getHeight());
-            ViewCompat.animate(mView)
-                    .translationY(0f)
-                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
-                    .setDuration(ANIMATION_DURATION)
-                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(View view) {
-                            mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
-                                    ANIMATION_FADE_DURATION);
-                        }
-
-                        @Override
-                        public void onAnimationEnd(View view) {
-                            onViewShown();
-                        }
-                    }).start();
-        } else {
-            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
-                    R.anim.design_snackbar_in);
-            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
-            anim.setDuration(ANIMATION_DURATION);
-            anim.setAnimationListener(new Animation.AnimationListener() {
-                @Override
-                public void onAnimationEnd(Animation animation) {
-                    onViewShown();
-                }
-
-                @Override
-                public void onAnimationStart(Animation animation) {}
-
-                @Override
-                public void onAnimationRepeat(Animation animation) {}
-            });
-            mView.startAnimation(anim);
-        }
-    }
-
-    private void animateViewOut(final int event) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            ViewCompat.animate(mView)
-                    .translationY(mView.getHeight())
-                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
-                    .setDuration(ANIMATION_DURATION)
-                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationStart(View view) {
-                            mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
-                        }
-
-                        @Override
-                        public void onAnimationEnd(View view) {
-                            onViewHidden(event);
-                        }
-                    }).start();
-        } else {
-            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
-                    R.anim.design_snackbar_out);
-            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
-            anim.setDuration(ANIMATION_DURATION);
-            anim.setAnimationListener(new Animation.AnimationListener() {
-                @Override
-                public void onAnimationEnd(Animation animation) {
-                    onViewHidden(event);
-                }
-
-                @Override
-                public void onAnimationStart(Animation animation) {}
-
-                @Override
-                public void onAnimationRepeat(Animation animation) {}
-            });
-            mView.startAnimation(anim);
-        }
-    }
-
-    final void hideView(@Callback.DismissEvent final int event) {
-        if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
-            animateViewOut(event);
-        } else {
-            // If anims are disabled or the view isn't visible, just call back now
-            onViewHidden(event);
-        }
-    }
-
-    void onViewShown() {
-        SnackbarManager.getInstance().onShown(mManagerCallback);
-        if (mCallback != null) {
-            mCallback.onShown(this);
-        }
-    }
-
-    void onViewHidden(int event) {
-        // First tell the SnackbarManager that it has been dismissed
-        SnackbarManager.getInstance().onDismissed(mManagerCallback);
-        // Now call the dismiss listener (if available)
-        if (mCallback != null) {
-            mCallback.onDismissed(this, event);
-        }
-        if (Build.VERSION.SDK_INT < 11) {
-            // We need to hide the Snackbar on pre-v11 since it uses an old style Animation.
-            // ViewGroup has special handling in removeView() when getAnimation() != null in
-            // that it waits. This then means that the calculated insets are wrong and the
-            // any dodging views do not return. We workaround it by setting the view to gone while
-            // ViewGroup actually gets around to removing it.
-            mView.setVisibility(View.GONE);
-        }
-        // Lastly, hide and remove the view from the parent (if attached)
-        final ViewParent parent = mView.getParent();
-        if (parent instanceof ViewGroup) {
-            ((ViewGroup) parent).removeView(mView);
-        }
-    }
-
-    /**
-     * Returns true if we should animate the Snackbar view in/out.
-     */
-    boolean shouldAnimate() {
-        return !mAccessibilityManager.isEnabled();
-    }
-
-    /**
      * @hide
+     *
+     * Note: this class is here to provide backwards-compatible way for apps written before
+     * the existence of the base {@link BaseTransientBottomBar} class.
      */
     @RestrictTo(GROUP_ID)
-    public static class SnackbarLayout extends LinearLayout {
-        private TextView mMessageView;
-        private Button mActionView;
-
-        private int mMaxWidth;
-        private int mMaxInlineActionWidth;
-
-        interface OnLayoutChangeListener {
-            void onLayoutChange(View view, int left, int top, int right, int bottom);
-        }
-
-        interface OnAttachStateChangeListener {
-            void onViewAttachedToWindow(View v);
-            void onViewDetachedFromWindow(View v);
-        }
-
-        private OnLayoutChangeListener mOnLayoutChangeListener;
-        private OnAttachStateChangeListener mOnAttachStateChangeListener;
-
+    public static final class SnackbarLayout extends BaseTransientBottomBar.SnackbarBaseLayout {
         public SnackbarLayout(Context context) {
-            this(context, null);
+            super(context);
         }
 
         public SnackbarLayout(Context context, AttributeSet attrs) {
             super(context, attrs);
-            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
-            mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
-            mMaxInlineActionWidth = a.getDimensionPixelSize(
-                    R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
-            if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
-                ViewCompat.setElevation(this, a.getDimensionPixelSize(
-                        R.styleable.SnackbarLayout_elevation, 0));
-            }
-            a.recycle();
-
-            setClickable(true);
-
-            // Now inflate our content. We need to do this manually rather than using an <include>
-            // in the layout since older versions of the Android do not inflate includes with
-            // the correct Context.
-            LayoutInflater.from(context).inflate(R.layout.design_layout_snackbar_include, this);
-
-            ViewCompat.setAccessibilityLiveRegion(this,
-                    ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
-            ViewCompat.setImportantForAccessibility(this,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-
-            // Make sure that we fit system windows and have a listener to apply any insets
-            ViewCompat.setFitsSystemWindows(this, true);
-            ViewCompat.setOnApplyWindowInsetsListener(this,
-                    new android.support.v4.view.OnApplyWindowInsetsListener() {
-                @Override
-                public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
-                    // Copy over the bottom inset as padding so that we're displayed above the
-                    // navigation bar
-                    v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
-                            v.getPaddingRight(), insets.getSystemWindowInsetBottom());
-                    return insets;
-                }
-            });
-        }
-
-        @Override
-        protected void onFinishInflate() {
-            super.onFinishInflate();
-            mMessageView = (TextView) findViewById(R.id.snackbar_text);
-            mActionView = (Button) findViewById(R.id.snackbar_action);
-        }
-
-        TextView getMessageView() {
-            return mMessageView;
-        }
-
-        Button getActionView() {
-            return mActionView;
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
-                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-
-            final int multiLineVPadding = getResources().getDimensionPixelSize(
-                    R.dimen.design_snackbar_padding_vertical_2lines);
-            final int singleLineVPadding = getResources().getDimensionPixelSize(
-                    R.dimen.design_snackbar_padding_vertical);
-            final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
-
-            boolean remeasure = false;
-            if (isMultiLine && mMaxInlineActionWidth > 0
-                    && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
-                if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
-                        multiLineVPadding - singleLineVPadding)) {
-                    remeasure = true;
-                }
-            } else {
-                final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
-                if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
-                    remeasure = true;
-                }
-            }
-
-            if (remeasure) {
-                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
-        }
-
-        void animateChildrenIn(int delay, int duration) {
-            ViewCompat.setAlpha(mMessageView, 0f);
-            ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
-                    .setStartDelay(delay).start();
-
-            if (mActionView.getVisibility() == VISIBLE) {
-                ViewCompat.setAlpha(mActionView, 0f);
-                ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
-                        .setStartDelay(delay).start();
-            }
-        }
-
-        void animateChildrenOut(int delay, int duration) {
-            ViewCompat.setAlpha(mMessageView, 1f);
-            ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
-                    .setStartDelay(delay).start();
-
-            if (mActionView.getVisibility() == VISIBLE) {
-                ViewCompat.setAlpha(mActionView, 1f);
-                ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
-                        .setStartDelay(delay).start();
-            }
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-            super.onLayout(changed, l, t, r, b);
-            if (mOnLayoutChangeListener != null) {
-                mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
-            }
-        }
-
-        @Override
-        protected void onAttachedToWindow() {
-            super.onAttachedToWindow();
-            if (mOnAttachStateChangeListener != null) {
-                mOnAttachStateChangeListener.onViewAttachedToWindow(this);
-            }
-
-            ViewCompat.requestApplyInsets(this);
-        }
-
-        @Override
-        protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            if (mOnAttachStateChangeListener != null) {
-                mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
-            }
-        }
-
-        void setOnLayoutChangeListener(OnLayoutChangeListener onLayoutChangeListener) {
-            mOnLayoutChangeListener = onLayoutChangeListener;
-        }
-
-        void setOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
-            mOnAttachStateChangeListener = listener;
-        }
-
-        private boolean updateViewsWithinLayout(final int orientation,
-                final int messagePadTop, final int messagePadBottom) {
-            boolean changed = false;
-            if (orientation != getOrientation()) {
-                setOrientation(orientation);
-                changed = true;
-            }
-            if (mMessageView.getPaddingTop() != messagePadTop
-                    || mMessageView.getPaddingBottom() != messagePadBottom) {
-                updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
-                changed = true;
-            }
-            return changed;
-        }
-
-        private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
-            if (ViewCompat.isPaddingRelative(view)) {
-                ViewCompat.setPaddingRelative(view,
-                        ViewCompat.getPaddingStart(view), topPadding,
-                        ViewCompat.getPaddingEnd(view), bottomPadding);
-            } else {
-                view.setPadding(view.getPaddingLeft(), topPadding,
-                        view.getPaddingRight(), bottomPadding);
-            }
-        }
-    }
-
-    final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
-        @Override
-        public boolean canSwipeDismissView(View child) {
-            return child instanceof SnackbarLayout;
-        }
-
-        @Override
-        public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarLayout child,
-                MotionEvent event) {
-            // We want to make sure that we disable any Snackbar timeouts if the user is
-            // currently touching the Snackbar. We restore the timeout when complete
-            if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
-                switch (event.getActionMasked()) {
-                    case MotionEvent.ACTION_DOWN:
-                        SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
-                        break;
-                    case MotionEvent.ACTION_UP:
-                    case MotionEvent.ACTION_CANCEL:
-                        SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
-                        break;
-                }
-            }
-
-            return super.onInterceptTouchEvent(parent, child, event);
         }
     }
 }
+
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index bc52ab7..904c904 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -44,6 +44,7 @@
 import android.support.v4.util.Pools;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.PointerIconCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewPager;
 import android.support.v4.widget.TextViewCompat;
@@ -1507,6 +1508,8 @@
             setGravity(Gravity.CENTER);
             setOrientation(VERTICAL);
             setClickable(true);
+            ViewCompat.setPointerIcon(this,
+                    PointerIconCompat.getSystemIcon(getContext(), PointerIconCompat.TYPE_HAND));
         }
 
         @Override
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 3355956..6aafb4f 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -58,6 +58,7 @@
 import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -159,6 +160,8 @@
     private boolean mHasReconstructedEditTextBackground;
     private boolean mInDrawableStateChanged;
 
+    private boolean mRestoringSavedState;
+
     public TextInputLayout(Context context) {
         this(context, null);
     }
@@ -316,7 +319,7 @@
         mEditText.addTextChangedListener(new TextWatcher() {
             @Override
             public void afterTextChanged(Editable s) {
-                updateLabelState(true);
+                updateLabelState(!mRestoringSavedState);
                 if (mCounterEnabled) {
                     updateCounter(s.length());
                 }
@@ -398,10 +401,14 @@
 
         if (hasText || (isEnabled() && (isFocused || isErrorShowing))) {
             // We should be showing the label so do so if it isn't already
-            collapseHint(animate);
+            if (mHintExpanded) {
+                collapseHint(animate);
+            }
         } else {
             // We should not be showing the label so hide it
-            expandHint(animate);
+            if (!mHintExpanded) {
+                expandHint(animate);
+            }
         }
     }
 
@@ -797,8 +804,8 @@
         } else {
             mCounterOverflowed = length > mCounterMaxLength;
             if (wasCounterOverflowed != mCounterOverflowed) {
-                TextViewCompat.setTextAppearance(mCounterView, mCounterOverflowed ?
-                        mCounterOverflowTextAppearance : mCounterTextAppearance);
+                TextViewCompat.setTextAppearance(mCounterView, mCounterOverflowed
+                        ? mCounterOverflowTextAppearance : mCounterTextAppearance);
             }
             mCounterView.setText(getContext().getString(R.string.character_counter_pattern,
                     length, mCounterMaxLength));
@@ -887,7 +894,7 @@
             super(superState);
         }
 
-        public SavedState(Parcel source, ClassLoader loader) {
+        SavedState(Parcel source, ClassLoader loader) {
             super(source, loader);
             error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
 
@@ -942,6 +949,13 @@
         requestLayout();
     }
 
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        mRestoringSavedState = true;
+        super.dispatchRestoreInstanceState(container);
+        mRestoringSavedState = false;
+    }
+
     /**
      * Returns the error message that was set to be displayed with
      * {@link #setError(CharSequence)}, or <code>null</code> if no error was set
@@ -1016,6 +1030,7 @@
             }
 
             mPasswordToggleView.setVisibility(VISIBLE);
+            mPasswordToggleView.setChecked(mPasswordToggledVisible);
 
             // We need to add a dummy drawable as the end compound drawable so that the text is
             // indented and doesn't display below the toggle view
@@ -1211,24 +1226,24 @@
         applyPasswordToggleTint();
     }
 
-   void passwordVisibilityToggleRequested() {
-       if (mPasswordToggleEnabled) {
-           // Store the current cursor position
-           final int selection = mEditText.getSelectionEnd();
+    void passwordVisibilityToggleRequested() {
+        if (mPasswordToggleEnabled) {
+            // Store the current cursor position
+            final int selection = mEditText.getSelectionEnd();
 
-           if (hasPasswordTransformation()) {
-               mEditText.setTransformationMethod(null);
-               mPasswordToggledVisible = true;
-           } else {
-               mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
-               mPasswordToggledVisible = false;
-           }
+            if (hasPasswordTransformation()) {
+                mEditText.setTransformationMethod(null);
+                mPasswordToggledVisible = true;
+            } else {
+                mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+                mPasswordToggledVisible = false;
+            }
 
-           mPasswordToggleView.setChecked(mPasswordToggledVisible);
+            mPasswordToggleView.setChecked(mPasswordToggledVisible);
 
-           // And restore the cursor position
-           mEditText.setSelection(selection);
-       }
+            // And restore the cursor position
+            mEditText.setSelection(selection);
+        }
     }
 
     private boolean hasPasswordTransformation() {
@@ -1339,7 +1354,8 @@
         mHintExpanded = true;
     }
 
-    private void animateToExpansionFraction(final float target) {
+    @VisibleForTesting
+    void animateToExpansionFraction(final float target) {
         if (mCollapsingTextHelper.getExpansionFraction() == target) {
             return;
         }
@@ -1411,4 +1427,4 @@
         }
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/design/tests/res/drawable-hdpi/ic_account_circle_white_48dp.png b/design/tests/res/drawable-hdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..bfd4632
--- /dev/null
+++ b/design/tests/res/drawable-hdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/design/tests/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png b/design/tests/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..c9dd4c1
--- /dev/null
+++ b/design/tests/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/design/tests/res/drawable-mdpi/ic_account_circle_white_48dp.png b/design/tests/res/drawable-mdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..246e0c8
--- /dev/null
+++ b/design/tests/res/drawable-mdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/design/tests/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png b/design/tests/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..91702b1
--- /dev/null
+++ b/design/tests/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xhdpi/ic_account_circle_white_48dp.png b/design/tests/res/drawable-xhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..07643f9
--- /dev/null
+++ b/design/tests/res/drawable-xhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png b/design/tests/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..afc7e53
--- /dev/null
+++ b/design/tests/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xxhdpi/ic_account_circle_white_48dp.png b/design/tests/res/drawable-xxhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..1ac34a7
--- /dev/null
+++ b/design/tests/res/drawable-xxhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png b/design/tests/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..1c16761
--- /dev/null
+++ b/design/tests/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png b/design/tests/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..baa6045
--- /dev/null
+++ b/design/tests/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/design/tests/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png b/design/tests/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..983a253
--- /dev/null
+++ b/design/tests/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/design/tests/res/layout/custom_snackbar_include.xml b/design/tests/res/layout/custom_snackbar_include.xml
new file mode 100644
index 0000000..4e610df
--- /dev/null
+++ b/design/tests/res/layout/custom_snackbar_include.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<android.support.design.widget.CustomSnackbarMainContent
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="8dp">
+
+    <ImageView
+        android:id="@+id/custom_snackbar_image_leading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_marginRight="8dp"
+        android:src="@drawable/ic_account_circle_white_48dp" />
+
+    <ImageView
+        android:id="@+id/custom_snackbar_image_trailing"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_marginTop="8dp"
+        android:src="@drawable/ic_lightbulb_outline_white_24dp" />
+
+    <TextView
+        android:id="@+id/custom_snackbar_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/custom_snackbar_image_leading"
+        android:layout_toLeftOf="@id/custom_snackbar_image_trailing"
+        android:layout_marginTop="4dp"
+        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+        android:textStyle="bold"
+        android:singleLine="true" />
+
+    <TextView
+        android:id="@+id/custom_snackbar_subtitle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/custom_snackbar_image_leading"
+        android:layout_toLeftOf="@id/custom_snackbar_image_trailing"
+        android:layout_below="@id/custom_snackbar_title"
+        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+        android:singleLine="true" />
+
+</android.support.design.widget.CustomSnackbarMainContent>
diff --git a/design/tests/res/layout/design_navigation_view_header_switch.xml b/design/tests/res/layout/design_navigation_view_header_switch.xml
new file mode 100644
index 0000000..5bfe8fa
--- /dev/null
+++ b/design/tests/res/layout/design_navigation_view_header_switch.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/header_frame"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp">
+
+    <android.support.v7.widget.SwitchCompat
+        android:id="@+id/header_toggle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"/>
+
+</FrameLayout>
diff --git a/documents-archive/AndroidManifest.xml b/design/tests/res/values-sw600dp/dimens.xml
similarity index 70%
copy from documents-archive/AndroidManifest.xml
copy to design/tests/res/values-sw600dp/dimens.xml
index 2cd0f7a..67765c5 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/design/tests/res/values-sw600dp/dimens.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<resources>
+    <dimen name="custom_snackbar_max_width">540dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/design/tests/res/values/dimens.xml b/design/tests/res/values/dimens.xml
index b46a4ba..37c1f48 100644
--- a/design/tests/res/values/dimens.xml
+++ b/design/tests/res/values/dimens.xml
@@ -32,4 +32,5 @@
     <dimen name="fab_mini_height">40dp</dimen>
     <dimen name="fab_normal_height">56dip</dimen>
 
+    <dimen name="custom_snackbar_max_width">-1px</dimen>
 </resources>
\ No newline at end of file
diff --git a/design/tests/res/values/ids.xml b/design/tests/res/values/ids.xml
index e5fcf63..52b8356 100644
--- a/design/tests/res/values/ids.xml
+++ b/design/tests/res/values/ids.xml
@@ -24,4 +24,6 @@
     <item name="page_7" type="id"/>
     <item name="page_8" type="id"/>
     <item name="page_9" type="id"/>
+    <item name="textinputlayout" type="id"/>
+    <item name="textinputedittext" type="id"/>
 </resources>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/SnackbarUtils.java b/design/tests/src/android/support/design/testutils/SnackbarUtils.java
index 18441e6..04aa878 100644
--- a/design/tests/src/android/support/design/testutils/SnackbarUtils.java
+++ b/design/tests/src/android/support/design/testutils/SnackbarUtils.java
@@ -15,21 +15,24 @@
  */
 package android.support.design.testutils;
 
-import android.support.annotation.Nullable;
-import android.support.design.widget.Snackbar;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingResource;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
-import org.hamcrest.Matcher;
-
 import static android.support.design.testutils.TestUtilsActions.waitUntilIdle;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
 
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.design.widget.Snackbar;
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.IdlingResource;
+
 public class SnackbarUtils {
-    private static class SnackbarShownCallback extends Snackbar.Callback
+    public interface TransientBottomBarAction {
+        void perform() throws Throwable;
+    }
+
+    private static class TransientBottomBarShownCallback
+            extends BaseTransientBottomBar.BaseCallback<BaseTransientBottomBar>
             implements IdlingResource {
         private boolean mIsShown = false;
 
@@ -45,7 +48,7 @@
 
         @Override
         public String getName() {
-            return "Snackbar shown callback";
+            return "Transient bottom bar shown callback";
         }
 
         @Override
@@ -58,7 +61,7 @@
         }
 
         @Override
-        public void onShown(Snackbar snackbar) {
+        public void onShown(BaseTransientBottomBar transientBottomBar) {
             mIsShown = true;
             if (mCallback != null) {
                 mCallback.onTransitionToIdle();
@@ -66,7 +69,8 @@
         }
     }
 
-    private static class SnackbarDismissedCallback extends Snackbar.Callback
+    private static class TransientBottomBarDismissedCallback
+            extends BaseTransientBottomBar.BaseCallback<BaseTransientBottomBar>
             implements IdlingResource {
         private boolean mIsDismissed = false;
 
@@ -82,7 +86,7 @@
 
         @Override
         public String getName() {
-            return "Snackbar dismissed callback";
+            return "Transient bottom bar dismissed callback";
         }
 
         @Override
@@ -95,7 +99,8 @@
         }
 
         @Override
-        public void onDismissed(Snackbar snackbar, @DismissEvent int event) {
+        public void onDismissed(BaseTransientBottomBar transientBottomBar,
+                @DismissEvent int event) {
             mIsDismissed = true;
             if (mCallback != null) {
                 mCallback.onTransitionToIdle();
@@ -105,82 +110,97 @@
 
     /**
      * Helper method that shows that specified {@link Snackbar} and waits until
-     * it has been fully shown. Note that calling this method will reset the currently
-     * set {@link Snackbar.Callback}.
+     * it has been fully shown.
      */
-    public static void showSnackbarAndWaitUntilFullyShown(Snackbar snackbar) {
-        SnackbarShownCallback snackbarCallback = new SnackbarShownCallback();
-        snackbar.setCallback(snackbarCallback);
+    public static void showTransientBottomBarAndWaitUntilFullyShown(
+            @NonNull BaseTransientBottomBar transientBottomBar) {
+        TransientBottomBarShownCallback callback = new TransientBottomBarShownCallback();
+        transientBottomBar.addCallback(callback);
         try {
             // Register our listener as idling resource so that Espresso waits until the
-            // the snackbar has been fully shown
-            Espresso.registerIdlingResources(snackbarCallback);
-            // Show the snackbar
-            snackbar.show();
+            // the bar has been fully shown
+            Espresso.registerIdlingResources(callback);
+            // Show the bar
+            transientBottomBar.show();
             // Mark the callback to require waiting for idle state
-            snackbarCallback.mNeedsIdle = true;
+            callback.mNeedsIdle = true;
             // Perform a dummy Espresso action that loops until the UI thread is idle. This
             // effectively blocks us until the Snackbar has completed its sliding animation.
             onView(isRoot()).perform(waitUntilIdle());
-            snackbarCallback.mNeedsIdle = false;
+            callback.mNeedsIdle = false;
         } finally {
             // Unregister our idling resource
-            Espresso.unregisterIdlingResources(snackbarCallback);
+            Espresso.unregisterIdlingResources(callback);
             // And remove our tracker listener from Snackbar
-            snackbar.setCallback(null);
+            transientBottomBar.removeCallback(callback);
         }
     }
 
     /**
      * Helper method that dismissed that specified {@link Snackbar} and waits until
-     * it has been fully dismissed. Note that calling this method will reset the currently
-     * set {@link Snackbar.Callback}.
+     * it has been fully dismissed.
      */
-    public static void dismissSnackbarAndWaitUntilFullyDismissed(Snackbar snackbar) {
-        SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback();
-        snackbar.setCallback(snackbarCallback);
+    public static void dismissTransientBottomBarAndWaitUntilFullyDismissed(
+            @NonNull final BaseTransientBottomBar transientBottomBar) throws Throwable {
+        performActionAndWaitUntilFullyDismissed(transientBottomBar,
+                new TransientBottomBarAction() {
+                    @Override
+                    public void perform() throws Throwable {
+                        transientBottomBar.dismiss();
+                    }
+                });
+    }
+
+    /**
+     * Helper method that dismissed that specified {@link Snackbar} and waits until
+     * it has been fully dismissed.
+     */
+    public static void performActionAndWaitUntilFullyDismissed(
+            @NonNull BaseTransientBottomBar transientBottomBar,
+            @NonNull TransientBottomBarAction action) throws Throwable {
+        TransientBottomBarDismissedCallback callback = new TransientBottomBarDismissedCallback();
+        transientBottomBar.addCallback(callback);
         try {
             // Register our listener as idling resource so that Espresso waits until the
-            // the snackbar has been fully dismissed
-            Espresso.registerIdlingResources(snackbarCallback);
-            // Dismiss the snackbar
-            snackbar.dismiss();
+            // the bar has been fully dismissed
+            Espresso.registerIdlingResources(callback);
+            // Run the action
+            action.perform();
             // Mark the callback to require waiting for idle state
-            snackbarCallback.mNeedsIdle = true;
+            callback.mNeedsIdle = true;
             // Perform a dummy Espresso action that loops until the UI thread is idle. This
             // effectively blocks us until the Snackbar has completed its sliding animation.
             onView(isRoot()).perform(waitUntilIdle());
-            snackbarCallback.mNeedsIdle = false;
+            callback.mNeedsIdle = false;
         } finally {
             // Unregister our idling resource
-            Espresso.unregisterIdlingResources(snackbarCallback);
+            Espresso.unregisterIdlingResources(callback);
             // And remove our tracker listener from Snackbar
-            snackbar.setCallback(null);
+            transientBottomBar.removeCallback(null);
         }
     }
 
     /**
-     * Helper method that waits until the given Snackbar has been fully dismissed. Note that
-     * calling this method will reset the currently set {@link Snackbar.Callback}.
+     * Helper method that waits until the given bar has been fully dismissed.
      */
-    public static void waitUntilFullyDismissed(Snackbar snackbar) {
-        SnackbarDismissedCallback snackbarCallback = new SnackbarDismissedCallback();
-        snackbar.setCallback(snackbarCallback);
+    public static void waitUntilFullyDismissed(@NonNull BaseTransientBottomBar transientBottomBar) {
+        TransientBottomBarDismissedCallback callback = new TransientBottomBarDismissedCallback();
+        transientBottomBar.addCallback(callback);
         try {
             // Register our listener as idling resource so that Espresso waits until the
-            // the snackbar has been fully dismissed
-            Espresso.registerIdlingResources(snackbarCallback);
+            // the bar has been fully dismissed
+            Espresso.registerIdlingResources(callback);
             // Mark the callback to require waiting for idle state
-            snackbarCallback.mNeedsIdle = true;
+            callback.mNeedsIdle = true;
             // Perform a dummy Espresso action that loops until the UI thread is idle. This
             // effectively blocks us until the Snackbar has completed its sliding animation.
             onView(isRoot()).perform(waitUntilIdle());
-            snackbarCallback.mNeedsIdle = false;
+            callback.mNeedsIdle = false;
         } finally {
             // Unregister our idling resource
-            Espresso.unregisterIdlingResources(snackbarCallback);
+            Espresso.unregisterIdlingResources(callback);
             // And remove our tracker listener from Snackbar
-            snackbar.setCallback(null);
+            transientBottomBar.removeCallback(null);
         }
     }
 }
diff --git a/design/tests/src/android/support/design/testutils/TestUtilsActions.java b/design/tests/src/android/support/design/testutils/TestUtilsActions.java
index dcac0ed..4fc982d 100644
--- a/design/tests/src/android/support/design/testutils/TestUtilsActions.java
+++ b/design/tests/src/android/support/design/testutils/TestUtilsActions.java
@@ -16,25 +16,28 @@
 
 package android.support.design.testutils;
 
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+
 import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
 import android.support.design.widget.CollapsingToolbarLayout;
 import android.support.design.widget.TabLayout;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPager;
 import android.support.v4.widget.TextViewCompat;
-import android.support.v7.widget.Toolbar;
+import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-import org.hamcrest.Matcher;
 
-import static android.support.test.espresso.matcher.ViewMatchers.*;
+import org.hamcrest.Matcher;
 
 public class TestUtilsActions {
     /**
@@ -68,7 +71,7 @@
                         // Create a new one
                         final LayoutInflater layoutInflater =
                                 LayoutInflater.from(view.getContext());
-                        final TabLayout newTabLayout =  (TabLayout) layoutInflater.inflate(
+                        final TabLayout newTabLayout = (TabLayout) layoutInflater.inflate(
                                 tabLayoutResId, viewGroup, false);
                         // Make sure we're adding the new TabLayout at the same index
                         viewGroup.addView(newTabLayout, i);
@@ -265,4 +268,31 @@
             }
         };
     }
+
+    /**
+     * Restores the saved hierarchy state.
+     *
+     * @param container The saved hierarchy state.
+     */
+    public static ViewAction restoreHierarchyState(final SparseArray<Parcelable> container) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(View.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "restore the saved state";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+                view.restoreHierarchyState(container);
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
 }
diff --git a/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java b/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
index e92ae28..39486e5 100644
--- a/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
@@ -16,6 +16,13 @@
 
 package android.support.design.widget;
 
+import static android.support.design.testutils.TestUtilsActions.setText;
+import static android.support.design.testutils.TestUtilsActions.setTitle;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertEquals;
+
 import android.os.Build;
 import android.support.annotation.CallSuper;
 import android.support.annotation.IdRes;
@@ -23,7 +30,6 @@
 import android.support.annotation.StringRes;
 import android.support.design.test.R;
 import android.support.design.testutils.Shakespeare;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.action.CoordinatesProvider;
 import android.support.test.espresso.action.GeneralSwipeAction;
 import android.support.test.espresso.action.Press;
@@ -35,13 +41,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import static android.support.design.testutils.TestUtilsActions.setText;
-import static android.support.design.testutils.TestUtilsActions.setTitle;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-import static org.junit.Assert.assertEquals;
-
 public abstract class AppBarLayoutBaseTest extends BaseDynamicCoordinatorLayoutTest {
 
     protected AppBarLayout mAppBar;
@@ -91,8 +90,8 @@
     }
 
     @CallSuper
-    protected void configureContent(final @LayoutRes int layoutResId,
-            final @StringRes int titleResId) {
+    protected void configureContent(@LayoutRes final int layoutResId,
+            @StringRes final int titleResId) throws Throwable {
         onView(withId(R.id.coordinator_stub)).perform(inflateViewStub(layoutResId));
 
         mAppBar = (AppBarLayout) mCoordinatorLayout.findViewById(R.id.app_bar);
@@ -101,7 +100,7 @@
         mToolbar = (Toolbar) mAppBar.findViewById(R.id.toolbar);
 
         final AppCompatActivity activity = mActivityTestRule.getActivity();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 activity.setSupportActionBar(mToolbar);
diff --git a/design/tests/src/android/support/design/widget/AppBarWithAnchoredFabMarginsTest.java b/design/tests/src/android/support/design/widget/AppBarWithAnchoredFabMarginsTest.java
index 748e079..416eda1 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithAnchoredFabMarginsTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithAnchoredFabMarginsTest.java
@@ -16,13 +16,14 @@
 
 package android.support.design.widget;
 
+import static org.junit.Assert.assertEquals;
+
 import android.support.design.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-
 @SmallTest
 public class AppBarWithAnchoredFabMarginsTest extends AppBarLayoutBaseTest {
     private int mFabMargin;
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index d04a510..1a796e3 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -21,9 +21,8 @@
 import android.os.Build;
 import android.os.SystemClock;
 import android.support.design.test.R;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.ImageView;
 
 import org.junit.Test;
@@ -31,7 +30,7 @@
 @MediumTest
 public class AppBarWithCollapsingToolbarTest extends AppBarLayoutBaseTest {
     @Test
-    public void testPinnedToolbar() {
+    public void testPinnedToolbar() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin,
                 R.string.design_appbar_collapsing_toolbar_pin);
 
@@ -131,7 +130,7 @@
     }
 
     @Test
-    public void testScrollingToolbar() {
+    public void testScrollingToolbar() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_scroll,
                 R.string.design_appbar_collapsing_toolbar_scroll);
 
@@ -301,7 +300,7 @@
     }
 
     @Test
-    public void testPinnedToolbarAndParallaxImage() {
+    public void testPinnedToolbarAndParallaxImage() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_with_image,
                 R.string.design_appbar_collapsing_toolbar_with_image);
 
@@ -384,11 +383,11 @@
     }
 
     @Test
-    public void testAddViewWithDefaultLayoutParams() {
+    public void testAddViewWithDefaultLayoutParams() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin,
                 R.string.design_appbar_collapsing_toolbar_pin);
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ImageView view = new ImageView(mCollapsingToolbar.getContext());
@@ -405,7 +404,7 @@
      */
     @Test
     @SdkSuppress(minSdkVersion = 11)
-    public void testPinnedToolbarWithMargins() {
+    public void testPinnedToolbarWithMargins() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin_margins,
                 R.string.design_appbar_collapsing_toolbar_pin_margins);
 
diff --git a/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java b/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
index c35b893..7604457 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithToolbarAndTabsTest.java
@@ -16,26 +16,29 @@
 
 package android.support.design.widget;
 
+import static android.support.design.testutils.TestUtilsActions.addTabs;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertEquals;
+
 import android.os.SystemClock;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.StringRes;
 import android.support.design.test.R;
 import android.support.design.testutils.Cheeses;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import org.junit.Test;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
 
-import static android.support.design.testutils.TestUtilsActions.addTabs;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assert.assertEquals;
+import org.junit.Test;
 
 @MediumTest
 public class AppBarWithToolbarAndTabsTest extends AppBarLayoutBaseTest {
     private TabLayout mTabLayout;
 
     @Override
-    protected void configureContent(@LayoutRes int layoutResId, @StringRes int titleResId) {
+    protected void configureContent(@LayoutRes int layoutResId, @StringRes int titleResId)
+            throws Throwable {
         super.configureContent(layoutResId, titleResId);
 
         mTabLayout = (TabLayout) mAppBar.findViewById(R.id.tabs);
@@ -45,7 +48,7 @@
     }
 
     @Test
-    public void testScrollingToolbarAndScrollingTabs() {
+    public void testScrollingToolbarAndScrollingTabs() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_tabs_scroll,
                 R.string.design_appbar_toolbar_scroll_tabs_scroll);
 
@@ -138,7 +141,7 @@
     }
 
     @Test
-    public void testScrollingToolbarAndPinnedTabs() {
+    public void testScrollingToolbarAndPinnedTabs() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_tabs_pinned,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -232,7 +235,7 @@
 
     @LargeTest
     @Test
-    public void testSnappingToolbarAndSnappingTabs() {
+    public void testSnappingToolbarAndSnappingTabs() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_tabs_scroll_snap,
                 R.string.design_appbar_toolbar_scroll_tabs_scroll_snap);
 
diff --git a/design/tests/src/android/support/design/widget/AppBarWithToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithToolbarTest.java
index eaaec82..7045faf 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithToolbarTest.java
@@ -20,13 +20,12 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 
 import android.graphics.Rect;
 import android.support.design.test.R;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -39,7 +38,7 @@
      * Tests a Toolbar with fitSystemWindows = undefined, with a fitSystemWindows = true parent
      */
     @Test
-    public void testScrollToolbarWithFitSystemWindowsParent() {
+    public void testScrollToolbarWithFitSystemWindowsParent() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_fitsystemwindows_parent,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -84,7 +83,7 @@
      * with a fitSystemWindows = true parent
      */
     @Test
-    public void testScrollingContentPositionWithFitSystemWindowsParent() {
+    public void testScrollingContentPositionWithFitSystemWindowsParent() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_fitsystemwindows_parent,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -108,7 +107,7 @@
      * with a fitSystemWindows = true parent, in RTL
      */
     @Test
-    public void testScrollingContentPositionWithFitSystemWindowsParentInRtl() {
+    public void testScrollingContentPositionWithFitSystemWindowsParentInRtl() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_fitsystemwindows_parent,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -131,7 +130,7 @@
     }
 
     @Test
-    public void testRequestRectangleWithChildThatDoesNotRequireScroll() {
+    public void testRequestRectangleWithChildThatDoesNotRequireScroll() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_fitsystemwindows_parent,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -142,7 +141,7 @@
         scrollingContent.getLocationInWindow(originalScrollingXY);
 
         // Now request that the first child has its full rectangle displayed
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final ViewGroup scrollingContentInner = (ViewGroup) scrollingContent
@@ -163,7 +162,7 @@
     }
 
     @Test
-    public void testRequestRectangleWithChildThatDoesRequireScroll() {
+    public void testRequestRectangleWithChildThatDoesRequireScroll() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_scroll_fitsystemwindows_parent,
                 R.string.design_appbar_toolbar_scroll_tabs_pin);
 
@@ -174,7 +173,7 @@
         scrollingContent.getLocationInWindow(originalScrollingXY);
 
         // Now request that the first child has its full rectangle displayed
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final ViewGroup scrollingContentInner = (ViewGroup) scrollingContent
diff --git a/design/tests/src/android/support/design/widget/BaseDynamicCoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/BaseDynamicCoordinatorLayoutTest.java
index 8c6f331..32cef17 100755
--- a/design/tests/src/android/support/design/widget/BaseDynamicCoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/BaseDynamicCoordinatorLayoutTest.java
@@ -15,23 +15,25 @@
  */
 package android.support.design.widget;
 
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.Matchers.any;
+
 import android.support.annotation.LayoutRes;
 import android.support.design.test.R;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
 import android.view.ViewStub;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.any;
-
 /**
  * Base class for tests that are exercising various aspects of {@link CoordinatorLayout}.
  */
@@ -43,18 +45,14 @@
         super(DynamicCoordinatorLayoutActivity.class);
     }
 
+    @UiThreadTest
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         // Now that the test is done, replace the activity content view with ViewStub so
         // that it's ready to be replaced for the next test.
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final DynamicCoordinatorLayoutActivity activity = mActivityTestRule.getActivity();
-                activity.setContentView(R.layout.dynamic_coordinator_layout);
-                mCoordinatorLayout = null;
-            }
-        });
+        final DynamicCoordinatorLayoutActivity activity = mActivityTestRule.getActivity();
+        activity.setContentView(R.layout.dynamic_coordinator_layout);
+        mCoordinatorLayout = null;
     }
 
     /**
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index b716630..9c1c6ca 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -43,8 +43,8 @@
 import android.support.design.testutils.TestDrawable;
 import android.support.design.testutils.TestUtilsMatchers;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.content.res.ResourcesCompat;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.ViewGroup;
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorInitialStateTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorInitialStateTest.java
index 7cfce3d..a59aa87 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorInitialStateTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorInitialStateTest.java
@@ -22,9 +22,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
index 1b79fb3..f38a03f 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
@@ -16,18 +16,17 @@
 
 package android.support.design.widget;
 
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.fail;
 
 import android.os.SystemClock;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.NonNull;
 import android.support.design.test.R;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.IdlingResource;
 import android.support.test.espresso.NoMatchingViewException;
@@ -45,10 +44,11 @@
 import android.support.test.espresso.assertion.ViewAssertions;
 import android.support.test.espresso.core.deps.guava.base.Preconditions;
 import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.NestedScrollView;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -285,27 +285,27 @@
 
     @Test
     @MediumTest
-    public void testSetStateExpandedToCollapsed() {
+    public void testSetStateExpandedToCollapsed() throws Throwable {
         checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
         checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
     }
 
     @Test
     @MediumTest
-    public void testSetStateHiddenToCollapsed() {
+    public void testSetStateHiddenToCollapsed() throws Throwable {
         checkSetState(BottomSheetBehavior.STATE_HIDDEN, not(ViewMatchers.isDisplayed()));
         checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
     }
 
     @Test
     @MediumTest
-    public void testSetStateCollapsedToCollapsed() {
+    public void testSetStateCollapsedToCollapsed() throws Throwable {
         checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
     }
 
     @Test
     @MediumTest
-    public void testSwipeDownToCollapse() {
+    public void testSwipeDownToCollapse() throws Throwable {
         checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
         Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet))
                 .perform(DesignViewActions.withCustomConstraints(new GeneralSwipeAction(
@@ -363,7 +363,8 @@
     }
 
     @Test
-    public void testSkipCollapsed() {
+    @MediumTest
+    public void testSkipCollapsed() throws Throwable {
         getBehavior().setSkipCollapsed(true);
         checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
         Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet))
@@ -430,9 +431,9 @@
 
     @Test
     @MediumTest
-    public void testInvisible() {
+    public void testInvisible() throws Throwable {
         // Make the bottomsheet invisible
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getBottomSheet().setVisibility(View.INVISIBLE);
@@ -451,7 +452,7 @@
                         }, Press.FINGER),
                         not(ViewMatchers.isDisplayed())));
         // Check that the bottom sheet stays the same collapsed state
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED));
@@ -461,8 +462,8 @@
 
     @Test
     @MediumTest
-    public void testInvisibleThenVisible() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testInvisibleThenVisible() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // The bottom sheet is initially invisible
@@ -501,12 +502,12 @@
 
     @Test
     @MediumTest
-    public void testNestedScroll() {
+    public void testNestedScroll() throws Throwable {
         final ViewGroup bottomSheet = getBottomSheet();
         final BottomSheetBehavior behavior = getBehavior();
         final NestedScrollView scroll = new NestedScrollView(mActivityTestRule.getActivity());
         // Set up nested scrolling area
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 bottomSheet.addView(scroll, new ViewGroup.LayoutParams(
@@ -549,7 +550,7 @@
         try {
             Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet))
                     .check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            mActivityTestRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     assertThat(behavior.getState(), is(BottomSheetBehavior.STATE_EXPANDED));
@@ -631,31 +632,43 @@
                 });
     }
 
+    // Test disabled because it is consistently failing.
+    @Suppress
     @Test
+    @MediumTest
     public void testFabVisibility() {
         withFabVisibilityChange(false, new Runnable() {
             @Override
             public void run() {
-                checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
+                try {
+                    checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
+                } catch (Throwable throwable) {
+                    fail(throwable.getMessage());
+                }
             }
         });
         withFabVisibilityChange(true, new Runnable() {
             @Override
             public void run() {
-                checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
+                try {
+                    checkSetState(BottomSheetBehavior.STATE_COLLAPSED, ViewMatchers.isDisplayed());
+                } catch (Throwable throwable) {
+                    fail(throwable.getMessage());
+                }
             }
         });
     }
 
     @Test
-    public void testAutoPeekHeight() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    @MediumTest
+    public void testAutoPeekHeight() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getBehavior().setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO);
             }
         });
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 CoordinatorLayout col = getCoordinatorLayout();
@@ -668,8 +681,8 @@
 
     @Test
     @MediumTest
-    public void testAutoPeekHeightHide() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testAutoPeekHeightHide() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getBehavior().setHideable(true);
@@ -681,10 +694,11 @@
     }
 
     @Test
-    public void testDynamicContent() {
+    @MediumTest
+    public void testDynamicContent() throws Throwable {
         registerIdlingResourceCallback();
         try {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            mActivityTestRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     ViewGroup.LayoutParams params = getBottomSheet().getLayoutParams();
@@ -712,10 +726,10 @@
         }
     }
 
-    private void checkSetState(final int state, Matcher<View> matcher) {
+    private void checkSetState(final int state, Matcher<View> matcher) throws Throwable {
         registerIdlingResourceCallback();
         try {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            mActivityTestRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     getBehavior().setState(state);
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
index fe62e441..4e3bbf5 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTouchTest.java
@@ -22,13 +22,13 @@
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
 import android.support.test.espresso.action.ViewActions;
+import android.support.test.filters.MediumTest;
 import android.support.v4.view.MotionEventCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -66,23 +66,19 @@
         super(CoordinatorLayoutActivity.class);
     }
 
+    @UiThreadTest
     @Before
     public void setUpBottomSheet() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                CoordinatorLayoutActivity activity = mActivityTestRule.getActivity();
-                activity.mContainer.setOnTouchListener(mOnTouchListener);
-                mBottomSheet = new FrameLayout(activity);
-                CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(
-                        CoordinatorLayout.LayoutParams.MATCH_PARENT,
-                        CoordinatorLayout.LayoutParams.MATCH_PARENT);
-                mBehavior = new BottomSheetBehavior<>();
-                mBehavior.setPeekHeight(PEEK_HEIGHT);
-                params.setBehavior(mBehavior);
-                activity.mCoordinatorLayout.addView(mBottomSheet, params);
-            }
-        });
+        CoordinatorLayoutActivity activity = mActivityTestRule.getActivity();
+        activity.mContainer.setOnTouchListener(mOnTouchListener);
+        mBottomSheet = new FrameLayout(activity);
+        CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(
+                CoordinatorLayout.LayoutParams.MATCH_PARENT,
+                CoordinatorLayout.LayoutParams.MATCH_PARENT);
+        mBehavior = new BottomSheetBehavior<>();
+        mBehavior.setPeekHeight(PEEK_HEIGHT);
+        params.setBehavior(mBehavior);
+        activity.mCoordinatorLayout.addView(mBottomSheet, params);
     }
 
     @Test
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorWithInsetsTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorWithInsetsTest.java
index 77a0b26..f77542e 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorWithInsetsTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorWithInsetsTest.java
@@ -19,8 +19,8 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import android.support.test.filters.SmallTest;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ViewGroup;
 
 import org.junit.Test;
diff --git a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
index 7f81320..84c522c 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
@@ -26,15 +26,14 @@
 import android.content.DialogInterface;
 import android.os.SystemClock;
 import android.support.design.test.R;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.action.ViewActions;
 import android.support.test.espresso.assertion.ViewAssertions;
 import android.support.test.espresso.matcher.ViewMatchers;
+import android.support.test.filters.MediumTest;
 import android.support.v7.widget.AppCompatTextView;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -56,8 +55,8 @@
 
     @Test
     @MediumTest
-    public void testBasicDialogSetup() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testBasicDialogSetup() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 showDialog();
@@ -76,7 +75,7 @@
         // Click outside the bottom sheet
         Espresso.onView(ViewMatchers.withId(R.id.touch_outside))
                 .perform(ViewActions.click());
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // Confirm that the dialog is no longer shown
@@ -87,8 +86,8 @@
 
     @Test
     @MediumTest
-    public void testShortDialog() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testShortDialog() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 showDialog();
@@ -98,7 +97,7 @@
         Espresso.onView(ViewMatchers.withId(R.id.design_bottom_sheet))
                 .perform(setTallPeekHeight())
                 .check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 FrameLayout bottomSheet = (FrameLayout) mDialog
@@ -119,8 +118,8 @@
 
     @Test
     @MediumTest
-    public void testNonCancelableDialog() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testNonCancelableDialog() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 showDialog();
@@ -130,7 +129,7 @@
         // Click outside the bottom sheet
         Espresso.onView(ViewMatchers.withId(R.id.touch_outside))
                 .perform(ViewActions.click());
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 FrameLayout bottomSheet = (FrameLayout) mDialog
@@ -146,9 +145,9 @@
 
     @Test
     @MediumTest
-    public void testHideBottomSheet() {
+    public void testHideBottomSheet() throws Throwable {
         final AtomicBoolean canceled = new AtomicBoolean(false);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 showDialog();
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java
index 34c70c8..0a407b7 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutSortTest.java
@@ -21,7 +21,7 @@
 import android.app.Instrumentation;
 import android.support.design.testutils.CoordinatorLayoutUtils;
 import android.support.test.InstrumentationRegistry;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -68,7 +68,7 @@
     }
 
     @Test
-    public void testDependencySortingOrder() {
+    public void testDependencySortingOrder() throws Throwable {
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
         // Let's create some views where each view depends on the previous view.
@@ -107,12 +107,12 @@
         addViewsAndAssertOrdering(col, views, testOrder);
     }
 
-    private static void addViewsAndAssertOrdering(final CoordinatorLayout col,
-            final List<View> expectedOrder, final List<View> addOrder) {
+    private void addViewsAndAssertOrdering(final CoordinatorLayout col,
+            final List<View> expectedOrder, final List<View> addOrder) throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
 
         // Add the Views in the given order
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 for (int i = 0; i < addOrder.size(); i++) {
@@ -126,7 +126,7 @@
         assertEquals(expectedOrder, col.getDependencySortedChildren());
 
         // Finally remove all of the views
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.removeAllViews();
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 9fff6a2..4a3b8b6 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -35,10 +35,10 @@
 import android.support.design.testutils.CoordinatorLayoutUtils;
 import android.support.design.widget.CoordinatorLayout.Behavior;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.WindowInsetsCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -65,7 +65,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
-    public void testSetFitSystemWindows() {
+    public void testSetFitSystemWindows() throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
         final View view = new View(col.getContext());
@@ -81,7 +81,7 @@
 
         // Now add a view with our mocked behavior to the CoordinatorLayout
         view.setFitsSystemWindows(true);
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final CoordinatorLayout.LayoutParams lp = col.generateDefaultLayoutParams();
@@ -92,7 +92,7 @@
         instrumentation.waitForIdleSync();
 
         // Now request some insets and wait for the pass to happen
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.requestApplyInsets();
@@ -105,7 +105,7 @@
                 .onApplyWindowInsets(same(col), same(view), any(WindowInsetsCompat.class));
 
         // Now enable fits system windows and wait for a pass to happen
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.setFitsSystemWindows(true);
@@ -173,7 +173,7 @@
     }
 
     @Test
-    public void testInsetEdge() {
+    public void testInsetEdge() throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
@@ -181,7 +181,7 @@
         final View dodgeInsetView = new View(col.getContext());
         final AtomicInteger originalTop = new AtomicInteger();
 
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 CoordinatorLayout.LayoutParams lpInsetView = col.generateDefaultLayoutParams();
@@ -202,7 +202,7 @@
             }
         });
         instrumentation.waitForIdleSync();
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 List<View> dependencies = col.getDependencies(dodgeInsetView);
@@ -216,7 +216,7 @@
             }
         });
         instrumentation.waitForIdleSync();
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // Confirm that the dodging view was moved by the same size
@@ -226,7 +226,7 @@
     }
 
     @Test
-    public void testDependentViewChanged() {
+    public void testDependentViewChanged() throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
@@ -244,7 +244,7 @@
                 spy(new CoordinatorLayoutUtils.DependentBehavior(viewA));
         lpB.setBehavior(behavior);
 
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.addView(viewA, lpA);
@@ -258,7 +258,7 @@
         reset(behavior);
 
         // Now offset view A
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.offsetLeftAndRight(viewA, 20);
@@ -272,7 +272,7 @@
     }
 
     @Test
-    public void testDependentViewRemoved() {
+    public void testDependentViewRemoved() throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
@@ -284,7 +284,7 @@
                 spy(new CoordinatorLayoutUtils.DependentBehavior(viewA));
         lpB.setBehavior(behavior);
 
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.addView(viewA);
@@ -294,7 +294,7 @@
         instrumentation.waitForIdleSync();
 
         // Now remove view A
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.removeView(viewA);
@@ -306,7 +306,7 @@
     }
 
     @Test
-    public void testGetDependenciesAfterDependentViewRemoved() {
+    public void testGetDependenciesAfterDependentViewRemoved() throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
@@ -325,7 +325,7 @@
         lpB.setBehavior(behavior);
 
         // Now add views
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.addView(viewA);
@@ -337,7 +337,7 @@
         instrumentation.waitForIdleSync();
 
         // Now remove view A, which will trigger onDependentViewRemoved() on view B's behavior
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 col.removeView(viewA);
diff --git a/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java b/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
index ca56ecc..2cdc990 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorSnackbarWithFabTest.java
@@ -16,28 +16,30 @@
 
 package android.support.design.widget;
 
+import static android.support.design.widget.DesignViewActions.setVisibility;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 import android.os.Build;
 import android.support.design.custom.TestFloatingBehavior;
 import android.support.design.test.R;
 import android.support.design.testutils.SnackbarUtils;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
+import android.support.test.filters.MediumTest;
 import android.support.v7.widget.AppCompatTextView;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
+
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Test;
 
-import static android.support.design.widget.DesignViewActions.setVisibility;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
 @MediumTest
 public class CoordinatorSnackbarWithFabTest extends BaseDynamicCoordinatorLayoutTest {
     private static final String MESSAGE_TEXT = "Test Message";
@@ -46,10 +48,10 @@
     private Snackbar mSnackbar;
 
     @After
-    public void teardown() {
+    public void teardown() throws Throwable {
         // Dismiss the snackbar to get back to clean state for the next test
         if (mSnackbar != null) {
-            SnackbarUtils.dismissSnackbarAndWaitUntilFullyDismissed(mSnackbar);
+            SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(mSnackbar);
         }
     }
 
@@ -105,7 +107,7 @@
         // Create and show a snackbar
         mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(mSnackbar);
 
         // Take into account bottom padding and bottom margin to account for how drop shadow is
         // emulated on pre-Lollipop devices
@@ -124,7 +126,7 @@
         // Create and show a snackbar
         mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(mSnackbar);
 
         // Take into account bottom padding and bottom margin to account for how drop shadow is
         // emulated on pre-Lollipop devices
@@ -145,7 +147,7 @@
         // Create and show a snackbar
         mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(mSnackbar);
 
         final AppCompatTextView textView =
                 (AppCompatTextView) mCoordinatorLayout.findViewById(R.id.text);
@@ -162,7 +164,7 @@
         // Create and show a snackbar
         mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(mSnackbar);
 
         final AppCompatTextView textView =
                 (AppCompatTextView) mCoordinatorLayout.findViewById(R.id.text);
@@ -185,7 +187,7 @@
         // Create and show a snackbar
         mSnackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(mSnackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(mSnackbar);
 
         verifySnackbarViewStacking(textView, 0);
     }
diff --git a/design/tests/src/android/support/design/widget/CustomSnackbar.java b/design/tests/src/android/support/design/widget/CustomSnackbar.java
new file mode 100644
index 0000000..caa1dc4
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/CustomSnackbar.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.design.widget;
+
+import android.support.design.test.R;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Sample code for a custom snackbar that shows two separate text views and two images
+ * in the main content area.
+ */
+public class CustomSnackbar extends BaseTransientBottomBar<CustomSnackbar> {
+    public CustomSnackbar(CoordinatorLayout parent, View content,
+            BaseTransientBottomBar.ContentViewCallback contentViewCallback) {
+        super(parent, content, contentViewCallback);
+    }
+
+    /** Sets the title of this custom snackbar. */
+    public CustomSnackbar setTitle(String title) {
+        TextView titleView = (TextView) getView().findViewById(R.id.custom_snackbar_title);
+        titleView.setText(title);
+        return this;
+    }
+
+    /** Sets the subtitle of this custom snackbar. */
+    public CustomSnackbar setSubtitle(String subtitle) {
+        TextView subtitleView = (TextView) getView().findViewById(R.id.custom_snackbar_subtitle);
+        subtitleView.setText(subtitle);
+        return this;
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/CustomSnackbarMainContent.java b/design/tests/src/android/support/design/widget/CustomSnackbarMainContent.java
new file mode 100644
index 0000000..8435bf7
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/CustomSnackbarMainContent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.design.widget;
+
+import android.content.Context;
+import android.support.design.test.R;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+/**
+ * Layout for the custom snackbar that shows two separate text views and two images
+ * in the main content area.
+ */
+public class CustomSnackbarMainContent extends RelativeLayout {
+    private final int mMaxWidth;
+
+    public CustomSnackbarMainContent(Context context) {
+        this(context, null);
+    }
+
+    public CustomSnackbarMainContent(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CustomSnackbarMainContent(Context context, AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mMaxWidth = context.getResources().getDimensionPixelSize(R.dimen.custom_snackbar_max_width);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if ((mMaxWidth > 0) && (getMeasuredWidth() > mMaxWidth)) {
+            super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY),
+                    heightMeasureSpec);
+        }
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/CustomSnackbarTest.java b/design/tests/src/android/support/design/widget/CustomSnackbarTest.java
new file mode 100644
index 0000000..cec87da
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/CustomSnackbarTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import static android.support.design.testutils.TestUtilsActions.setLayoutDirection;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.swipeLeft;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.core.AllOf.allOf;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.support.annotation.Nullable;
+import android.support.design.test.R;
+import android.support.design.testutils.SnackbarUtils;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.v4.view.ViewCompat;
+import android.view.LayoutInflater;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CustomSnackbarTest extends BaseInstrumentationTestCase<SnackbarActivity> {
+    private static final String TITLE_TEXT = "Test title";
+    private static final String SUBTITLE_TEXT = "Test subtitle";
+
+    private CoordinatorLayout mCoordinatorLayout;
+
+    private interface DismissAction {
+        void dismiss(CustomSnackbar snackbar);
+    }
+
+    public CustomSnackbarTest() {
+        super(SnackbarActivity.class);
+    }
+
+    @Before
+    public void setup() {
+        mCoordinatorLayout =
+                (CoordinatorLayout) mActivityTestRule.getActivity().findViewById(R.id.col);
+    }
+
+    private void verifySnackbarContent(final CustomSnackbar snackbar, final String expectedTitle,
+            final String expectedSubtitle) throws Throwable {
+        // Show the snackbar
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+
+        // Verify that we're showing the title
+        withText(expectedTitle).matches(allOf(
+                isDescendantOfA(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                isDescendantOfA(isAssignableFrom(CustomSnackbarMainContent.class)),
+                isCompletelyDisplayed()));
+
+        // Verify that we're showing the subtitle
+        withText(expectedSubtitle).matches(allOf(
+                isDescendantOfA(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                isDescendantOfA(isAssignableFrom(CustomSnackbarMainContent.class)),
+                isCompletelyDisplayed()));
+
+        // Dismiss the snackbar
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+    }
+
+    private CustomSnackbar makeCustomSnackbar() {
+        final LayoutInflater inflater = LayoutInflater.from(mCoordinatorLayout.getContext());
+        final CustomSnackbarMainContent content =
+                (CustomSnackbarMainContent) inflater.inflate(
+                        R.layout.custom_snackbar_include, mCoordinatorLayout, false);
+        final BaseTransientBottomBar.ContentViewCallback contentViewCallback =
+                new BaseTransientBottomBar.ContentViewCallback() {
+                    @Override
+                    public void animateContentIn(int delay, int duration) {
+                        ViewCompat.setAlpha(content, 0f);
+                        ViewCompat.animate(content).alpha(1f).setDuration(duration)
+                                .setStartDelay(delay).start();
+                    }
+
+                    @Override
+                    public void animateContentOut(int delay, int duration) {
+                        ViewCompat.setAlpha(content, 1f);
+                        ViewCompat.animate(content).alpha(0f).setDuration(duration)
+                                .setStartDelay(delay).start();
+                    }
+                };
+        return new CustomSnackbar(mCoordinatorLayout, content, contentViewCallback);
+    }
+
+    @Test
+    @SmallTest
+    public void testBasicContent() throws Throwable {
+        // Verify different combinations of snackbar content (title / subtitle and action)
+        // and duration
+
+        // Short duration
+        verifySnackbarContent(
+                makeCustomSnackbar().setTitle(TITLE_TEXT)
+                        .setSubtitle(SUBTITLE_TEXT).setDuration(Snackbar.LENGTH_SHORT),
+                TITLE_TEXT, SUBTITLE_TEXT);
+
+        // Long duration
+        verifySnackbarContent(
+                makeCustomSnackbar().setTitle(TITLE_TEXT)
+                        .setSubtitle(SUBTITLE_TEXT).setDuration(Snackbar.LENGTH_LONG),
+                TITLE_TEXT, SUBTITLE_TEXT);
+
+        // Indefinite duration
+        verifySnackbarContent(
+                makeCustomSnackbar().setTitle(TITLE_TEXT)
+                        .setSubtitle(SUBTITLE_TEXT).setDuration(Snackbar.LENGTH_INDEFINITE),
+                TITLE_TEXT, SUBTITLE_TEXT);
+    }
+
+    private void verifyDismissCallback(final ViewInteraction interaction,
+            @Nullable final ViewAction action, @Nullable final DismissAction dismissAction,
+            final int length, @Snackbar.Callback.DismissEvent final int expectedEvent)
+            throws Throwable {
+        final BaseTransientBottomBar.BaseCallback mockCallback =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        final CustomSnackbar snackbar = makeCustomSnackbar().setTitle(TITLE_TEXT)
+                .setSubtitle(SUBTITLE_TEXT).setDuration(length)
+                .addCallback(mockCallback);
+
+        // Show the snackbar
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        // Verify that our onShown has been called
+        verify(mockCallback, times(1)).onShown(snackbar);
+        // and that the snackbar is either shown or queued to be shown
+        assertTrue(snackbar.isShownOrQueued());
+        // and also check that we have the intended title / subtitle displayed somewhere in
+        // our hierarchy
+        onView(withText(TITLE_TEXT)).check(matches(isCompletelyDisplayed()));
+        onView(withText(SUBTITLE_TEXT)).check(matches(isCompletelyDisplayed()));
+
+        // Now perform the UI interaction
+        SnackbarUtils.performActionAndWaitUntilFullyDismissed(snackbar,
+                new SnackbarUtils.TransientBottomBarAction() {
+                    @Override
+                    public void perform() throws Throwable {
+                        if (action != null) {
+                            interaction.perform(action);
+                        } else if (dismissAction != null) {
+                            mActivityTestRule.runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    dismissAction.dismiss(snackbar);
+                                }
+                            });
+                        }
+                    }
+                });
+
+        // Verify that our onDismissed has been called
+        verify(mockCallback, times(1)).onDismissed(snackbar, expectedEvent);
+        verifyNoMoreInteractions(mockCallback);
+        // and that the snackbar is neither shown nor queued to be shown
+        assertFalse(snackbar.isShownOrQueued());
+    }
+
+    @Test
+    @MediumTest
+    public void testDismissViaSwipe() throws Throwable {
+        verifyDismissCallback(
+                onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                swipeRight(),
+                null,
+                Snackbar.LENGTH_LONG,
+                Snackbar.Callback.DISMISS_EVENT_SWIPE);
+    }
+
+    @Test
+    @MediumTest
+    public void testDismissViaSwipeRtl() throws Throwable {
+        onView(withId(R.id.col)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL));
+        if (ViewCompat.getLayoutDirection(mCoordinatorLayout) == ViewCompat.LAYOUT_DIRECTION_RTL) {
+            // On devices that support RTL layout, the start-to-end dismiss swipe is done
+            // with swipeLeft() action
+            verifyDismissCallback(
+                    onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                    swipeLeft(),
+                    null,
+                    Snackbar.LENGTH_LONG,
+                    Snackbar.Callback.DISMISS_EVENT_SWIPE);
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testDismissViaApi() throws Throwable {
+        verifyDismissCallback(
+                onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                null,
+                new DismissAction() {
+                    @Override
+                    public void dismiss(CustomSnackbar snackbar) {
+                        snackbar.dismiss();
+                    }
+                },
+                Snackbar.LENGTH_LONG,
+                Snackbar.Callback.DISMISS_EVENT_MANUAL);
+    }
+
+    @Test
+    @MediumTest
+    public void testDismissViaTimeout() throws Throwable {
+        verifyDismissCallback(
+                onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                null,
+                null,
+                Snackbar.LENGTH_LONG,
+                Snackbar.Callback.DISMISS_EVENT_TIMEOUT);
+    }
+
+    @Test
+    @MediumTest
+    public void testDismissViaAnotherSnackbar() throws Throwable {
+        final CustomSnackbar anotherSnackbar =
+                makeCustomSnackbar().setTitle("Different title")
+                        .setSubtitle("Different subtitle").setDuration(Snackbar.LENGTH_SHORT);
+
+        // Our dismiss action is to show another snackbar (and verify that the original snackbar
+        // is now dismissed with CONSECUTIVE event)
+        verifyDismissCallback(
+                onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
+                null,
+                new DismissAction() {
+                    @Override
+                    public void dismiss(CustomSnackbar snackbar) {
+                        anotherSnackbar.show();
+                    }
+                },
+                Snackbar.LENGTH_LONG,
+                Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE);
+
+        // And dismiss the second snackbar to get back to clean state
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(anotherSnackbar);
+    }
+
+    @Test
+    @MediumTest
+    public void testMultipleCallbacks() throws Throwable {
+        final CustomSnackbar snackbar = makeCustomSnackbar().setTitle(TITLE_TEXT)
+                .setSubtitle(SUBTITLE_TEXT).setDuration(Snackbar.LENGTH_INDEFINITE);
+        final BaseTransientBottomBar.BaseCallback mockCallback1 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        final BaseTransientBottomBar.BaseCallback mockCallback2 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        snackbar.addCallback(mockCallback1);
+        snackbar.addCallback(mockCallback2);
+
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        verify(mockCallback1, times(1)).onShown(snackbar);
+        verify(mockCallback2, times(1)).onShown(snackbar);
+
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+        verify(mockCallback1, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+        verify(mockCallback2, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
index acd42ff..857121e 100644
--- a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
+++ b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
@@ -36,7 +36,7 @@
 import android.graphics.Color;
 import android.support.design.test.R;
 import android.support.design.testutils.TestUtils;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 import android.view.Gravity;
 import android.view.View;
 
diff --git a/design/tests/src/android/support/design/widget/NavigationViewTest.java b/design/tests/src/android/support/design/widget/NavigationViewTest.java
index 91caf0a..1327154 100755
--- a/design/tests/src/android/support/design/widget/NavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/NavigationViewTest.java
@@ -27,6 +27,7 @@
 import static android.support.design.testutils.NavigationViewActions.setItemIconTintList;
 import static android.support.design.testutils.NavigationViewActions.setItemTextAppearance;
 import static android.support.design.testutils.NavigationViewActions.setItemTextColor;
+import static android.support.design.testutils.TestUtilsActions.restoreHierarchyState;
 import static android.support.design.testutils.TestUtilsMatchers.isChildOfA;
 import static android.support.design.testutils.TestUtilsMatchers.withBackgroundFill;
 import static android.support.design.testutils.TestUtilsMatchers.withStartDrawableFilledWith;
@@ -38,9 +39,11 @@
 import static android.support.test.espresso.matcher.ViewMatchers.Visibility;
 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
 import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
 import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
@@ -57,16 +60,18 @@
 
 import android.content.res.Resources;
 import android.os.Build;
+import android.os.Parcelable;
 import android.support.annotation.ColorInt;
 import android.support.annotation.IdRes;
 import android.support.design.test.R;
 import android.support.design.testutils.TestDrawable;
+import android.support.test.filters.SmallTest;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.SwitchCompat;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -400,6 +405,45 @@
 
     @Test
     @SmallTest
+    public void testHeaderState() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        // Inflate a header with a toggle switch and check that it's there in the navigation view
+        onView(withId(R.id.start_drawer)).perform(
+                inflateHeaderView(R.layout.design_navigation_view_header_switch));
+        verifyHeaders(R.id.header_frame);
+
+        onView(withId(R.id.header_toggle))
+                .check(matches(isNotChecked()))
+                .perform(click())
+                .check(matches(isChecked()));
+
+        // Save the current state
+        SparseArray<Parcelable> container = new SparseArray<>();
+        mNavigationView.saveHierarchyState(container);
+
+        // Remove the header
+        final View header = mNavigationView.findViewById(R.id.header_frame);
+        onView(withId(R.id.start_drawer)).perform(removeHeaderView(header));
+        verifyHeaders();
+
+        // Inflate the header again
+        onView(withId(R.id.start_drawer)).perform(
+                inflateHeaderView(R.layout.design_navigation_view_header_switch));
+        verifyHeaders(R.id.header_frame);
+
+        // Restore the saved state
+        onView(withId(R.id.start_drawer)).perform(
+                restoreHierarchyState(container));
+
+        // Confirm that the state was restored
+        onView(withId(R.id.header_toggle))
+                .check(matches(isChecked()));
+    }
+
+    @Test
+    @SmallTest
     public void testNavigationSelectionListener() {
         // Open our drawer
         onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
diff --git a/design/tests/src/android/support/design/widget/SnackbarTest.java b/design/tests/src/android/support/design/widget/SnackbarTest.java
index 8523402..1cd2960 100644
--- a/design/tests/src/android/support/design/widget/SnackbarTest.java
+++ b/design/tests/src/android/support/design/widget/SnackbarTest.java
@@ -16,31 +16,44 @@
 
 package android.support.design.widget;
 
+import static android.support.design.testutils.TestUtilsActions.setLayoutDirection;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.swipeLeft;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.core.AllOf.allOf;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
 import android.content.res.Resources;
-import android.os.SystemClock;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.design.test.R;
 import android.support.design.testutils.SnackbarUtils;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewInteraction;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.view.View;
+
 import org.junit.Before;
 import org.junit.Test;
 
-import static android.support.design.testutils.TestUtilsActions.setLayoutDirection;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.*;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
-import static org.hamcrest.core.AllOf.allOf;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
 public class SnackbarTest extends BaseInstrumentationTestCase<SnackbarActivity> {
     private static final String MESSAGE_TEXT = "Test Message";
     private static final @StringRes int MESSAGE_ID = R.string.snackbar_text;
@@ -64,9 +77,9 @@
     }
 
     private void verifySnackbarContent(final Snackbar snackbar, final String expectedMessage,
-            final String expectedAction) {
+            final String expectedAction) throws Throwable {
         // Show the snackbar
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(snackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
 
         // Verify that we're showing the message
         withText(expectedMessage).matches(allOf(
@@ -81,12 +94,12 @@
         }
 
         // Dismiss the snackbar
-        SnackbarUtils.dismissSnackbarAndWaitUntilFullyDismissed(snackbar);
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
     }
 
     @Test
     @SmallTest
-    public void testBasicContent() {
+    public void testBasicContent() throws Throwable {
         // Verify different combinations of snackbar content (message and action) and duration
 
         final Resources res = mActivityTestRule.getActivity().getResources();
@@ -124,46 +137,52 @@
 
     private void verifyDismissCallback(final ViewInteraction interaction,
             final @Nullable ViewAction action, final @Nullable DismissAction dismissAction,
-            final int length, @Snackbar.Callback.DismissEvent final int expectedEvent) {
+            final int length, @Snackbar.Callback.DismissEvent final int expectedEvent)
+            throws Throwable {
         final Snackbar.Callback mockCallback = mock(Snackbar.Callback.class);
         final Snackbar snackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, length)
                 .setAction(ACTION_TEXT, mock(View.OnClickListener.class))
-                .setCallback(mockCallback);
+                .addCallback(mockCallback);
 
-        // Note that unlike other tests around Snackbar that use Espresso's IdlingResources
-        // to wait until the snackbar is shown (SnackbarUtils.showSnackbarAndWaitUntilFullyShown),
-        // here we want to verify our callback has been called with onShown after snackbar is shown
-        // and with onDismissed after snackbar is dismissed.
-
-        // Now show the Snackbar
-        snackbar.show();
-        // sleep for the animation
-        SystemClock.sleep(Snackbar.ANIMATION_DURATION + 50);
+        // Show the snackbar
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        // Verify that our onShown has been called
+        verify(mockCallback, times(1)).onShown(snackbar);
+        // and that the snackbar is either shown or queued to be shown
+        assertTrue(snackbar.isShownOrQueued());
+        // and also check that we have the intended message / action displayed somewhere in
+        // our hierarchy
+        onView(withText(MESSAGE_TEXT)).check(matches(isCompletelyDisplayed()));
+        onView(withText(ACTION_TEXT)).check(matches(isCompletelyDisplayed()));
 
         // Now perform the UI interaction
-        if (action != null) {
-            interaction.perform(action);
-        } else if (dismissAction != null) {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-                @Override
-                public void run() {
-                    dismissAction.dismiss(snackbar);
-                }
-            });
-        }
-        // wait until the Snackbar has been removed from the view hierarchy
-        while (snackbar.isShownOrQueued()) {
-            SystemClock.sleep(20);
-        }
-        // and verify that our callback was invoked with onShown and onDismissed
-        verify(mockCallback, times(1)).onShown(snackbar);
+        SnackbarUtils.performActionAndWaitUntilFullyDismissed(snackbar,
+                new SnackbarUtils.TransientBottomBarAction() {
+                    @Override
+                    public void perform() throws Throwable {
+                        if (action != null) {
+                            interaction.perform(action);
+                        } else if (dismissAction != null) {
+                            mActivityTestRule.runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    dismissAction.dismiss(snackbar);
+                                }
+                            });
+                        }
+                    }
+                });
+
+        // Verify that our onDismissed has been called
         verify(mockCallback, times(1)).onDismissed(snackbar, expectedEvent);
         verifyNoMoreInteractions(mockCallback);
+        // and that the snackbar is neither shown nor queued to be shown
+        assertFalse(snackbar.isShownOrQueued());
     }
 
     @Test
     @MediumTest
-    public void testDismissViaActionClick() {
+    public void testDismissViaActionClick() throws Throwable {
         verifyDismissCallback(
                 onView(withId(R.id.snackbar_action)),
                 click(),
@@ -174,7 +193,7 @@
 
     @Test
     @MediumTest
-    public void testDismissViaSwipe() {
+    public void testDismissViaSwipe() throws Throwable {
         verifyDismissCallback(
                 onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
                 swipeRight(),
@@ -185,7 +204,7 @@
 
     @Test
     @MediumTest
-    public void testDismissViaSwipeRtl() {
+    public void testDismissViaSwipeRtl() throws Throwable {
         onView(withId(R.id.col)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL));
         if (ViewCompat.getLayoutDirection(mCoordinatorLayout) == ViewCompat.LAYOUT_DIRECTION_RTL) {
             // On devices that support RTL layout, the start-to-end dismiss swipe is done
@@ -201,7 +220,7 @@
 
     @Test
     @MediumTest
-    public void testDismissViaApi() {
+    public void testDismissViaApi() throws Throwable {
         verifyDismissCallback(
                 onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
                 null,
@@ -217,7 +236,7 @@
 
     @Test
     @MediumTest
-    public void testDismissViaTimeout() {
+    public void testDismissViaTimeout() throws Throwable {
         verifyDismissCallback(
                 onView(isAssignableFrom(Snackbar.SnackbarLayout.class)),
                 null,
@@ -228,7 +247,7 @@
 
     @Test
     @MediumTest
-    public void testDismissViaAnotherSnackbar() {
+    public void testDismissViaAnotherSnackbar() throws Throwable {
         final Snackbar anotherSnackbar =
                 Snackbar.make(mCoordinatorLayout, "A different message", Snackbar.LENGTH_SHORT);
 
@@ -247,7 +266,7 @@
                 Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE);
 
         // And dismiss the second snackbar to get back to clean state
-        SnackbarUtils.dismissSnackbarAndWaitUntilFullyDismissed(anotherSnackbar);
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(anotherSnackbar);
     }
 
     @Test
@@ -259,10 +278,101 @@
                     .setAction(ACTION_TEXT, mockClickListener);
 
         // Show the snackbar
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(snackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
         // perform the action click
         onView(withId(R.id.snackbar_action)).perform(click());
         // and verify that our click listener has been called
         verify(mockClickListener, times(1)).onClick(any(View.class));
     }
+
+    @Test
+    @MediumTest
+    public void testSetCallback() throws Throwable {
+        final Snackbar snackbar =
+                Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
+                        .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
+        final BaseTransientBottomBar.BaseCallback mockCallback =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        snackbar.setCallback(mockCallback);
+
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        verify(mockCallback, times(1)).onShown(snackbar);
+
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+        verify(mockCallback, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+    }
+
+    @Test
+    @MediumTest
+    public void testSingleCallback() throws Throwable {
+        final Snackbar snackbar =
+                Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
+                        .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
+        final BaseTransientBottomBar.BaseCallback mockCallback1 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        final BaseTransientBottomBar.BaseCallback mockCallback2 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        snackbar.addCallback(mockCallback1);
+
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        verify(mockCallback1, times(1)).onShown(snackbar);
+        verify(mockCallback2, never()).onShown(snackbar);
+
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+        verify(mockCallback1, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+        verify(mockCallback2, never()).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+    }
+
+    @Test
+    @MediumTest
+    public void testMultipleCallbacks() throws Throwable {
+        final Snackbar snackbar =
+                Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
+                        .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
+        final BaseTransientBottomBar.BaseCallback mockCallback1 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        final BaseTransientBottomBar.BaseCallback mockCallback2 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        snackbar.addCallback(mockCallback1);
+        snackbar.addCallback(mockCallback2);
+
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        verify(mockCallback1, times(1)).onShown(snackbar);
+        verify(mockCallback2, times(1)).onShown(snackbar);
+
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+        verify(mockCallback1, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+        verify(mockCallback2, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+    }
+
+    @Test
+    @MediumTest
+    public void testMultipleCallbacksWithRemoval() throws Throwable {
+        final Snackbar snackbar =
+                Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT, Snackbar.LENGTH_INDEFINITE)
+                        .setAction(ACTION_TEXT, mock(View.OnClickListener.class));
+        final BaseTransientBottomBar.BaseCallback mockCallback1 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        final BaseTransientBottomBar.BaseCallback mockCallback2 =
+                mock(BaseTransientBottomBar.BaseCallback.class);
+        snackbar.addCallback(mockCallback1);
+        snackbar.addCallback(mockCallback2);
+
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
+        verify(mockCallback1, times(1)).onShown(snackbar);
+        verify(mockCallback2, times(1)).onShown(snackbar);
+
+        snackbar.removeCallback(mockCallback2);
+
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
+        verify(mockCallback1, times(1)).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+        verify(mockCallback2, never()).onDismissed(snackbar,
+                BaseTransientBottomBar.BaseCallback.DISMISS_EVENT_MANUAL);
+    }
 }
diff --git a/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java b/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
index 894d126..7d90a2b 100644
--- a/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
+++ b/design/tests/src/android/support/design/widget/SnackbarTestWithFAB.java
@@ -20,7 +20,7 @@
 
 import android.support.design.test.R;
 import android.support.design.testutils.SnackbarUtils;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 
 import org.junit.Before;
@@ -52,7 +52,7 @@
         // Show a simple Snackbar and wait for it to be shown
         final Snackbar snackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT,
                 Snackbar.LENGTH_SHORT);
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(snackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
 
         // Now check that the FAB has shifted up to make space for the Snackbar
         final int[] fabPosition = new int[2];
@@ -63,14 +63,14 @@
         // Now wait until the Snackbar has been dismissed
         SnackbarUtils.waitUntilFullyDismissed(snackbar);
 
-        // And check that the FAB is back in it's original position
+        // And check that the FAB is back in its original position
         fab.getLocationOnScreen(fabPosition);
         assertEquals(originalFabPosition[0], fabPosition[0]);
         assertEquals(originalFabPosition[1], fabPosition[1]);
     }
 
     @Test
-    public void testIndefiniteSnackbarDodgesFab() {
+    public void testIndefiniteSnackbarDodgesFab() throws Throwable {
         final int[] originalFabPosition = new int[2];
         final View fab = mCoordinatorLayout.findViewById(R.id.fab);
         fab.getLocationOnScreen(originalFabPosition);
@@ -78,7 +78,7 @@
         // Show a simple Snackbar and wait for it to be shown
         final Snackbar snackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT,
                 Snackbar.LENGTH_INDEFINITE);
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(snackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
 
         // Now check that the FAB has shifted up to make space for the Snackbar
         final int[] fabPosition = new int[2];
@@ -87,9 +87,9 @@
         assertEquals(originalFabPosition[1] - snackbar.getView().getHeight(), fabPosition[1]);
 
         // Now dismiss the Snackbar and wait for it to be dismissed
-        SnackbarUtils.dismissSnackbarAndWaitUntilFullyDismissed(snackbar);
+        SnackbarUtils.dismissTransientBottomBarAndWaitUntilFullyDismissed(snackbar);
 
-        // And check that the FAB is back in it's original position
+        // And check that the FAB is back in its original position
         fab.getLocationOnScreen(fabPosition);
         assertEquals(originalFabPosition[0], fabPosition[0]);
         assertEquals(originalFabPosition[1], fabPosition[1]);
diff --git a/design/tests/src/android/support/design/widget/SnackbarTestWithTranslucentNavBar.java b/design/tests/src/android/support/design/widget/SnackbarTestWithTranslucentNavBar.java
index 83eee70..07a8d0f 100644
--- a/design/tests/src/android/support/design/widget/SnackbarTestWithTranslucentNavBar.java
+++ b/design/tests/src/android/support/design/widget/SnackbarTestWithTranslucentNavBar.java
@@ -21,9 +21,9 @@
 
 import android.support.design.test.R;
 import android.support.design.testutils.SnackbarUtils;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.WindowInsetsCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
 import org.junit.Before;
@@ -53,7 +53,7 @@
         // Show a simple Snackbar and wait for it to be shown
         final Snackbar snackbar = Snackbar.make(mCoordinatorLayout, MESSAGE_TEXT,
                 Snackbar.LENGTH_SHORT);
-        SnackbarUtils.showSnackbarAndWaitUntilFullyShown(snackbar);
+        SnackbarUtils.showTransientBottomBarAndWaitUntilFullyShown(snackbar);
 
         final WindowInsetsCompat colLastInsets = mCoordinatorLayout.getLastWindowInsets();
         assertNotNull(colLastInsets);
diff --git a/design/tests/src/android/support/design/widget/TabLayoutPoolingTest.java b/design/tests/src/android/support/design/widget/TabLayoutPoolingTest.java
index 09601db..a805a2e 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutPoolingTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutPoolingTest.java
@@ -20,8 +20,8 @@
 
 import android.app.Activity;
 import android.support.design.test.R;
-import android.support.test.InstrumentationRegistry;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Test;
 
@@ -31,29 +31,25 @@
         super(TabLayoutPoolingActivity.class);
     }
 
+    @UiThreadTest
     @SmallTest
     @Test
     public void testUsingTabsFromOtherInstance() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final Activity activity = mActivityTestRule.getActivity();
+        final Activity activity = mActivityTestRule.getActivity();
 
-                // TabLayout1 has items added via the layout, so we'll just check they're
-                // there first
-                final TabLayout tabLayout1 = (TabLayout) activity.findViewById(R.id.tabs_1);
-                assertTrue(tabLayout1.getTabCount() > 0);
+        // TabLayout1 has items added via the layout, so we'll just check they're
+        // there first
+        final TabLayout tabLayout1 = (TabLayout) activity.findViewById(R.id.tabs_1);
+        assertTrue(tabLayout1.getTabCount() > 0);
 
-                // Now remove all tabs. TabLayout will pool the Tab instances...
-                tabLayout1.removeAllTabs();
+        // Now remove all tabs. TabLayout will pool the Tab instances...
+        tabLayout1.removeAllTabs();
 
-                // Now add some tabs to the second TabLayout and make sure that we don't crash
-                final TabLayout tabLayout2 = (TabLayout) activity.findViewById(R.id.tabs_2);
-                tabLayout2.addTab(tabLayout2.newTab());
-                tabLayout2.addTab(tabLayout2.newTab());
-                tabLayout2.addTab(tabLayout2.newTab());
-            }
-        });
+        // Now add some tabs to the second TabLayout and make sure that we don't crash
+        final TabLayout tabLayout2 = (TabLayout) activity.findViewById(R.id.tabs_2);
+        tabLayout2.addTab(tabLayout2.newTab());
+        tabLayout2.addTab(tabLayout2.newTab());
+        tabLayout2.addTab(tabLayout2.newTab());
     }
 
 }
diff --git a/design/tests/src/android/support/design/widget/TabLayoutTest.java b/design/tests/src/android/support/design/widget/TabLayoutTest.java
index 69bf429..539ab86 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutTest.java
@@ -29,8 +29,8 @@
 
 import android.support.design.test.R;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
 import android.support.v7.app.AppCompatActivity;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.InflateException;
 import android.view.LayoutInflater;
 import android.view.View;
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
index eb942f6..2e75fb7 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
@@ -15,6 +15,24 @@
  */
 package android.support.design.widget;
 
+import static android.support.design.testutils.TabLayoutActions.setupWithViewPager;
+import static android.support.design.testutils.ViewPagerActions.notifyAdapterContentChange;
+import static android.support.design.testutils.ViewPagerActions.setAdapter;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withParent;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.support.annotation.DimenRes;
@@ -24,32 +42,22 @@
 import android.support.design.testutils.TestUtilsActions;
 import android.support.design.testutils.TestUtilsMatchers;
 import android.support.design.testutils.ViewPagerActions;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.HorizontalScrollView;
 import android.widget.TextView;
+
 import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
 
-import static android.support.design.testutils.TabLayoutActions.setupWithViewPager;
-import static android.support.design.testutils.ViewPagerActions.notifyAdapterContentChange;
-import static android.support.design.testutils.ViewPagerActions.setAdapter;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
-import static org.hamcrest.Matchers.allOf;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
 public class TabLayoutWithViewPagerTest
         extends BaseInstrumentationTestCase<TabLayoutWithViewPagerActivity> {
     private TabLayout mTabLayout;
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 66ea00b..5125f5d 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -28,6 +28,7 @@
 import static android.support.test.espresso.action.ViewActions.typeText;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -39,14 +40,18 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
+import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
 import android.support.design.test.R;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.util.AttributeSet;
+import android.util.SparseArray;
 import android.view.View;
 import android.widget.EditText;
 
@@ -60,6 +65,30 @@
 
     private static final String INPUT_TEXT = "Random input text";
 
+    public class TestTextInputLayout extends TextInputLayout {
+        public int animateToExpansionFractionCount = 0;
+        public float animateToExpansionFractionRecentValue = -1;
+
+        public TestTextInputLayout(Context context) {
+            super(context);
+        }
+
+        public TestTextInputLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public TestTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+            super(context, attrs, defStyleAttr);
+        }
+
+        @Override
+        protected void animateToExpansionFraction(float target) {
+            super.animateToExpansionFraction(target);
+            animateToExpansionFractionRecentValue = target;
+            animateToExpansionFractionCount++;
+        }
+    }
+
     public TextInputLayoutTest() {
         super(TextInputLayoutActivity.class);
     }
@@ -193,6 +222,25 @@
     }
 
     @Test
+    public void testPasswordToggleIsHiddenAfterReenable() {
+        final Activity activity = mActivityTestRule.getActivity();
+        final EditText textInput = (EditText) activity.findViewById(R.id.textinput_edittext_pwd);
+
+        // Type some text on the EditText and then click the toggle button
+        onView(withId(R.id.textinput_edittext_pwd)).perform(typeText(INPUT_TEXT));
+        onView(withId(R.id.text_input_password_toggle)).perform(click());
+
+        // Disable the password toggle, and then re-enable it
+        onView(withId(R.id.textinput_password))
+                .perform(setPasswordVisibilityToggleEnabled(false))
+                .perform(setPasswordVisibilityToggleEnabled(true));
+
+        // Check that the password is disguised and the toggle button reflects the same state
+        assertNotEquals(INPUT_TEXT, textInput.getLayout().getText().toString());
+        onView(withId(R.id.text_input_password_toggle)).check(matches(not(isChecked())));
+    }
+
+    @Test
     public void testSetEnabledFalse() {
         // First click on the EditText, so that it is focused and the hint collapses...
         onView(withId(R.id.textinput_edittext)).perform(click());
@@ -230,6 +278,35 @@
         layout.drawableStateChanged();
     }
 
+    @UiThreadTest
+    @Test
+    public void testSaveRestoreStateAnimation() {
+        final Activity activity = mActivityTestRule.getActivity();
+        final TestTextInputLayout layout = new TestTextInputLayout(activity);
+        layout.setId(R.id.textinputlayout);
+        final TextInputEditText editText = new TextInputEditText(activity);
+        editText.setText(INPUT_TEXT);
+        editText.setId(R.id.textinputedittext);
+        layout.addView(editText);
+
+        SparseArray<Parcelable> container = new SparseArray<>();
+        layout.saveHierarchyState(container);
+        layout.restoreHierarchyState(container);
+        assertEquals("Expected no animations since we simply saved/restored state",
+                0, layout.animateToExpansionFractionCount);
+
+        editText.setText("");
+        assertEquals("Expected one call to animate because we cleared text in editText",
+                1, layout.animateToExpansionFractionCount);
+        assertEquals(0f, layout.animateToExpansionFractionRecentValue, 0f);
+
+        container = new SparseArray<>();
+        layout.saveHierarchyState(container);
+        layout.restoreHierarchyState(container);
+        assertEquals("Expected no additional animations since we simply saved/restored state",
+                1, layout.animateToExpansionFractionCount);
+    }
+
     static ViewAssertion isHintExpanded(final boolean expanded) {
         return new ViewAssertion() {
             @Override
diff --git a/documents-archive/Android.mk b/documents-archive/Android.mk
deleted file mode 100644
index 32ec7d6..0000000
--- a/documents-archive/Android.mk
+++ /dev/null
@@ -1,39 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Here is the final static library that apps can link against.
-# Applications that use this library must specify
-#
-#   LOCAL_STATIC_ANDROID_LIBRARIES := \
-#       android-support-documents-archive \
-#       android-support-v4 \
-#       android-support-annotations
-#
-# in their makefiles to include the resources and their dependencies in their package.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-documents-archive
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations \
-    android-support-v4
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/documents-archive/src/android/support/provider/DocumentArchive.java b/documents-archive/src/android/support/provider/DocumentArchive.java
deleted file mode 100644
index 91cd5e7..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchive.java
+++ /dev/null
@@ -1,538 +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.provider;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Point;
-import android.media.ExifInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.lang.IllegalArgumentException;
-import java.lang.IllegalStateException;
-import java.lang.UnsupportedOperationException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Stack;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider. The id delimiter
- * must be a character which is not used in document ids generated by the
- * document provider.
- *
- * <p>This class is thread safe.
- *
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public class DocumentArchive implements Closeable {
-    private static final String TAG = "DocumentArchive";
-
-    private static final String[] DEFAULT_PROJECTION = new String[] {
-            Document.COLUMN_DOCUMENT_ID,
-            Document.COLUMN_DISPLAY_NAME,
-            Document.COLUMN_MIME_TYPE,
-            Document.COLUMN_SIZE,
-            Document.COLUMN_FLAGS
-    };
-
-    private final Context mContext;
-    private final String mDocumentId;
-    private final char mIdDelimiter;
-    private final Uri mNotificationUri;
-    private final ZipFile mZipFile;
-    private final ExecutorService mExecutor;
-    private final Map<String, ZipEntry> mEntries;
-    private final Map<String, List<ZipEntry>> mTree;
-
-    private DocumentArchive(
-            Context context,
-            File file,
-            String documentId,
-            char idDelimiter,
-            @Nullable Uri notificationUri)
-            throws IOException {
-        mContext = context;
-        mDocumentId = documentId;
-        mIdDelimiter = idDelimiter;
-        mNotificationUri = notificationUri;
-        mZipFile = new ZipFile(file);
-        mExecutor = Executors.newSingleThreadExecutor();
-
-        // Build the tree structure in memory.
-        mTree = new HashMap<String, List<ZipEntry>>();
-        mTree.put("/", new ArrayList<ZipEntry>());
-
-        mEntries = new HashMap<String, ZipEntry>();
-        ZipEntry entry;
-        final List<? extends ZipEntry> entries = Collections.list(mZipFile.entries());
-        final Stack<ZipEntry> stack = new Stack<>();
-        for (int i = entries.size() - 1; i >= 0; i--) {
-            entry = entries.get(i);
-            if (entry.isDirectory() != entry.getName().endsWith("/")) {
-                throw new IOException(
-                        "Directories must have a trailing slash, and files must not.");
-            }
-            if (mEntries.containsKey(entry.getName())) {
-                throw new IOException("Multiple entries with the same name are not supported.");
-            }
-            mEntries.put(entry.getName(), entry);
-            if (entry.isDirectory()) {
-                mTree.put(entry.getName(), new ArrayList<ZipEntry>());
-            }
-            stack.push(entry);
-        }
-
-        int delimiterIndex;
-        String parentPath;
-        ZipEntry parentEntry;
-        List<ZipEntry> parentList;
-
-        while (stack.size() > 0) {
-            entry = stack.pop();
-
-            delimiterIndex = entry.getName().lastIndexOf('/', entry.isDirectory()
-                    ? entry.getName().length() - 2 : entry.getName().length() - 1);
-            parentPath =
-                    delimiterIndex != -1 ? entry.getName().substring(0, delimiterIndex) + "/" : "/";
-            parentList = mTree.get(parentPath);
-
-            if (parentList == null) {
-                parentEntry = mEntries.get(parentPath);
-                if (parentEntry == null) {
-                    // The ZIP file doesn't contain all directories leading to the entry.
-                    // It's rare, but can happen in a valid ZIP archive. In such case create a
-                    // fake ZipEntry and add it on top of the stack to process it next.
-                    parentEntry = new ZipEntry(parentPath);
-                    parentEntry.setSize(0);
-                    parentEntry.setTime(entry.getTime());
-                    mEntries.put(parentPath, parentEntry);
-                    stack.push(parentEntry);
-                }
-                parentList = new ArrayList<ZipEntry>();
-                mTree.put(parentPath, parentList);
-            }
-
-            parentList.add(entry);
-        }
-    }
-
-    /**
-     * Creates a DocumentsArchive instance for opening, browsing and accessing
-     * documents within the archive passed as a local file.
-     *
-     * @param context Context of the provider.
-     * @param File Local file containing the archive.
-     * @param documentId ID of the archive document.
-     * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
-     *            The delimiter must never be used for IDs of other documents.
-     * @param Uri notificationUri Uri for notifying that the archive file has changed.
-     * @see createForParcelFileDescriptor(DocumentsProvider, ParcelFileDescriptor, String, char,
-     *          Uri)
-     */
-    public static DocumentArchive createForLocalFile(
-            Context context, File file, String documentId, char idDelimiter,
-            @Nullable Uri notificationUri)
-            throws IOException {
-        return new DocumentArchive(context, file, documentId, idDelimiter, notificationUri);
-    }
-
-    /**
-     * Creates a DocumentsArchive instance for opening, browsing and accessing
-     * documents within the archive passed as a file descriptor.
-     *
-     * <p>Note, that this method should be used only if the document does not exist
-     * on the local storage. A snapshot file will be created, which may be slower
-     * and consume significant resources, in contrast to using
-     * {@see createForLocalFile(Context, File, String, char, Uri}.
-     *
-     * @param context Context of the provider.
-     * @param descriptor File descriptor for the archive's contents.
-     * @param documentId ID of the archive document.
-     * @param idDelimiter Delimiter for constructing IDs of documents within the archive.
-     *            The delimiter must never be used for IDs of other documents.
-     * @param Uri notificationUri Uri for notifying that the archive file has changed.
-     * @see createForLocalFile(Context, File, String, char, Uri)
-     */
-    public static DocumentArchive createForParcelFileDescriptor(
-            Context context, ParcelFileDescriptor descriptor, String documentId,
-            char idDelimiter, @Nullable Uri notificationUri)
-            throws IOException {
-        File snapshotFile = null;
-        try {
-            // Create a copy of the archive, as ZipFile doesn't operate on streams.
-            // Moreover, ZipInputStream would be inefficient for large files on
-            // pipes.
-            snapshotFile = File.createTempFile("android.support.provider.snapshot{",
-                    "}.zip", context.getCacheDir());
-
-            try (
-                final FileOutputStream outputStream =
-                        new ParcelFileDescriptor.AutoCloseOutputStream(
-                                ParcelFileDescriptor.open(
-                                        snapshotFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
-                final ParcelFileDescriptor.AutoCloseInputStream inputStream =
-                        new ParcelFileDescriptor.AutoCloseInputStream(descriptor);
-            ) {
-                final byte[] buffer = new byte[32 * 1024];
-                int bytes;
-                while ((bytes = inputStream.read(buffer)) != -1) {
-                    outputStream.write(buffer, 0, bytes);
-                }
-                outputStream.flush();
-                return new DocumentArchive(context, snapshotFile, documentId, idDelimiter,
-                        notificationUri);
-            }
-        } finally {
-            // On UNIX the file will be still available for processes which opened it, even
-            // after deleting it. Remove it ASAP, as it won't be used by anyone else.
-            if (snapshotFile != null) {
-                snapshotFile.delete();
-            }
-        }
-    }
-
-    /**
-     * Lists child documents of an archive or a directory within an
-     * archive. Must be called only for archives with supported mime type,
-     * or for documents within archives.
-     *
-     * @see DocumentsProvider.queryChildDocuments(String, String[], String)
-     */
-    public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
-            @Nullable String sortOrder) throws FileNotFoundException {
-        final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-
-        final String parentPath = parsedParentId.mPath != null ? parsedParentId.mPath : "/";
-        final MatrixCursor result = new MatrixCursor(
-                projection != null ? projection : DEFAULT_PROJECTION);
-        if (mNotificationUri != null) {
-            result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
-        }
-
-        final List<ZipEntry> parentList = mTree.get(parentPath);
-        if (parentList == null) {
-            throw new FileNotFoundException();
-        }
-        for (final ZipEntry entry : parentList) {
-            addCursorRow(result, entry);
-        }
-        return result;
-    }
-
-    /**
-     * Returns a MIME type of a document within an archive.
-     *
-     * @see DocumentsProvider.getDocumentType(String)
-     */
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-        return getMimeTypeForEntry(entry);
-    }
-
-    /**
-     * Returns true if a document within an archive is a child or any descendant of the archive
-     * document or another document within the archive.
-     *
-     * @see DocumentsProvider.isChildDocument(String, String)
-     */
-    public boolean isChildDocument(String parentDocumentId, String documentId) {
-        final ParsedDocumentId parsedParentId = ParsedDocumentId.fromDocumentId(
-                parentDocumentId, mIdDelimiter);
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedParentId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath,
-                "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            return false;
-        }
-
-        if (parsedParentId.mPath == null) {
-            // No need to compare paths. Every file in the archive is a child of the archive
-            // file.
-            return true;
-        }
-
-        final ZipEntry parentEntry = mEntries.get(parsedParentId.mPath);
-        if (parentEntry == null || !parentEntry.isDirectory()) {
-            return false;
-        }
-
-        final String parentPath = entry.getName();
-
-        // Add a trailing slash even if it's not a directory, so it's easy to check if the
-        // entry is a descendant.
-        final String pathWithSlash = entry.isDirectory() ? entry.getName() : entry.getName() + "/";
-        return pathWithSlash.startsWith(parentPath) && !parentPath.equals(pathWithSlash);
-    }
-
-    /**
-     * Returns metadata of a document within an archive.
-     *
-     * @see DocumentsProvider.queryDocument(String, String[])
-     */
-    public Cursor queryDocument(String documentId, @Nullable String[] projection)
-            throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        final MatrixCursor result = new MatrixCursor(
-                projection != null ? projection : DEFAULT_PROJECTION);
-        if (mNotificationUri != null) {
-            result.setNotificationUri(mContext.getContentResolver(), mNotificationUri);
-        }
-        addCursorRow(result, entry);
-        return result;
-    }
-
-    /**
-     * Opens a file within an archive.
-     *
-     * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
-     */
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, @Nullable final CancellationSignal signal)
-            throws FileNotFoundException {
-        Preconditions.checkArgumentEquals("r", mode,
-                "Invalid mode. Only reading \"r\" supported, but got: \"%s\".");
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(
-                documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        ParcelFileDescriptor[] pipe;
-        InputStream inputStream = null;
-        try {
-            pipe = ParcelFileDescriptor.createReliablePipe();
-            inputStream = mZipFile.getInputStream(entry);
-        } catch (IOException e) {
-            if (inputStream != null) {
-                IoUtils.closeQuietly(inputStream);
-            }
-            // Ideally we'd simply throw IOException to the caller, but for consistency
-            // with DocumentsProvider::openDocument, converting it to IllegalStateException.
-            throw new IllegalStateException("Failed to open the document.", e);
-        }
-        final ParcelFileDescriptor outputPipe = pipe[1];
-        final InputStream finalInputStream = inputStream;
-        mExecutor.execute(
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        try (final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
-                                new ParcelFileDescriptor.AutoCloseOutputStream(outputPipe)) {
-                            try {
-                                final byte buffer[] = new byte[32 * 1024];
-                                int bytes;
-                                while ((bytes = finalInputStream.read(buffer)) != -1) {
-                                    if (Thread.interrupted()) {
-                                        throw new InterruptedException();
-                                    }
-                                    if (signal != null) {
-                                        signal.throwIfCanceled();
-                                    }
-                                    outputStream.write(buffer, 0, bytes);
-                                }
-                            } catch (IOException | InterruptedException e) {
-                                // Catch the exception before the outer try-with-resource closes the
-                                // pipe with close() instead of closeWithError().
-                                try {
-                                    outputPipe.closeWithError(e.getMessage());
-                                } catch (IOException e2) {
-                                    Log.e(TAG, "Failed to close the pipe after an error.", e2);
-                                }
-                            }
-                        } catch (OperationCanceledException e) {
-                            // Cancelled gracefully.
-                        } catch (IOException e) {
-                            Log.e(TAG, "Failed to close the output stream gracefully.", e);
-                        } finally {
-                            IoUtils.closeQuietly(finalInputStream);
-                        }
-                    }
-                });
-
-        return pipe[0];
-    }
-
-    /**
-     * Opens a thumbnail of a file within an archive.
-     *
-     * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
-     */
-    public AssetFileDescriptor openDocumentThumbnail(
-            String documentId, Point sizeHint, final CancellationSignal signal)
-            throws FileNotFoundException {
-        final ParsedDocumentId parsedId = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
-        Preconditions.checkArgumentEquals(mDocumentId, parsedId.mArchiveId,
-                "Mismatching document ID. Expected: %s, actual: %s.");
-        Preconditions.checkArgumentNotNull(parsedId.mPath, "Not a document within an archive.");
-        Preconditions.checkArgument(getDocumentType(documentId).startsWith("image/"),
-                "Thumbnails only supported for image/* MIME type.");
-
-        final ZipEntry entry = mEntries.get(parsedId.mPath);
-        if (entry == null) {
-            throw new FileNotFoundException();
-        }
-
-        InputStream inputStream = null;
-        try {
-            inputStream = mZipFile.getInputStream(entry);
-            final ExifInterface exif = new ExifInterface(inputStream);
-            if (exif.hasThumbnail()) {
-                Bundle extras = null;
-                switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
-                    case ExifInterface.ORIENTATION_ROTATE_90:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 90);
-                        break;
-                    case ExifInterface.ORIENTATION_ROTATE_180:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 180);
-                        break;
-                    case ExifInterface.ORIENTATION_ROTATE_270:
-                        extras = new Bundle(1);
-                        extras.putInt(DocumentsContract.EXTRA_ORIENTATION, 270);
-                        break;
-                }
-                final long[] range = exif.getThumbnailRange();
-                return new AssetFileDescriptor(
-                        openDocument(documentId, "r", signal), range[0], range[1], extras);
-            }
-        } catch (IOException e) {
-            // Ignore the exception, as reading the EXIF may legally fail.
-            Log.e(TAG, "Failed to obtain thumbnail from EXIF.", e);
-        } finally {
-            IoUtils.closeQuietly(inputStream);
-        }
-
-        return new AssetFileDescriptor(
-                openDocument(documentId, "r", signal), 0, entry.getSize(), null);
-    }
-
-    /**
-     * Schedules a gracefully close of the archive after any opened files are closed.
-     *
-     * <p>This method does not block until shutdown. Once called, other methods should not be
-     * called.
-     */
-    @Override
-    public void close() {
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                IoUtils.closeQuietly(mZipFile);
-            }
-        });
-        mExecutor.shutdown();
-    }
-
-    private void addCursorRow(MatrixCursor cursor, ZipEntry entry) {
-        final MatrixCursor.RowBuilder row = cursor.newRow();
-        final ParsedDocumentId parsedId = new ParsedDocumentId(mDocumentId, entry.getName());
-        row.add(Document.COLUMN_DOCUMENT_ID, parsedId.toDocumentId(mIdDelimiter));
-
-        final File file = new File(entry.getName());
-        row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
-        row.add(Document.COLUMN_SIZE, entry.getSize());
-
-        final String mimeType = getMimeTypeForEntry(entry);
-        row.add(Document.COLUMN_MIME_TYPE, mimeType);
-
-        final int flags = mimeType.startsWith("image/") ? Document.FLAG_SUPPORTS_THUMBNAIL : 0;
-        row.add(Document.COLUMN_FLAGS, flags);
-    }
-
-    private String getMimeTypeForEntry(ZipEntry entry) {
-        if (entry.isDirectory()) {
-            return Document.MIME_TYPE_DIR;
-        }
-
-        final int lastDot = entry.getName().lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = entry.getName().substring(lastDot + 1).toLowerCase(Locale.US);
-            final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mimeType != null) {
-                return mimeType;
-            }
-        }
-
-        return "application/octet-stream";
-    }
-};
diff --git a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java b/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
deleted file mode 100644
index fd5ff94..0000000
--- a/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
+++ /dev/null
@@ -1,373 +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.provider;
-
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsProvider;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.util.LruCache;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Provides basic implementation for creating, extracting and accessing
- * files within archives exposed by a document provider.
- *
- * <p>This class is thread safe. All methods can be called on any thread without
- * synchronization.
- *
- * TODO: Update the documentation. b/26047732
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public class DocumentArchiveHelper implements Closeable {
-    /**
-     * Cursor column to be used for passing the local file path for documents.
-     * If it's not specified, then a snapshot will be created, which is slower
-     * and consumes more resources.
-     *
-     * <p>Type: STRING
-     */
-    public static final String COLUMN_LOCAL_FILE_PATH = "local_file_path";
-
-    private static final String TAG = "DocumentArchiveHelper";
-    private static final int OPENED_ARCHIVES_CACHE_SIZE = 4;
-    private static final String[] ZIP_MIME_TYPES = {
-            "application/zip", "application/x-zip", "application/x-zip-compressed"
-    };
-
-    private final DocumentsProvider mProvider;
-    private final char mIdDelimiter;
-
-    // @GuardedBy("mArchives")
-    private final LruCache<String, Loader> mArchives =
-            new LruCache<String, Loader>(OPENED_ARCHIVES_CACHE_SIZE) {
-                @Override
-                public void entryRemoved(boolean evicted, String key,
-                        Loader oldValue, Loader newValue) {
-                    oldValue.getWriteLock().lock();
-                    try {
-                        oldValue.get().close();
-                    } catch (FileNotFoundException e) {
-                        Log.e(TAG, "Failed to close an archive as it no longer exists.");
-                    } finally {
-                        oldValue.getWriteLock().unlock();
-                    }
-                }
-            };
-
-    /**
-     * Creates a helper for handling archived documents.
-     *
-     * @param provider Instance of a documents provider which provides archived documents.
-     * @param idDelimiter A character used to create document IDs within archives. Can be any
-     *            character which is not used in any other document ID. If your provider uses
-     *            numbers as document IDs, the delimiter can be eg. a colon. However if your
-     *            provider uses paths, then a delimiter can be any character not allowed in the
-     *            path, which is often \0.
-     */
-    public DocumentArchiveHelper(DocumentsProvider provider, char idDelimiter) {
-        mProvider = provider;
-        mIdDelimiter = idDelimiter;
-    }
-
-    /**
-     * Lists child documents of an archive or a directory within an
-     * archive. Must be called only for archives with supported mime type,
-     * or for documents within archives.
-     *
-     * @see DocumentsProvider.queryChildDocuments(String, String[], String)
-     */
-    public Cursor queryChildDocuments(String documentId, @Nullable String[] projection,
-            @Nullable String sortOrder)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().queryChildDocuments(documentId, projection, sortOrder);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns a MIME type of a document within an archive.
-     *
-     * @see DocumentsProvider.getDocumentType(String)
-     */
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().getDocumentType(documentId);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns true if a document within an archive is a child or any descendant of the archive
-     * document or another document within the archive.
-     *
-     * @see DocumentsProvider.isChildDocument(String, String)
-     */
-    public boolean isChildDocument(String parentDocumentId, String documentId) {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().isChildDocument(parentDocumentId, documentId);
-        } catch (FileNotFoundException e) {
-            throw new IllegalStateException(e);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns metadata of a document within an archive.
-     *
-     * @see DocumentsProvider.queryDocument(String, String[])
-     */
-    public Cursor queryDocument(String documentId, @Nullable String[] projection)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().queryDocument(documentId, projection);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Opens a file within an archive.
-     *
-     * @see DocumentsProvider.openDocument(String, String, CancellationSignal))
-     */
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, final CancellationSignal signal)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().openDocument(documentId, mode, signal);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Opens a thumbnail of a file within an archive.
-     *
-     * @see DocumentsProvider.openDocumentThumbnail(String, Point, CancellationSignal))
-     */
-    public AssetFileDescriptor openDocumentThumbnail(
-            String documentId, Point sizeHint, final CancellationSignal signal)
-            throws FileNotFoundException {
-        Loader loader = null;
-        try {
-            loader = obtainInstance(documentId);
-            return loader.get().openDocumentThumbnail(documentId, sizeHint, signal);
-        } finally {
-            releaseInstance(loader);
-        }
-    }
-
-    /**
-     * Returns true if the passed document ID is for a document within an archive.
-     */
-    public boolean isArchivedDocument(String documentId) {
-        return ParsedDocumentId.hasPath(documentId, mIdDelimiter);
-    }
-
-    /**
-     * Returns true if the passed mime type is supported by the helper.
-     */
-    public boolean isSupportedArchiveType(String mimeType) {
-        for (final String zipMimeType : ZIP_MIME_TYPES) {
-            if (zipMimeType.equals(mimeType)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Closes the helper and disposes all existing archives. It will block until all ongoing
-     * operations on each opened archive are finished.
-     */
-    @Override
-    public void close() {
-        synchronized (mArchives) {
-            mArchives.evictAll();
-        }
-    }
-
-    /**
-     * Releases resources for an archive with the specified document ID. It will block until all
-     * operations on the archive are finished. If not opened, the method does nothing.
-     *
-     * <p>Calling this method is optional. The helper automatically closes the least recently used
-     * archives if too many archives are opened.
-     *
-     * @param archiveDocumentId ID of the archive file.
-     */
-    public void closeArchive(String documentId) {
-        synchronized (mArchives) {
-            mArchives.remove(documentId);
-        }
-    }
-
-    private Loader obtainInstance(String documentId) throws FileNotFoundException {
-        Loader loader;
-        synchronized (mArchives) {
-            loader = getInstanceUncheckedLocked(documentId);
-            loader.getReadLock().lock();
-        }
-        return loader;
-    }
-
-    private void releaseInstance(@Nullable Loader loader) {
-        if (loader != null) {
-            loader.getReadLock().unlock();
-        }
-    }
-
-    private Loader getInstanceUncheckedLocked(String documentId)
-            throws FileNotFoundException {
-        try {
-            final ParsedDocumentId id = ParsedDocumentId.fromDocumentId(documentId, mIdDelimiter);
-            if (mArchives.get(id.mArchiveId) != null) {
-                return mArchives.get(id.mArchiveId);
-            }
-
-            final Cursor cursor = mProvider.queryDocument(id.mArchiveId, new String[]
-                    { Document.COLUMN_MIME_TYPE, COLUMN_LOCAL_FILE_PATH });
-            cursor.moveToFirst();
-            final String mimeType = cursor.getString(cursor.getColumnIndex(
-                    Document.COLUMN_MIME_TYPE));
-            Preconditions.checkArgument(isSupportedArchiveType(mimeType),
-                    "Unsupported archive type.");
-            final int columnIndex = cursor.getColumnIndex(COLUMN_LOCAL_FILE_PATH);
-            final String localFilePath = columnIndex != -1 ? cursor.getString(columnIndex) : null;
-            final File localFile = localFilePath != null ? new File(localFilePath) : null;
-            final Uri notificationUri = cursor.getNotificationUri();
-            final Loader loader = new Loader(mProvider, localFile, id, mIdDelimiter,
-                    notificationUri);
-
-            // Remove the instance from mArchives collection once the archive file changes.
-            if (notificationUri != null) {
-                final LruCache<String, Loader> finalArchives = mArchives;
-                mProvider.getContext().getContentResolver().registerContentObserver(notificationUri,
-                        false,
-                        new ContentObserver(null) {
-                            @Override
-                            public void onChange(boolean selfChange, Uri uri) {
-                                synchronized (mArchives) {
-                                    final Loader currentLoader = mArchives.get(id.mArchiveId);
-                                    if (currentLoader == loader) {
-                                        mArchives.remove(id.mArchiveId);
-                                    }
-                                }
-                            }
-                        });
-            }
-
-            mArchives.put(id.mArchiveId, loader);
-            return loader;
-        } catch (IOException e) {
-            // DocumentsProvider doesn't use IOException. For consistency convert it to
-            // IllegalStateException.
-            throw new IllegalStateException(e);
-        }
-    }
-
-    /**
-     * Loads an instance of DocumentArchive lazily.
-     */
-    private static final class Loader {
-        private final DocumentsProvider mProvider;
-        private final File mLocalFile;
-        private final ParsedDocumentId mId;
-        private final char mIdDelimiter;
-        private final Uri mNotificationUri;
-        private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
-        private DocumentArchive mArchive = null;
-
-        Loader(DocumentsProvider provider, @Nullable File localFile, ParsedDocumentId id,
-                char idDelimiter, Uri notificationUri) {
-            this.mProvider = provider;
-            this.mLocalFile = localFile;
-            this.mId = id;
-            this.mIdDelimiter = idDelimiter;
-            this.mNotificationUri = notificationUri;
-        }
-
-        synchronized DocumentArchive get() throws FileNotFoundException {
-            if (mArchive != null) {
-                return mArchive;
-            }
-
-            try {
-                if (mLocalFile != null) {
-                    mArchive = DocumentArchive.createForLocalFile(
-                            mProvider.getContext(), mLocalFile, mId.mArchiveId, mIdDelimiter,
-                            mNotificationUri);
-                } else {
-                    mArchive = DocumentArchive.createForParcelFileDescriptor(
-                            mProvider.getContext(),
-                            mProvider.openDocument(mId.mArchiveId, "r", null /* signal */),
-                            mId.mArchiveId, mIdDelimiter, mNotificationUri);
-                }
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
-            }
-
-            return mArchive;
-        }
-
-        Lock getReadLock() {
-            return mLock.readLock();
-        }
-
-        Lock getWriteLock() {
-            return mLock.writeLock();
-        }
-    }
-}
diff --git a/documents-archive/src/android/support/provider/IoUtils.java b/documents-archive/src/android/support/provider/IoUtils.java
deleted file mode 100644
index 4806575..0000000
--- a/documents-archive/src/android/support/provider/IoUtils.java
+++ /dev/null
@@ -1,57 +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.provider;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-import java.io.Closeable;
-import java.io.InputStream;
-import java.util.Collection;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Simple static methods to perform common IO operations.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-final class IoUtils {
-    static void closeQuietly(@Nullable Closeable closeable) {
-       if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException e) {
-                throw e;
-            } catch (Exception e) {
-                // Ignore.
-            }
-        }
-    }
-
-    static void closeQuietly(@Nullable InputStream stream) {
-       if (stream != null) {
-            try {
-                stream.close();
-            } catch (RuntimeException e) {
-                throw e;
-            } catch (Exception e) {
-                // Ignore.
-            }
-        }
-    }
-}
diff --git a/documents-archive/src/android/support/provider/ParsedDocumentId.java b/documents-archive/src/android/support/provider/ParsedDocumentId.java
deleted file mode 100644
index 2834455..0000000
--- a/documents-archive/src/android/support/provider/ParsedDocumentId.java
+++ /dev/null
@@ -1,57 +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.provider;
-
-import android.support.annotation.RestrictTo;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * @hide
- */
-@RestrictTo(GROUP_ID)
-class ParsedDocumentId {
-    public final String mArchiveId;
-    public final String mPath;
-
-    public ParsedDocumentId(String archiveId, String path) {
-        mArchiveId = archiveId;
-        mPath = path;
-    }
-
-    static public ParsedDocumentId fromDocumentId(String documentId, char idDelimiter) {
-        final int delimiterPosition = documentId.indexOf(idDelimiter);
-        if (delimiterPosition == -1) {
-            return new ParsedDocumentId(documentId, null);
-        } else {
-            return new ParsedDocumentId(documentId.substring(0, delimiterPosition),
-                    documentId.substring((delimiterPosition + 1)));
-        }
-    }
-
-    static public boolean hasPath(String documentId, char idDelimiter) {
-        return documentId.indexOf(idDelimiter) != -1;
-    }
-
-    public String toDocumentId(char idDelimiter) {
-        if (mPath == null) {
-            return mArchiveId;
-        } else {
-            return mArchiveId + idDelimiter + mPath;
-        }
-    }
-};
diff --git a/documents-archive/src/android/support/provider/Preconditions.java b/documents-archive/src/android/support/provider/Preconditions.java
deleted file mode 100644
index 050ca9a..0000000
--- a/documents-archive/src/android/support/provider/Preconditions.java
+++ /dev/null
@@ -1,58 +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.provider;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-
-import java.util.Collection;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Simple static methods to be called at the start of your own methods to verify
- * correct arguments and state.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-final class Preconditions {
-    static void checkArgument(boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalArgumentException(message);
-        }
-    }
-
-    static void checkArgumentNotNull(Object object, String message) {
-        if (object == null) {
-            throw new IllegalArgumentException(message);
-        }
-    }
-
-    static void checkArgumentEquals(String expected, @Nullable String actual, String message) {
-        if (!TextUtils.equals(expected, actual)) {
-            throw new IllegalArgumentException(String.format(message, String.valueOf(expected),
-                    String.valueOf(actual)));
-        }
-    }
-
-    static void checkState(boolean expression, String message) {
-        if (!expression) {
-            throw new IllegalStateException(message);
-        }
-    }
-}
diff --git a/documents-archive/tests/Android.mk b/documents-archive/tests/Android.mk
deleted file mode 100644
index 84cc3c3..0000000
--- a/documents-archive/tests/Android.mk
+++ /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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test \
-    android-support-documents-archive
-LOCAL_AAPT_FLAGS := --auto-add-overlay -0 zip
-LOCAL_PACKAGE_NAME := AndroidSupportDocumentsArchiveTests
-
-include $(BUILD_PACKAGE)
diff --git a/documents-archive/tests/AndroidManifest.xml b/documents-archive/tests/AndroidManifest.xml
deleted file mode 100644
index 47da733..0000000
--- a/documents-archive/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.support.provider.tests">
-    <application>
-        <uses-library android:name="android.test.runner" />
-        <provider
-            android:name="android.support.provider.tests.StubProvider"
-            android:authorities="android.support.provider.tests.mystubprovider"
-            android:grantUriPermissions="true"
-            android:exported="true"
-            android:permission="android.permission.MANAGE_DOCUMENTS" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="android.support.provider.tests"
-        android:label="Tests for android.support.provider." />
-</manifest>
diff --git a/documents-archive/tests/NO_DOCS b/documents-archive/tests/NO_DOCS
deleted file mode 100644
index e69de29..0000000
--- a/documents-archive/tests/NO_DOCS
+++ /dev/null
diff --git a/documents-archive/tests/res/raw/archive.zip b/documents-archive/tests/res/raw/archive.zip
deleted file mode 100644
index c3b8d22..0000000
--- a/documents-archive/tests/res/raw/archive.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/empty_dirs.zip b/documents-archive/tests/res/raw/empty_dirs.zip
deleted file mode 100644
index 1dd2251..0000000
--- a/documents-archive/tests/res/raw/empty_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/res/raw/no_dirs.zip b/documents-archive/tests/res/raw/no_dirs.zip
deleted file mode 100644
index e178ae1..0000000
--- a/documents-archive/tests/res/raw/no_dirs.zip
+++ /dev/null
Binary files differ
diff --git a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java b/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
deleted file mode 100644
index 9845412..0000000
--- a/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
+++ /dev/null
@@ -1,273 +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.provider.tests;
-
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.support.provider.DocumentArchive;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Scanner;
-
-/**
- * Tests for DocumentArchive.
- */
-public class DocumentArchiveTest extends AndroidTestCase {
-    private static final String DOCUMENT_ID = "document-id";
-    private static final char DELIMITER = ':';
-    private static final String NOTIFICATION_URI = "content://notification-uri";
-    private DocumentArchive mArchive = null;
-
-    public void loadArchive(int resource) {
-        // Extract the file from resources.
-        File file = null;
-        try {
-            file = File.createTempFile("android.support.provider.tests{",
-                    "}.zip", mContext.getCacheDir());
-            try (
-                final FileOutputStream outputStream =
-                        new ParcelFileDescriptor.AutoCloseOutputStream(
-                                ParcelFileDescriptor.open(
-                                        file, ParcelFileDescriptor.MODE_WRITE_ONLY));
-                final InputStream inputStream =
-                        mContext.getResources().openRawResource(resource);
-            ) {
-                final byte[] buffer = new byte[32 * 1024];
-                int bytes;
-                while ((bytes = inputStream.read(buffer)) != -1) {
-                    outputStream.write(buffer, 0, bytes);
-                }
-                outputStream.flush();
-                mArchive = DocumentArchive.createForLocalFile(
-                      mContext,
-                      file,
-                      DOCUMENT_ID,
-                      DELIMITER,
-                      Uri.parse(NOTIFICATION_URI));
-
-            }
-        } catch (IOException e) {
-            fail(String.valueOf(e));
-        } finally {
-            // On UNIX the file will be still available for processes which opened it, even
-            // after deleting it. Remove it ASAP, as it won't be used by anyone else.
-            if (file != null) {
-                file.delete();
-            }
-        }
-    }
-
-    @Override
-    public void tearDown() {
-        if (mArchive != null) {
-            mArchive.close();
-        }
-    }
-
-    public void testQueryChildDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(cursor.moveToNext());
-        assertEquals("document-id:dir2/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(cursor.moveToNext());
-        assertEquals("document-id:file1.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("file1.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(13,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertFalse(cursor.moveToNext());
-
-        // Check if querying children works too.
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/cherries.txt",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("cherries.txt",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(17,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-    }
-
-    public void testQueryChildDocument_NoDirs() throws IOException {
-        loadArchive(R.raw.no_dirs);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/dir2/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(childCursor.moveToNext());
-
-        final Cursor childCursor2 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir2/", null, null);
-
-        assertTrue(childCursor2.moveToFirst());
-        assertEquals("document-id:dir1/dir2/cherries.txt",
-                childCursor2.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertFalse(childCursor2.moveToNext());
-    }
-
-    public void testQueryChildDocument_EmptyDirs() throws IOException {
-        loadArchive(R.raw.empty_dirs);
-        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir1/",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir1",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor = mArchive.queryChildDocuments("document-id:dir1/", null, null);
-
-        assertTrue(childCursor.moveToFirst());
-        assertEquals("document-id:dir1/dir2/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir2",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-
-        assertTrue(childCursor.moveToNext());
-        assertEquals("document-id:dir1/dir3/",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("dir3",
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_DISPLAY_NAME)));
-        assertEquals(Document.MIME_TYPE_DIR,
-                childCursor.getString(childCursor.getColumnIndexOrThrow(
-                        Document.COLUMN_MIME_TYPE)));
-        assertEquals(0,
-                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-        assertFalse(cursor.moveToNext());
-
-        final Cursor childCursor2 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir2/", null, null);
-        assertFalse(childCursor2.moveToFirst());
-
-        final Cursor childCursor3 = mArchive.queryChildDocuments(
-                "document-id:dir1/dir3/", null, null);
-        assertFalse(childCursor3.moveToFirst());
-    }
-
-    public void testGetDocumentType() throws IOException {
-        loadArchive(R.raw.archive);
-        assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType("document-id:dir1/"));
-        assertEquals("text/plain", mArchive.getDocumentType("document-id:file1.txt"));
-    }
-
-    public void testIsChildDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/"));
-        assertFalse(mArchive.isChildDocument(DOCUMENT_ID, "document-id:this-does-not-exist"));
-        assertTrue(mArchive.isChildDocument("document-id:dir1/", "document-id:dir1/cherries.txt"));
-        assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1/cherries.txt"));
-    }
-
-    public void testQueryDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final Cursor cursor = mArchive.queryDocument("document-id:dir2/strawberries.txt", null);
-
-        assertTrue(cursor.moveToFirst());
-        assertEquals("document-id:dir2/strawberries.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
-        assertEquals("strawberries.txt",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
-        assertEquals("text/plain",
-                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
-        assertEquals(21,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
-    }
-
-    public void testOpenDocument() throws IOException {
-        loadArchive(R.raw.archive);
-        final ParcelFileDescriptor descriptor = mArchive.openDocument(
-                "document-id:dir2/strawberries.txt", "r", null /* signal */);
-        try (final ParcelFileDescriptor.AutoCloseInputStream inputStream =
-                new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
-            assertEquals("I love strawberries!", new Scanner(inputStream).nextLine());
-        }
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/IntegrationTest.java b/documents-archive/tests/src/android/support/provider/IntegrationTest.java
deleted file mode 100644
index 0445d82..0000000
--- a/documents-archive/tests/src/android/support/provider/IntegrationTest.java
+++ /dev/null
@@ -1,136 +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.provider.tests;
-
-import android.content.ContentProviderClient;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.IllegalArgumentException;
-import java.util.Scanner;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Integration tests for DocumentsProvider and DocumentArchiveHelper.
- *
- * <p>Only checks if the provider, then helper are forwarding the calls to the
- * underlying {@code ArchiveDocument} correctly. More detailed output testing is
- * done in {@code DocumentArchiveTest}.
- */
-public class IntegrationTest extends AndroidTestCase {
-    private ContentProviderClient mClient;
-
-    @Override
-    public void setUp() throws RemoteException {
-        mClient = getContext().getContentResolver().acquireContentProviderClient(
-                StubProvider.AUTHORITY);
-        assertNotNull(mClient);
-        mClient.call("reset", null, null);
-    }
-
-    @Override
-    public void tearDown() {
-        if (mClient != null) {
-            mClient.release();
-            mClient = null;
-        }
-    }
-
-    public void testQueryForChildren() throws IOException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildChildDocumentsUri(
-                        StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(3, cursor.getCount());
-    }
-
-    public void testQueryForDocument_Archive()
-            throws IOException, RemoteException, InterruptedException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertTrue(cursor.moveToFirst());
-        assertEquals(Document.FLAG_ARCHIVE,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS)));
-    }
-
-    public void testQueryForDocument_ArchiveDescendant()
-            throws IOException, RemoteException, InterruptedException {
-        final Cursor cursor = mContext.getContentResolver().query(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                        null, null, null, null);
-        assertEquals(1, cursor.getCount());
-        assertEquals(StubProvider.NOTIFY_URI, cursor.getNotificationUri());
-
-        final CountDownLatch changeSignal = new CountDownLatch(1);
-        final ContentObserver observer = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                changeSignal.countDown();
-            }
-        };
-
-        try {
-            getContext().getContentResolver().registerContentObserver(
-                    cursor.getNotificationUri(), false /* notifyForDescendants */, observer);
-
-            // Simulate deleting the archive file, then confirm that the notification is
-            // propagated and the archive closed.
-            mClient.call("delete", null, null);
-            changeSignal.await();
-
-            mContext.getContentResolver().query(
-                    DocumentsContract.buildChildDocumentsUri(
-                            StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                            null, null, null, null);
-            fail("Expected IllegalStateException, but succeeded.");
-        } catch (IllegalStateException e) {
-            // Expected, as the file is gone.
-        } finally {
-            getContext().getContentResolver().unregisterContentObserver(observer);
-        }
-    }
-
-    public void testGetType() throws IOException {
-        assertEquals("text/plain", mContext.getContentResolver().getType(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID)));
-    }
-
-    public void testOpenFileDescriptor() throws IOException {
-        final ParcelFileDescriptor descriptor = mContext.getContentResolver().openFileDescriptor(
-                DocumentsContract.buildDocumentUri(
-                        StubProvider.AUTHORITY, StubProvider.FILE_DOCUMENT_ID),
-                        "r", null);
-        assertNotNull(descriptor);
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/StubProvider.java b/documents-archive/tests/src/android/support/provider/StubProvider.java
deleted file mode 100644
index 3f72cd2..0000000
--- a/documents-archive/tests/src/android/support/provider/StubProvider.java
+++ /dev/null
@@ -1,154 +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.provider.tests;
-
-import android.database.Cursor;
-import android.database.MatrixCursor.RowBuilder;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsProvider;
-import android.support.provider.DocumentArchiveHelper;
-import android.util.Log;
-
-import java.io.FileNotFoundException;
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Stub provider for testing support for archives.
- */
-public class StubProvider extends DocumentsProvider {
-    public static final String AUTHORITY = "android.support.provider.tests.mystubprovider";
-    public static final String DOCUMENT_ID = "document-id";
-    public static final String FILE_DOCUMENT_ID = "document-id:dir1/cherries.txt";
-    public static final Uri NOTIFY_URI = DocumentsContract.buildRootsUri(AUTHORITY);
-
-    private static final String TAG = "StubProvider";
-    private static final String[] DEFAULT_PROJECTION = new String[] {
-            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_SIZE,
-            Document.COLUMN_MIME_TYPE, Document.COLUMN_FLAGS,
-            DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH
-    };
-
-    public File file;
-    public DocumentArchiveHelper archiveHelper;
-    public boolean simulatedDelete = false;
-
-    @Override
-    public Bundle call(String method, String args, Bundle extras) {
-        switch (method) {
-            case "reset":
-                simulatedDelete = false;
-                getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
-                return null;
-            case "delete":
-                simulatedDelete = true;
-                getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
-                return null;
-            default:
-                return super.call(method, args, extras);
-        }
-    }
-
-    @Override
-    public boolean onCreate() {
-        try {
-            archiveHelper = new DocumentArchiveHelper(this, ':');
-            file = TestUtils.createFileFromResource(getContext(), R.raw.archive);
-            return true;
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to initialize StubProvider.");
-            return false;
-        }
-    }
-
-    @Override
-    public ParcelFileDescriptor openDocument(
-            String documentId, String mode, CancellationSignal signal)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.openDocument(documentId, mode, signal);
-        }
-
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Cursor queryChildDocuments(
-            String parentDocumentId, String[] projection, String sortOrder)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(parentDocumentId) ||
-                archiveHelper.isSupportedArchiveType(getDocumentType(parentDocumentId))) {
-            return archiveHelper.queryChildDocuments(parentDocumentId, projection, sortOrder);
-        }
-
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Cursor queryDocument(String documentId, String[] projection)
-            throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.queryDocument(documentId, projection);
-        }
-
-        if (DOCUMENT_ID.equals(documentId)) {
-            if (simulatedDelete) {
-                throw new FileNotFoundException();
-            }
-
-            final MatrixCursor result = new MatrixCursor(
-                    projection != null ? projection : DEFAULT_PROJECTION);
-            result.setNotificationUri(getContext().getContentResolver(), NOTIFY_URI);
-            final RowBuilder row = result.newRow();
-            row.add(Document.COLUMN_DOCUMENT_ID, DOCUMENT_ID);
-            row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
-            row.add(Document.COLUMN_SIZE, file.length());
-            row.add(Document.COLUMN_MIME_TYPE, "application/zip");
-            final int flags = archiveHelper.isSupportedArchiveType("application/zip")
-                    ? Document.FLAG_ARCHIVE : 0;
-            row.add(Document.COLUMN_FLAGS, flags);
-            row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, file.getPath());
-            return result;
-        }
-
-        throw new FileNotFoundException();
-    }
-
-    @Override
-    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String getDocumentType(String documentId) throws FileNotFoundException {
-        if (archiveHelper.isArchivedDocument(documentId)) {
-            return archiveHelper.getDocumentType(documentId);
-        }
-
-        if (DOCUMENT_ID.equals(documentId)) {
-            return "application/zip";
-        }
-
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/documents-archive/tests/src/android/support/provider/TestUtils.java b/documents-archive/tests/src/android/support/provider/TestUtils.java
deleted file mode 100644
index 17ec3e1..0000000
--- a/documents-archive/tests/src/android/support/provider/TestUtils.java
+++ /dev/null
@@ -1,55 +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.provider.tests;
-
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Utilities for tests.
- */
-final class TestUtils {
-    /**
-     * Saves a file from resources to a temporary location and returns a File instance for it.
-     *
-     * @param id Resource ID
-     */
-    static File createFileFromResource(Context context, int id) throws IOException {
-        final File file = File.createTempFile("android.support.provider.tests{",
-                "}.zip", context.getCacheDir());
-        try (
-            final FileOutputStream outputStream =
-                    new ParcelFileDescriptor.AutoCloseOutputStream(
-                            ParcelFileDescriptor.open(
-                                    file, ParcelFileDescriptor.MODE_WRITE_ONLY));
-            final InputStream inputStream = context.getResources().openRawResource(id);
-        ) {
-            final byte[] buffer = new byte[32 * 1024];
-            int bytes;
-            while ((bytes = inputStream.read(buffer)) != -1) {
-                outputStream.write(buffer, 0, bytes);
-            }
-            outputStream.flush();
-            return file;
-        }
-    }
-}
diff --git a/fragment/Android.mk b/fragment/Android.mk
index e5cb644..a41b0c2 100644
--- a/fragment/Android.mk
+++ b/fragment/Android.mk
@@ -14,81 +14,34 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# A helper sub-library that makes direct use of Gingerbread APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-compat \
-    android-support-core-utils \
-    android-support-media-compat \
-    android-support-core-ui
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Honeycomb APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-honeycomb
-LOCAL_SDK_VERSION := 11
-LOCAL_SRC_FILES := $(call all-java-files-under, honeycomb)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-gingerbread
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-honeycomb
-LOCAL_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-compat \
-    android-support-media-compat \
-    android-support-core-ui \
-    android-support-core-utils
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-fragment-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-jellybean
-LOCAL_JAVA_LIBRARIES := \
-    android-support-annotations \
-    android-support-compat \
-    android-support-media-compat \
-    android-support-core-ui \
-    android-support-core-utils
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-fragment \
+#       android-support-compat \
+#       android-support-media-compat \
+#       android-support-core-ui \
+#       android-support-core-utils
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-fragment
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, gingerbread) \
+    $(call all-java-files-under, honeycomb) \
+    $(call all-java-files-under, jellybean) \
+    $(call all-java-files-under, api21) \
+    $(call all-java-files-under, java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-fragment-api21
-LOCAL_JAVA_LIBRARIES := \
-    android-support-annotations \
+LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
-    android-support-media-compat \
     android-support-core-ui \
-    android-support-core-utils
+    android-support-core-utils \
+    android-support-media-compat \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
index 49f2ade..846c568 100644
--- a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -16,7 +16,9 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
@@ -28,232 +30,34 @@
 import java.util.List;
 import java.util.Map;
 
+@RequiresApi(21)
+@TargetApi(21)
 class FragmentTransitionCompat21 {
-    public static String getTransitionName(View view) {
-        return view.getTransitionName();
-    }
 
+    /**
+     * Returns a clone of a transition or null if it is null
+     */
     public static Object cloneTransition(Object transition) {
+        Transition copy = null;
         if (transition != null) {
-            transition = ((Transition)transition).clone();
+            copy = ((Transition) transition).clone();
         }
-        return transition;
+        return copy;
     }
 
-    public static Object captureExitingViews(Object exitTransition, View root,
-            ArrayList<View> viewList, Map<String, View> namedViews, View nonExistentView) {
-        if (exitTransition != null) {
-            captureTransitioningViews(viewList, root);
-            if (namedViews != null) {
-                viewList.removeAll(namedViews.values());
-            }
-            if (viewList.isEmpty()) {
-                exitTransition = null;
-            } else {
-                viewList.add(nonExistentView);
-                addTargets((Transition) exitTransition, viewList);
-            }
-        }
-        return exitTransition;
-    }
-
-    public static void excludeTarget(Object transitionObject, View view, boolean exclude) {
-        Transition transition = (Transition) transitionObject;
-        transition.excludeTarget(view, exclude);
-    }
-
-    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
-        Transition transition = (Transition) transitionObject;
-        TransitionManager.beginDelayedTransition(sceneRoot, transition);
-    }
-
-    public static void setEpicenter(Object transitionObject, View view) {
-        Transition transition = (Transition) transitionObject;
-        final Rect epicenter = getBoundsOnScreen(view);
-
-        transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-            @Override
-            public Rect onGetEpicenter(Transition transition) {
-                return epicenter;
-            }
-        });
-    }
-
-    public static Object wrapSharedElementTransition(Object transitionObj) {
-        if (transitionObj == null) {
-            return null;
-        }
-        Transition transition = (Transition) transitionObj;
+    /**
+     * Wraps a transition in a TransitionSet and returns the set. If transition is null, null is
+     * returned.
+     */
+    public static Object wrapTransitionInSet(Object transition) {
         if (transition == null) {
             return null;
         }
         TransitionSet transitionSet = new TransitionSet();
-        transitionSet.addTransition(transition);
+        transitionSet.addTransition((Transition) transition);
         return transitionSet;
     }
 
-    private static void excludeViews(Transition transition, Transition fromTransition,
-            ArrayList<View> views, boolean exclude) {
-        if (transition != null) {
-            final int viewCount = fromTransition == null ? 0 : views.size();
-            for (int i = 0; i < viewCount; i++) {
-                transition.excludeTarget(views.get(i), exclude);
-            }
-        }
-    }
-
-    /**
-     * Exclude (or remove the exclude) of shared element views from the enter and exit transitions.
-     *
-     * @param enterTransitionObj The enter transition
-     * @param exitTransitionObj The exit transition
-     * @param sharedElementTransitionObj The shared element transition
-     * @param views The shared element target views.
-     * @param exclude <code>true</code> to exclude or <code>false</code> to remove the excluded
-     *                views.
-     */
-    public static void excludeSharedElementViews(Object enterTransitionObj,
-            Object exitTransitionObj, Object sharedElementTransitionObj, ArrayList<View> views,
-            boolean exclude) {
-        Transition enterTransition = (Transition) enterTransitionObj;
-        Transition exitTransition = (Transition) exitTransitionObj;
-        Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
-        excludeViews(enterTransition, sharedElementTransition, views, exclude);
-        excludeViews(exitTransition, sharedElementTransition, views, exclude);
-    }
-
-    /**
-     * Prepares the enter transition by adding a non-existent view to the transition's target list
-     * and setting it epicenter callback. By adding a non-existent view to the target list,
-     * we can prevent any view from being targeted at the beginning of the transition.
-     * We will add to the views before the end state of the transition is captured so that the
-     * views will appear. At the start of the transition, we clear the list of targets so that
-     * we can restore the state of the transition and use it again.
-     *
-     * <p>The shared element transition maps its shared elements immediately prior to
-     *  capturing the final state of the Transition.</p>
-     */
-    public static void addTransitionTargets(Object enterTransitionObject,
-            Object sharedElementTransitionObject, Object exitTransitionObject, final View container,
-            final ViewRetriever inFragment, final View nonExistentView,
-            EpicenterView epicenterView, final Map<String, String> nameOverrides,
-            final ArrayList<View> enteringViews, final ArrayList<View> exitingViews,
-            final Map<String, View> namedViews, final Map<String, View> renamedViews,
-            final ArrayList<View> sharedElementTargets) {
-        final Transition enterTransition = (Transition) enterTransitionObject;
-        final Transition exitTransition = (Transition) exitTransitionObject;
-        final Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
-        excludeViews(enterTransition, exitTransition, exitingViews, true);
-        if (enterTransitionObject != null || sharedElementTransitionObject != null) {
-            if (enterTransition != null) {
-                enterTransition.addTarget(nonExistentView);
-            }
-            if (sharedElementTransitionObject != null) {
-                setSharedElementTargets(sharedElementTransition, nonExistentView,
-                        namedViews, sharedElementTargets);
-                excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
-                excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
-            }
-
-            container.getViewTreeObserver().addOnPreDrawListener(
-                    new ViewTreeObserver.OnPreDrawListener() {
-                        @Override
-                        public boolean onPreDraw() {
-                            container.getViewTreeObserver().removeOnPreDrawListener(this);
-                            if (enterTransition != null) {
-                                enterTransition.removeTarget(nonExistentView);
-                            }
-                            if (inFragment != null) {
-                                View fragmentView = inFragment.getView();
-                                if (fragmentView != null) {
-                                    if (!nameOverrides.isEmpty()) {
-                                        findNamedViews(renamedViews, fragmentView);
-                                        renamedViews.keySet().retainAll(nameOverrides.values());
-                                        for (Map.Entry<String, String> entry : nameOverrides
-                                                .entrySet()) {
-                                            String to = entry.getValue();
-                                            View view = renamedViews.get(to);
-                                            if (view != null) {
-                                                String from = entry.getKey();
-                                                view.setTransitionName(from);
-                                            }
-                                        }
-                                    }
-                                    if (enterTransition != null) {
-                                        captureTransitioningViews(enteringViews, fragmentView);
-                                        enteringViews.removeAll(renamedViews.values());
-                                        enteringViews.add(nonExistentView);
-                                        addTargets(enterTransition, enteringViews);
-                                    }
-                                }
-                            }
-                            excludeViews(exitTransition, enterTransition, enteringViews, true);
-
-                            return true;
-                        }
-                    });
-            setSharedElementEpicenter(enterTransition, epicenterView);
-        }
-    }
-
-    public static Object mergeTransitions(Object enterTransitionObject,
-            Object exitTransitionObject, Object sharedElementTransitionObject,
-            boolean allowOverlap) {
-        boolean overlap = true;
-        Transition enterTransition = (Transition) enterTransitionObject;
-        Transition exitTransition = (Transition) exitTransitionObject;
-        Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
-
-        if (enterTransition != null && exitTransition != null) {
-            overlap = allowOverlap;
-        }
-
-        // Wrap the transitions. Explicit targets like in enter and exit will cause the
-        // views to be targeted regardless of excluded views. If that happens, then the
-        // excluded fragments views (hidden fragments) will still be in the transition.
-
-        Transition transition;
-        if (overlap) {
-            // Regular transition -- do it all together
-            TransitionSet transitionSet = new TransitionSet();
-            if (enterTransition != null) {
-                transitionSet.addTransition(enterTransition);
-            }
-            if (exitTransition != null) {
-                transitionSet.addTransition(exitTransition);
-            }
-            if (sharedElementTransition != null) {
-                transitionSet.addTransition(sharedElementTransition);
-            }
-            transition = transitionSet;
-        } else {
-            // First do exit, then enter, but allow shared element transition to happen
-            // during both.
-            Transition staggered = null;
-            if (exitTransition != null && enterTransition != null) {
-                staggered = new TransitionSet()
-                        .addTransition(exitTransition)
-                        .addTransition(enterTransition)
-                        .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
-            } else if (exitTransition != null) {
-                staggered = exitTransition;
-            } else if (enterTransition != null) {
-                staggered = enterTransition;
-            }
-            if (sharedElementTransition != null) {
-                TransitionSet together = new TransitionSet();
-                if (staggered != null) {
-                    together.addTransition(staggered);
-                }
-                together.addTransition(sharedElementTransition);
-                transition = together;
-            } else {
-                transition = staggered;
-            }
-        }
-        return transition;
-    }
-
     /**
      * Finds all children of the shared elements and sets the wrapping TransitionSet
      * targets to point to those. It also limits transitions that have no targets to the
@@ -261,21 +65,18 @@
      * shared elements specifically, but this doesn't happen by default.
      */
     public static void setSharedElementTargets(Object transitionObj,
-            View nonExistentView, Map<String, View> namedViews,
-            ArrayList<View> sharedElementTargets) {
+            View nonExistentView, ArrayList<View> sharedViews) {
         TransitionSet transition = (TransitionSet) transitionObj;
-        sharedElementTargets.clear();
-        sharedElementTargets.addAll(namedViews.values());
-
         final List<View> views = transition.getTargets();
         views.clear();
-        final int count = sharedElementTargets.size();
+        final int count = sharedViews.size();
         for (int i = 0; i < count; i++) {
-            final View view = sharedElementTargets.get(i);
+            final View view = sharedViews.get(i);
             bfsAddViewChildren(views, view);
         }
-        sharedElementTargets.add(nonExistentView);
-        addTargets(transition, sharedElementTargets);
+        views.add(nonExistentView);
+        sharedViews.add(nonExistentView);
+        addTargets(transition, sharedViews);
     }
 
     /**
@@ -316,139 +117,32 @@
         return false;
     }
 
-    private static void setSharedElementEpicenter(Transition transition,
-            final EpicenterView epicenterView) {
-        if (transition != null) {
-            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-                private Rect mEpicenter;
+    /**
+     * Sets a transition epicenter to the rectangle of a given View.
+     */
+    public static void setEpicenter(Object transitionObj, View view) {
+        if (view != null) {
+            Transition transition = (Transition) transitionObj;
+            final Rect epicenter = new Rect();
+            getBoundsOnScreen(view, epicenter);
 
+            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
                 @Override
                 public Rect onGetEpicenter(Transition transition) {
-                    if (mEpicenter == null && epicenterView.epicenter != null) {
-                        mEpicenter = getBoundsOnScreen(epicenterView.epicenter);
-                    }
-                    return mEpicenter;
-                }
-            });
-        }
-    }
-
-    private static Rect getBoundsOnScreen(View view) {
-        Rect epicenter = new Rect();
-        int[] loc = new int[2];
-        view.getLocationOnScreen(loc);
-        // not as good as View.getBoundsOnScreen, but that's not public
-        epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
-        return epicenter;
-    }
-
-    private static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                if (viewGroup.isTransitionGroup()) {
-                    transitioningViews.add(viewGroup);
-                } else {
-                    int count = viewGroup.getChildCount();
-                    for (int i = 0; i < count; i++) {
-                        View child = viewGroup.getChildAt(i);
-                        captureTransitioningViews(transitioningViews, child);
-                    }
-                }
-            } else {
-                transitioningViews.add(view);
-            }
-        }
-    }
-
-    public static void findNamedViews(Map<String, View> namedViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            String transitionName = view.getTransitionName();
-            if (transitionName != null) {
-                namedViews.put(transitionName, view);
-            }
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                int count = viewGroup.getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = viewGroup.getChildAt(i);
-                    findNamedViews(namedViews, child);
-                }
-            }
-        }
-    }
-
-    public static void cleanupTransitions(final View sceneRoot, final View nonExistentView,
-            Object enterTransitionObject, final ArrayList<View> enteringViews,
-            Object exitTransitionObject, final ArrayList<View> exitingViews,
-            Object sharedElementTransitionObject, final ArrayList<View> sharedElementTargets,
-            Object overallTransitionObject, final ArrayList<View> hiddenViews,
-            final Map<String, View> renamedViews) {
-        final Transition enterTransition = (Transition) enterTransitionObject;
-        final Transition exitTransition = (Transition) exitTransitionObject;
-        final Transition sharedElementTransition = (Transition) sharedElementTransitionObject;
-        final Transition overallTransition = (Transition) overallTransitionObject;
-        if (overallTransition != null) {
-            sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                    new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                    if (enterTransition != null) {
-                        removeTargets(enterTransition, enteringViews);
-                        excludeViews(enterTransition, exitTransition, exitingViews, false);
-                        excludeViews(enterTransition, sharedElementTransition, sharedElementTargets,
-                                false);
-                    }
-                    if (exitTransition != null) {
-                        removeTargets(exitTransition, exitingViews);
-                        excludeViews(exitTransition, enterTransition, enteringViews, false);
-                        excludeViews(exitTransition, sharedElementTransition, sharedElementTargets,
-                                false);
-                    }
-                    if (sharedElementTransition != null) {
-                        removeTargets(sharedElementTransition, sharedElementTargets);
-                    }
-                    for (Map.Entry<String, View> entry : renamedViews.entrySet()) {
-                        View view = entry.getValue();
-                        String name = entry.getKey();
-                        view.setTransitionName(name);
-                    }
-                    int numViews = hiddenViews.size();
-                    for (int i = 0; i < numViews; i++) {
-                        overallTransition.excludeTarget(hiddenViews.get(i), false);
-                    }
-                    overallTransition.excludeTarget(nonExistentView, false);
-                    return true;
+                    return epicenter;
                 }
             });
         }
     }
 
     /**
-     * This method removes the views from transitions that target ONLY those views.
-     * The views list should match those added in addTargets and should contain
-     * one view that is not in the view hierarchy (state.nonExistentView).
+     * Replacement for view.getBoundsOnScreen because that is not public. This returns a rect
+     * containing the bounds relative to the screen that the view is in.
      */
-    public static void removeTargets(Object transitionObject, ArrayList<View> views) {
-        Transition transition = (Transition) transitionObject;
-        if (transition instanceof TransitionSet) {
-            TransitionSet set = (TransitionSet) transition;
-            int numTransitions = set.getTransitionCount();
-            for (int i = 0; i < numTransitions; i++) {
-                Transition child = set.getTransitionAt(i);
-                removeTargets(child, views);
-            }
-        } else if (!hasSimpleTarget(transition)) {
-            List<View> targets = transition.getTargets();
-            if (targets != null && targets.size() == views.size() &&
-                    targets.containsAll(views)) {
-                // We have an exact match. We must have added these earlier in addTargets
-                for (int i = views.size() - 1; i >= 0; i--) {
-                    transition.removeTarget(views.get(i));
-                }
-            }
-        }
+    public static void getBoundsOnScreen(View view, Rect epicenter) {
+        int[] loc = new int[2];
+        view.getLocationOnScreen(loc);
+        epicenter.set(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
     }
 
     /**
@@ -457,10 +151,13 @@
      * that does not exist in the view hierarchy (state.nonExistentView) so that
      * when they are removed later, a list match will suffice to remove the targets.
      * Otherwise, if you happened to have targeted the exact views for the transition,
-     * the removeTargets call will remove them unexpectedly.
+     * the replaceTargets call will remove them unexpectedly.
      */
-    public static void addTargets(Object transitionObject, ArrayList<View> views) {
-        Transition transition = (Transition) transitionObject;
+    public static void addTargets(Object transitionObj, ArrayList<View> views) {
+        Transition transition = (Transition) transitionObj;
+        if (transition == null) {
+            return;
+        }
         if (transition instanceof TransitionSet) {
             TransitionSet set = (TransitionSet) transition;
             int numTransitions = set.getTransitionCount();
@@ -480,21 +177,373 @@
         }
     }
 
+    /**
+     * Returns true if there are any targets based on ID, transition or type.
+     */
     private static boolean hasSimpleTarget(Transition transition) {
-        return !isNullOrEmpty(transition.getTargetIds()) ||
-                !isNullOrEmpty(transition.getTargetNames()) ||
-                !isNullOrEmpty(transition.getTargetTypes());
+        return !isNullOrEmpty(transition.getTargetIds())
+                || !isNullOrEmpty(transition.getTargetNames())
+                || !isNullOrEmpty(transition.getTargetTypes());
     }
 
+    /**
+     * Simple utility to detect if a list is null or has no elements.
+     */
     private static boolean isNullOrEmpty(List list) {
         return list == null || list.isEmpty();
     }
 
-    public interface ViewRetriever {
-        View getView();
+    /**
+     * Creates a TransitionSet that plays all passed transitions together. Any null
+     * transitions passed will not be added to the set. If all are null, then an empty
+     * TransitionSet will be returned.
+     */
+    public static Object mergeTransitionsTogether(Object transition1, Object transition2,
+            Object transition3) {
+        TransitionSet transitionSet = new TransitionSet();
+        if (transition1 != null) {
+            transitionSet.addTransition((Transition) transition1);
+        }
+        if (transition2 != null) {
+            transitionSet.addTransition((Transition) transition2);
+        }
+        if (transition3 != null) {
+            transitionSet.addTransition((Transition) transition3);
+        }
+        return transitionSet;
     }
 
-    public static class EpicenterView {
-        public View epicenter;
+    /**
+     * Combines enter, exit, and shared element transition so that they play in the proper
+     * sequence. First the exit transition plays along with the shared element transition.
+     * When the exit transition completes, the enter transition starts. The shared element
+     * transition can continue running while the enter transition plays.
+     *
+     * @return A TransitionSet with all of enter, exit, and shared element transitions in
+     * it (modulo null values), ordered such that they play in the proper sequence.
+     */
+    public static Object mergeTransitionsInSequence(Object exitTransitionObj,
+            Object enterTransitionObj, Object sharedElementTransitionObj) {
+        // First do exit, then enter, but allow shared element transition to happen
+        // during both.
+        Transition staggered = null;
+        final Transition exitTransition = (Transition) exitTransitionObj;
+        final Transition enterTransition = (Transition) enterTransitionObj;
+        final Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
+        if (exitTransition != null && enterTransition != null) {
+            staggered = new TransitionSet()
+                    .addTransition(exitTransition)
+                    .addTransition(enterTransition)
+                    .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
+        } else if (exitTransition != null) {
+            staggered = exitTransition;
+        } else if (enterTransition != null) {
+            staggered = enterTransition;
+        }
+        if (sharedElementTransition != null) {
+            TransitionSet together = new TransitionSet();
+            if (staggered != null) {
+                together.addTransition(staggered);
+            }
+            together.addTransition(sharedElementTransition);
+            return together;
+        } else {
+            return staggered;
+        }
+    }
+
+    /**
+     * Calls {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}.
+     */
+    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
+        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
+    }
+
+    /**
+     * Prepares for setting the shared element names by gathering the names of the incoming
+     * shared elements and clearing them. {@link #setNameOverridesOptimized(View, ArrayList,
+     * ArrayList, ArrayList, Map)} must be called after this to complete setting the shared element
+     * name overrides. This must be called before
+     * {@link #beginDelayedTransition(ViewGroup, Object)}.
+     */
+    public static ArrayList<String> prepareSetNameOverridesOptimized(
+            final ArrayList<View> sharedElementsIn) {
+        final ArrayList<String> names = new ArrayList<>();
+        final int numSharedElements = sharedElementsIn.size();
+        for (int i = 0; i < numSharedElements; i++) {
+            final View view = sharedElementsIn.get(i);
+            names.add(view.getTransitionName());
+            view.setTransitionName(null);
+        }
+        return names;
+    }
+
+    /**
+     * Changes the shared element names for the incoming shared eleemnts to match those of the
+     * outgoing shared elements. This also temporarily clears the shared element names of the
+     * outgoing shared elements. Must be called after
+     * {@link #beginDelayedTransition(ViewGroup, Object)}.
+     */
+    public static void setNameOverridesOptimized(final View sceneRoot,
+            final ArrayList<View> sharedElementsOut, final ArrayList<View> sharedElementsIn,
+            final ArrayList<String> inNames, final Map<String, String> nameOverrides) {
+        final int numSharedElements = sharedElementsIn.size();
+        final ArrayList<String> outNames = new ArrayList<>();
+
+        for (int i = 0; i < numSharedElements; i++) {
+            final View view = sharedElementsOut.get(i);
+            final String name = view.getTransitionName();
+            outNames.add(name);
+            if (name == null) {
+                continue;
+            }
+            view.setTransitionName(null);
+            final String inName = nameOverrides.get(name);
+            for (int j = 0; j < numSharedElements; j++) {
+                if (inName.equals(inNames.get(j))) {
+                    sharedElementsIn.get(j).setTransitionName(name);
+                    break;
+                }
+            }
+        }
+
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                        for (int i = 0; i < numSharedElements; i++) {
+                            sharedElementsIn.get(i).setTransitionName(inNames.get(i));
+                            sharedElementsOut.get(i).setTransitionName(outNames.get(i));
+                        }
+                        return true;
+                    }
+                });
+    }
+
+    /**
+     * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
+     * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
+     *                           a normal View or a ViewGroup with
+     *                           {@link android.view.ViewGroup#isTransitionGroup()} true.
+     * @param view The base of the view hierarchy to look in.
+     */
+    public static void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
+        if (view.getVisibility() == View.VISIBLE) {
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) view;
+                if (viewGroup.isTransitionGroup()) {
+                    transitioningViews.add(viewGroup);
+                } else {
+                    int count = viewGroup.getChildCount();
+                    for (int i = 0; i < count; i++) {
+                        View child = viewGroup.getChildAt(i);
+                        captureTransitioningViews(transitioningViews, child);
+                    }
+                }
+            } else {
+                transitioningViews.add(view);
+            }
+        }
+    }
+
+    /**
+     * Finds all views that have transition names in the hierarchy under the given view and
+     * stores them in {@code namedViews} map with the name as the key.
+     */
+    public static void findNamedViews(Map<String, View> namedViews, View view) {
+        if (view.getVisibility() == View.VISIBLE) {
+            String transitionName = view.getTransitionName();
+            if (transitionName != null) {
+                namedViews.put(transitionName, view);
+            }
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = (ViewGroup) view;
+                int count = viewGroup.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View child = viewGroup.getChildAt(i);
+                    findNamedViews(namedViews, child);
+                }
+            }
+        }
+    }
+
+    public static void setNameOverridesUnoptimized(final View sceneRoot,
+            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                        final int numSharedElements = sharedElementsIn.size();
+                        for (int i = 0; i < numSharedElements; i++) {
+                            View view = sharedElementsIn.get(i);
+                            String name = view.getTransitionName();
+                            if (name != null) {
+                                String inName = findKeyForValue(nameOverrides, name);
+                                view.setTransitionName(inName);
+                            }
+                        }
+                        return true;
+                    }
+                });
+    }
+
+    /**
+     * Utility to find the String key in {@code map} that maps to {@code value}.
+     */
+    private static String findKeyForValue(Map<String, String> map, String value) {
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            if (value.equals(entry.getValue())) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * After the transition has started, remove all targets that we added to the transitions
+     * so that the transitions are left in a clean state.
+     */
+    public static void scheduleRemoveTargets(final Object overallTransitionObj,
+            final Object enterTransition, final ArrayList<View> enteringViews,
+            final Object exitTransition, final ArrayList<View> exitingViews,
+            final Object sharedElementTransition, final ArrayList<View> sharedElementsIn) {
+        final Transition overallTransition = (Transition) overallTransitionObj;
+        overallTransition.addListener(new Transition.TransitionListener() {
+            @Override
+            public void onTransitionStart(Transition transition) {
+                if (enterTransition != null) {
+                    replaceTargets(enterTransition, enteringViews, null);
+                }
+                if (exitTransition != null) {
+                    replaceTargets(exitTransition, exitingViews, null);
+                }
+                if (sharedElementTransition != null) {
+                    replaceTargets(sharedElementTransition, sharedElementsIn, null);
+                }
+            }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionCancel(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionPause(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionResume(Transition transition) {
+            }
+        });
+    }
+
+    /**
+     * Swap the targets for the shared element transition from those Views in sharedElementsOut
+     * to those in sharedElementsIn
+     */
+    public static void swapSharedElementTargets(Object sharedElementTransitionObj,
+            ArrayList<View> sharedElementsOut, ArrayList<View> sharedElementsIn) {
+        TransitionSet sharedElementTransition = (TransitionSet) sharedElementTransitionObj;
+        if (sharedElementTransition != null) {
+            sharedElementTransition.getTargets().clear();
+            sharedElementTransition.getTargets().addAll(sharedElementsIn);
+            replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
+        }
+    }
+
+
+    /**
+     * This method removes the views from transitions that target ONLY those views and
+     * replaces them with the new targets list.
+     * The views list should match those added in addTargets and should contain
+     * one view that is not in the view hierarchy (state.nonExistentView).
+     */
+    public static void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
+            ArrayList<View> newTargets) {
+        Transition transition = (Transition) transitionObj;
+        if (transition instanceof TransitionSet) {
+            TransitionSet set = (TransitionSet) transition;
+            int numTransitions = set.getTransitionCount();
+            for (int i = 0; i < numTransitions; i++) {
+                Transition child = set.getTransitionAt(i);
+                replaceTargets(child, oldTargets, newTargets);
+            }
+        } else if (!hasSimpleTarget(transition)) {
+            List<View> targets = transition.getTargets();
+            if (targets != null && targets.size() == oldTargets.size()
+                    && targets.containsAll(oldTargets)) {
+                // We have an exact match. We must have added these earlier in addTargets
+                final int targetCount = newTargets == null ? 0 : newTargets.size();
+                for (int i = 0; i < targetCount; i++) {
+                    transition.addTarget(newTargets.get(i));
+                }
+                for (int i = oldTargets.size() - 1; i >= 0; i--) {
+                    transition.removeTarget(oldTargets.get(i));
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a View target to a transition. If transitionObj is null, nothing is done.
+     */
+    public static void addTarget(Object transitionObj, View view) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.addTarget(view);
+        }
+    }
+
+    /**
+     * Remove a View target to a transition. If transitionObj is null, nothing is done.
+     */
+    public static void removeTarget(Object transitionObj, View view) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.removeTarget(view);
+        }
+    }
+
+    /**
+     * Sets the epicenter of a transition to a rect object. The object can be modified until
+     * the transition runs.
+     */
+    public static void setEpicenter(Object transitionObj, final Rect epicenter) {
+        if (transitionObj != null) {
+            Transition transition = (Transition) transitionObj;
+            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+                @Override
+                public Rect onGetEpicenter(Transition transition) {
+                    if (epicenter == null || epicenter.isEmpty()) {
+                        return null;
+                    }
+                    return epicenter;
+                }
+            });
+        }
+    }
+
+    public static void scheduleNameReset(final ViewGroup sceneRoot,
+            final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                        final int numSharedElements = sharedElementsIn.size();
+                        for (int i = 0; i < numSharedElements; i++) {
+                            final View view = sharedElementsIn.get(i);
+                            final String name = view.getTransitionName();
+                            final String inName = nameOverrides.get(name);
+                            view.setTransitionName(inName);
+                        }
+                        return true;
+                    }
+                });
     }
 }
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 592c856..0a862d4 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -1,8 +1,6 @@
 apply plugin: 'com.android.library'
 archivesBaseName = 'support-fragment'
 
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.fragment.apiTargets)
 dependencies {
     compile project(':support-compat')
     compile project(':support-media-compat')
@@ -22,22 +20,25 @@
 
 sourceCompatibility = JavaVersion.VERSION_1_7
 targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.fragment.dependencies)
 
 android {
-    compileSdkVersion 9
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
 
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
+        main.java.srcDirs = [
+                'gingerbread',
+                'honeycomb',
+                'jellybean',
+                'api21',
+                'java'
+        ]
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/java'
@@ -92,11 +93,6 @@
         exclude('android/service/media/**')
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java b/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
index 890b802..f2f5154 100644
--- a/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
+++ b/fragment/gingerbread/android/support/v4/app/BaseFragmentActivityGingerbread.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +24,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -31,6 +33,8 @@
  *
  * @hide
  */
+@RequiresApi(9)
+@TargetApi(9)
 abstract class BaseFragmentActivityGingerbread extends Activity {
 
     // We need to keep track of whether startIntentSenderForResult originated from a Fragment, so we
diff --git a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java b/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
index 95f3d8d..a266fba 100644
--- a/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
+++ b/fragment/honeycomb/android/support/v4/app/BaseFragmentActivityHoneycomb.java
@@ -16,8 +16,10 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -26,6 +28,8 @@
  *
  * @hide
  */
+@RequiresApi(11)
+@TargetApi(11)
 abstract class BaseFragmentActivityHoneycomb extends BaseFragmentActivityGingerbread {
 
     @Override
diff --git a/fragment/java/android/support/v4/app/BackStackRecord.java b/fragment/java/android/support/v4/app/BackStackRecord.java
index 2e3116f..19f949c 100644
--- a/fragment/java/android/support/v4/app/BackStackRecord.java
+++ b/fragment/java/android/support/v4/app/BackStackRecord.java
@@ -19,14 +19,11 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.v4.util.ArrayMap;
 import android.support.v4.util.LogWriter;
+import android.support.v4.view.ViewCompat;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -45,39 +42,25 @@
     final CharSequence mBreadCrumbShortTitleText;
     final ArrayList<String> mSharedElementSourceNames;
     final ArrayList<String> mSharedElementTargetNames;
+    final boolean mAllowOptimization;
 
     public BackStackState(BackStackRecord bse) {
-        int numRemoved = 0;
-        BackStackRecord.Op op = bse.mHead;
-        while (op != null) {
-            if (op.removed != null) numRemoved += op.removed.size();
-            op = op.next;
-        }
-        mOps = new int[bse.mNumOp*7 + numRemoved];
+        final int numOps = bse.mOps.size();
+        mOps = new int[numOps * 6];
 
         if (!bse.mAddToBackStack) {
             throw new IllegalStateException("Not on back stack");
         }
 
-        op = bse.mHead;
         int pos = 0;
-        while (op != null) {
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final BackStackRecord.Op op = bse.mOps.get(opNum);
             mOps[pos++] = op.cmd;
             mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
             mOps[pos++] = op.enterAnim;
             mOps[pos++] = op.exitAnim;
             mOps[pos++] = op.popEnterAnim;
             mOps[pos++] = op.popExitAnim;
-            if (op.removed != null) {
-                final int N = op.removed.size();
-                mOps[pos++] = N;
-                for (int i=0; i<N; i++) {
-                    mOps[pos++] = op.removed.get(i).mIndex;
-                }
-            } else {
-                mOps[pos++] = 0;
-            }
-            op = op.next;
         }
         mTransition = bse.mTransition;
         mTransitionStyle = bse.mTransitionStyle;
@@ -89,6 +72,7 @@
         mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
         mSharedElementSourceNames = bse.mSharedElementSourceNames;
         mSharedElementTargetNames = bse.mSharedElementTargetNames;
+        mAllowOptimization = bse.mAllowOptimization;
     }
 
     public BackStackState(Parcel in) {
@@ -103,6 +87,7 @@
         mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mSharedElementSourceNames = in.createStringArrayList();
         mSharedElementTargetNames = in.createStringArrayList();
+        mAllowOptimization = in.readInt() != 0;
     }
 
     public BackStackRecord instantiate(FragmentManagerImpl fm) {
@@ -125,16 +110,6 @@
             op.exitAnim = mOps[pos++];
             op.popEnterAnim = mOps[pos++];
             op.popExitAnim = mOps[pos++];
-            final int N = mOps[pos++];
-            if (N > 0) {
-                op.removed = new ArrayList<Fragment>(N);
-                for (int i=0; i<N; i++) {
-                    if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
-                            "Instantiate " + bse + " set remove fragment #" + mOps[pos]);
-                    Fragment r = fm.mActive.get(mOps[pos++]);
-                    op.removed.add(r);
-                }
-            }
             bse.mEnterAnim = op.enterAnim;
             bse.mExitAnim = op.exitAnim;
             bse.mPopEnterAnim = op.popEnterAnim;
@@ -153,6 +128,7 @@
         bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
         bse.mSharedElementSourceNames = mSharedElementSourceNames;
         bse.mSharedElementTargetNames = mSharedElementTargetNames;
+        bse.mAllowOptimization = mAllowOptimization;
         bse.bumpBackStackNesting(1);
         return bse;
     }
@@ -175,6 +151,7 @@
         TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
         dest.writeStringList(mSharedElementSourceNames);
         dest.writeStringList(mSharedElementTargetNames);
+        dest.writeInt(mAllowOptimization ? 1 : 0);
     }
 
     public static final Parcelable.Creator<BackStackState> CREATOR
@@ -195,7 +172,7 @@
  * Entry of an operation on the fragment back stack.
  */
 final class BackStackRecord extends FragmentTransaction implements
-        FragmentManager.BackStackEntry, Runnable {
+        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
     static final String TAG = FragmentManagerImpl.TAG;
     static final boolean SUPPORTS_TRANSITIONS = Build.VERSION.SDK_INT >= 21;
 
@@ -211,20 +188,15 @@
     static final int OP_ATTACH = 7;
 
     static final class Op {
-        Op next;
-        Op prev;
         int cmd;
         Fragment fragment;
         int enterAnim;
         int exitAnim;
         int popEnterAnim;
         int popExitAnim;
-        ArrayList<Fragment> removed;
     }
 
-    Op mHead;
-    Op mTail;
-    int mNumOp;
+    ArrayList<Op> mOps = new ArrayList<>();
     int mEnterAnim;
     int mExitAnim;
     int mPopEnterAnim;
@@ -244,6 +216,7 @@
 
     ArrayList<String> mSharedElementSourceNames;
     ArrayList<String> mSharedElementTargetNames;
+    boolean mAllowOptimization = true;
 
     @Override
     public String toString() {
@@ -303,12 +276,12 @@
             }
         }
 
-        if (mHead != null) {
+        if (!mOps.isEmpty()) {
             writer.print(prefix); writer.println("Operations:");
             String innerPrefix = prefix + "    ";
-            Op op = mHead;
-            int num = 0;
-            while (op != null) {
+            final int numOps = mOps.size();
+            for (int opNum = 0; opNum < numOps; opNum++) {
+                final Op op = mOps.get(opNum);
                 String cmdStr;
                 switch (op.cmd) {
                     case OP_NULL: cmdStr="NULL"; break;
@@ -321,7 +294,7 @@
                     case OP_ATTACH: cmdStr="ATTACH"; break;
                     default: cmdStr="cmd=" + op.cmd; break;
                 }
-                writer.print(prefix); writer.print("  Op #"); writer.print(num);
+                writer.print(prefix); writer.print("  Op #"); writer.print(opNum);
                         writer.print(": "); writer.print(cmdStr);
                         writer.print(" "); writer.println(op.fragment);
                 if (full) {
@@ -338,23 +311,6 @@
                                 writer.println(Integer.toHexString(op.popExitAnim));
                     }
                 }
-                if (op.removed != null && op.removed.size() > 0) {
-                    for (int i=0; i<op.removed.size(); i++) {
-                        writer.print(innerPrefix);
-                        if (op.removed.size() == 1) {
-                            writer.print("Removed: ");
-                        } else {
-                            if (i == 0) {
-                                writer.println("Removed:");
-                            }
-                            writer.print(innerPrefix); writer.print("  #"); writer.print(i);
-                                    writer.print(": ");
-                        }
-                        writer.println(op.removed.get(i));
-                    }
-                }
-                op = op.next;
-                num++;
             }
         }
     }
@@ -395,18 +351,11 @@
     }
 
     void addOp(Op op) {
-        if (mHead == null) {
-            mHead = mTail = op;
-        } else {
-            op.prev = mTail;
-            mTail.next = op;
-            mTail = op;
-        }
+        mOps.add(op);
         op.enterAnim = mEnterAnim;
         op.exitAnim = mExitAnim;
         op.popEnterAnim = mPopEnterAnim;
         op.popExitAnim = mPopExitAnim;
-        mNumOp++;
     }
 
     @Override
@@ -556,7 +505,7 @@
     @Override
     public FragmentTransaction addSharedElement(View sharedElement, String name) {
         if (SUPPORTS_TRANSITIONS) {
-            String transitionName = FragmentTransitionCompat21.getTransitionName(sharedElement);
+            String transitionName = ViewCompat.getTransitionName(sharedElement);
             if (transitionName == null) {
                 throw new IllegalArgumentException("Unique transitionNames are required for all" +
                         " sharedElements");
@@ -638,22 +587,14 @@
         }
         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
                 + " by " + amt);
-        Op op = mHead;
-        while (op != null) {
+        final int numOps = mOps.size();
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final Op op = mOps.get(opNum);
             if (op.fragment != null) {
                 op.fragment.mBackStackNesting += amt;
                 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                         + op.fragment + " to " + op.fragment.mBackStackNesting);
             }
-            if (op.removed != null) {
-                for (int i=op.removed.size()-1; i>=0; i--) {
-                    Fragment r = op.removed.get(i);
-                    r.mBackStackNesting += amt;
-                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
-                            + r + " to " + r.mBackStackNesting);
-                }
-            }
-            op = op.next;
         }
     }
 
@@ -679,6 +620,12 @@
         mManager.execSingleAction(this, true);
     }
 
+    @Override
+    public FragmentTransaction setAllowOptimization(boolean allowOptimization) {
+        mAllowOptimization = allowOptimization;
+        return this;
+    }
+
     int commitInternal(boolean allowStateLoss) {
         if (mCommitted) throw new IllegalStateException("commit already called");
         if (FragmentManagerImpl.DEBUG) {
@@ -697,354 +644,240 @@
         return mIndex;
     }
 
+    /**
+     * Implementation of {@link FragmentManagerImpl.OpGenerator}.
+     * This operation is added to the list of pending actions during {@link #commit()}, and
+     * will be executed on the UI thread to run this FragmentTransaction.
+     *
+     * @param records Modified to add this BackStackRecord
+     * @param isRecordPop Modified to add a false (this isn't a pop)
+     * @return true always because the records and isRecordPop will always be changed
+     */
     @Override
-    public void run() {
-        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
-
-        if (mAddToBackStack) {
-            if (mIndex < 0) {
-                throw new IllegalStateException("addToBackStack() called after commit()");
-            }
+    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
+        if (FragmentManagerImpl.DEBUG) {
+            Log.v(TAG, "Run: " + this);
         }
 
-        bumpBackStackNesting(1);
-
-        TransitionState state = null;
-        SparseArray<Fragment> firstOutFragments = null;
-        SparseArray<Fragment> lastInFragments = null;
-        if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {
-            firstOutFragments = new SparseArray<Fragment>();
-            lastInFragments = new SparseArray<Fragment>();
-
-            calculateFragments(firstOutFragments, lastInFragments);
-
-            state = beginTransition(firstOutFragments, lastInFragments, false);
-        }
-
-        int transitionStyle = state != null ? 0 : mTransitionStyle;
-        int transition = state != null ? 0 : mTransition;
-        Op op = mHead;
-        while (op != null) {
-            int enterAnim = state != null ? 0 : op.enterAnim;
-            int exitAnim = state != null ? 0 : op.exitAnim;
-            switch (op.cmd) {
-                case OP_ADD: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = enterAnim;
-                    mManager.addFragment(f, false);
-                } break;
-                case OP_REPLACE: {
-                    Fragment f = op.fragment;
-                    int containerId = f.mContainerId;
-                    if (mManager.mAdded != null) {
-                        for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
-                            Fragment old = mManager.mAdded.get(i);
-                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
-                                    "OP_REPLACE: adding=" + f + " old=" + old);
-                            if (old.mContainerId == containerId) {
-                                if (old == f) {
-                                    op.fragment = f = null;
-                                } else {
-                                    if (op.removed == null) {
-                                        op.removed = new ArrayList<Fragment>();
-                                    }
-                                    op.removed.add(old);
-                                    old.mNextAnim = exitAnim;
-                                    if (mAddToBackStack) {
-                                        old.mBackStackNesting += 1;
-                                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
-                                                + old + " to " + old.mBackStackNesting);
-                                    }
-                                    mManager.removeFragment(old, transition, transitionStyle);
-                                }
-                            }
-                        }
-                    }
-                    if (f != null) {
-                        f.mNextAnim = enterAnim;
-                        mManager.addFragment(f, false);
-                    }
-                } break;
-                case OP_REMOVE: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = exitAnim;
-                    mManager.removeFragment(f, transition, transitionStyle);
-                } break;
-                case OP_HIDE: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = exitAnim;
-                    mManager.hideFragment(f, transition, transitionStyle);
-                } break;
-                case OP_SHOW: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = enterAnim;
-                    mManager.showFragment(f, transition, transitionStyle);
-                } break;
-                case OP_DETACH: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = exitAnim;
-                    mManager.detachFragment(f, transition, transitionStyle);
-                } break;
-                case OP_ATTACH: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = enterAnim;
-                    mManager.attachFragment(f, transition, transitionStyle);
-                } break;
-                default: {
-                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
-                }
-            }
-
-            op = op.next;
-        }
-
-        mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
-
+        records.add(this);
+        isRecordPop.add(false);
         if (mAddToBackStack) {
             mManager.addBackStackState(this);
         }
+        return true;
     }
 
-    private static void setFirstOut(SparseArray<Fragment> firstOutFragments,
-            SparseArray<Fragment> lastInFragments, Fragment fragment) {
-        if (fragment != null) {
-            int containerId = fragment.mContainerId;
-            if (containerId != 0 && !fragment.isHidden()) {
-                if (fragment.isAdded() && fragment.getView() != null
-                        && firstOutFragments.get(containerId) == null) {
-                    firstOutFragments.put(containerId, fragment);
-                }
-                if (lastInFragments.get(containerId) == fragment) {
-                    lastInFragments.remove(containerId);
+    boolean interactsWith(int containerId) {
+        final int numOps = mOps.size();
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final Op op = mOps.get(opNum);
+            if (op.fragment.mContainerId == containerId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
+        if (endIndex == startIndex) {
+            return false;
+        }
+        final int numOps = mOps.size();
+        int lastContainer = -1;
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final Op op = mOps.get(opNum);
+            final int container = op.fragment.mContainerId;
+            if (container != 0 && container != lastContainer) {
+                lastContainer = container;
+                for (int i = startIndex; i < endIndex; i++) {
+                    BackStackRecord record = records.get(i);
+                    final int numThoseOps = record.mOps.size();
+                    for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
+                        final Op thatOp = record.mOps.get(thoseOpIndex);
+                        if (thatOp.fragment.mContainerId == container) {
+                            return true;
+                        }
+                    }
                 }
             }
         }
+        return false;
     }
 
-    private void setLastIn(SparseArray<Fragment> firstOutFragments,
-            SparseArray<Fragment> lastInFragments, Fragment fragment) {
-        if (fragment != null) {
-            int containerId = fragment.mContainerId;
-            if (containerId != 0) {
-                if (!fragment.isAdded()) {
-                    lastInFragments.put(containerId, fragment);
-                }
-                if (firstOutFragments.get(containerId) == fragment) {
-                    firstOutFragments.remove(containerId);
-                }
+    /**
+     * Executes the operations contained within this transaction. The Fragment states will only
+     * be modified if optimizations are not allowed.
+     */
+    void executeOps() {
+        final int numOps = mOps.size();
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final Op op = mOps.get(opNum);
+            final Fragment f = op.fragment;
+            f.setNextTransition(mTransition, mTransitionStyle);
+            switch (op.cmd) {
+                case OP_ADD:
+                    f.setNextAnim(op.enterAnim);
+                    mManager.addFragment(f, false);
+                    break;
+                case OP_REMOVE:
+                    f.setNextAnim(op.exitAnim);
+                    mManager.removeFragment(f);
+                    break;
+                case OP_HIDE:
+                    f.setNextAnim(op.exitAnim);
+                    mManager.hideFragment(f);
+                    break;
+                case OP_SHOW:
+                    f.setNextAnim(op.enterAnim);
+                    mManager.showFragment(f);
+                    break;
+                case OP_DETACH:
+                    f.setNextAnim(op.exitAnim);
+                    mManager.detachFragment(f);
+                    break;
+                case OP_ATTACH:
+                    f.setNextAnim(op.enterAnim);
+                    mManager.attachFragment(f);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
             }
-            if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) {
-                mManager.makeActive(fragment);
-                mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+            if (!mAllowOptimization && op.cmd != OP_ADD) {
+                mManager.moveFragmentToExpectedState(f);
             }
         }
+        if (!mAllowOptimization) {
+            // Added fragments are added at the end to comply with prior behavior.
+            mManager.moveToState(mManager.mCurState);
+        }
     }
 
     /**
-     * Finds the first removed fragment and last added fragments when going forward.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param firstOutFragments The list of first fragments to be removed, keyed on the
-     *                          container ID. This list will be modified by the method.
-     * @param lastInFragments The list of last fragments to be added, keyed on the
-     *                        container ID. This list will be modified by the method.
+     * Reverses the execution of the operations within this transaction. The Fragment states will
+     * only be modified if optimizations are not allowed.
      */
-    private void calculateFragments(SparseArray<Fragment> firstOutFragments,
-            SparseArray<Fragment> lastInFragments) {
-        if (!mManager.mContainer.onHasView()) {
-            return; // nothing to see, so no transitions
-        }
-        Op op = mHead;
-        while (op != null) {
+    void executePopOps() {
+        for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
+            final Op op = mOps.get(opNum);
+            Fragment f = op.fragment;
+            f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
             switch (op.cmd) {
                 case OP_ADD:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
+                    f.setNextAnim(op.popExitAnim);
+                    mManager.removeFragment(f);
+                    break;
+                case OP_REMOVE:
+                    f.setNextAnim(op.popEnterAnim);
+                    mManager.addFragment(f, false);
+                    break;
+                case OP_HIDE:
+                    f.setNextAnim(op.popEnterAnim);
+                    mManager.showFragment(f);
+                    break;
+                case OP_SHOW:
+                    f.setNextAnim(op.popExitAnim);
+                    mManager.hideFragment(f);
+                    break;
+                case OP_DETACH:
+                    f.setNextAnim(op.popEnterAnim);
+                    mManager.attachFragment(f);
+                    break;
+                case OP_ATTACH:
+                    f.setNextAnim(op.popExitAnim);
+                    mManager.detachFragment(f);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
+            }
+            if (!mAllowOptimization && op.cmd != OP_ADD) {
+                mManager.moveFragmentToExpectedState(f);
+            }
+        }
+        if (!mAllowOptimization) {
+            mManager.moveToState(mManager.mCurState);
+        }
+    }
+
+    /**
+     * Removes all OP_REPLACE ops and replaces them with the proper add and remove
+     * operations that are equivalent to the replace. This must be called prior to
+     * {@link #executeOps()} or any other call that operations on mOps.
+     *
+     * @param added Initialized to the fragments that are in the mManager.mAdded, this
+     *              will be modified to contain the fragments that will be in mAdded
+     *              after the execution ({@link #executeOps()}.
+     */
+    void expandReplaceOps(ArrayList<Fragment> added) {
+        for (int opNum = 0; opNum < mOps.size(); opNum++) {
+            final Op op = mOps.get(opNum);
+            switch (op.cmd) {
+                case OP_ADD:
+                case OP_ATTACH:
+                    added.add(op.fragment);
+                    break;
+                case OP_REMOVE:
+                case OP_DETACH:
+                    added.remove(op.fragment);
                     break;
                 case OP_REPLACE: {
                     Fragment f = op.fragment;
-                    if (mManager.mAdded != null) {
-                        for (int i = 0; i < mManager.mAdded.size(); i++) {
-                            Fragment old = mManager.mAdded.get(i);
-                            if (f == null || old.mContainerId == f.mContainerId) {
-                                if (old == f) {
-                                    f = null;
-                                    lastInFragments.remove(old.mContainerId);
-                                } else {
-                                    setFirstOut(firstOutFragments, lastInFragments, old);
-                                }
+                    int containerId = f.mContainerId;
+                    boolean alreadyAdded = false;
+                    for (int i = added.size() - 1; i >= 0; i--) {
+                        Fragment old = added.get(i);
+                        if (old.mContainerId == containerId) {
+                            if (old == f) {
+                                alreadyAdded = true;
+                            } else {
+                                Op removeOp = new Op();
+                                removeOp.cmd = OP_REMOVE;
+                                removeOp.fragment = old;
+                                removeOp.enterAnim = op.enterAnim;
+                                removeOp.popEnterAnim = op.popEnterAnim;
+                                removeOp.exitAnim = op.exitAnim;
+                                removeOp.popExitAnim = op.popExitAnim;
+                                mOps.add(opNum, removeOp);
+                                added.remove(old);
+                                opNum++;
                             }
                         }
                     }
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
+                    if (alreadyAdded) {
+                        mOps.remove(opNum);
+                        opNum--;
+                    } else {
+                        op.cmd = OP_ADD;
+                        added.add(f);
+                    }
                 }
-                case OP_REMOVE:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_HIDE:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_SHOW:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_DETACH:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_ATTACH:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
+                break;
             }
-
-            op = op.next;
         }
     }
 
-    /**
-     * Finds the first removed fragment and last added fragments when popping the back stack.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param firstOutFragments The list of first fragments to be removed, keyed on the
-     *                          container ID. This list will be modified by the method.
-     * @param lastInFragments The list of last fragments to be added, keyed on the
-     *                        container ID. This list will be modified by the method.
-     */
-    public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
-            SparseArray<Fragment> lastInFragments) {
-        if (!mManager.mContainer.onHasView()) {
-            return; // nothing to see, so no transitions
-        }
-        Op op = mTail;
-        while (op != null) {
-            switch (op.cmd) {
-                case OP_ADD:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_REPLACE:
-                    if (op.removed != null) {
-                        for (int i = op.removed.size() - 1; i >= 0; i--) {
-                            setLastIn(firstOutFragments, lastInFragments, op.removed.get(i));
-                        }
-                    }
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_REMOVE:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_HIDE:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_SHOW:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_DETACH:
-                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
-                    break;
-                case OP_ATTACH:
-                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
-                    break;
+    boolean isPostponed() {
+        for (int opNum = 0; opNum < mOps.size(); opNum++) {
+            final Op op = mOps.get(opNum);
+            if (isFragmentPostponed(op)) {
+                return true;
             }
+        }
+        return false;
+    }
 
-            op = op.prev;
+    void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
+        for (int opNum = 0; opNum < mOps.size(); opNum++) {
+            final Op op = mOps.get(opNum);
+            if (isFragmentPostponed(op)) {
+                op.fragment.setOnStartEnterTransitionListener(listener);
+            }
         }
     }
 
-    public TransitionState popFromBackStack(boolean doStateMove, TransitionState state,
-            SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
-        if (FragmentManagerImpl.DEBUG) {
-            Log.v(TAG, "popFromBackStack: " + this);
-            LogWriter logw = new LogWriter(TAG);
-            PrintWriter pw = new PrintWriter(logw);
-            dump("  ", null, pw, null);
-        }
-
-        if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {
-            if (state == null) {
-                if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
-                    state = beginTransition(firstOutFragments, lastInFragments, true);
-                }
-            } else if (!doStateMove) {
-                setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
-            }
-        }
-
-        bumpBackStackNesting(-1);
-
-        int transitionStyle = state != null ? 0 : mTransitionStyle;
-        int transition = state != null ? 0 : mTransition;
-        Op op = mTail;
-        while (op != null) {
-            int popEnterAnim = state != null ? 0 : op.popEnterAnim;
-            int popExitAnim= state != null ? 0 : op.popExitAnim;
-            switch (op.cmd) {
-                case OP_ADD: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popExitAnim;
-                    mManager.removeFragment(f,
-                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                } break;
-                case OP_REPLACE: {
-                    Fragment f = op.fragment;
-                    if (f != null) {
-                        f.mNextAnim = popExitAnim;
-                        mManager.removeFragment(f,
-                                FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                    }
-                    if (op.removed != null) {
-                        for (int i=0; i<op.removed.size(); i++) {
-                            Fragment old = op.removed.get(i);
-                            old.mNextAnim = popEnterAnim;
-                            mManager.addFragment(old, false);
-                        }
-                    }
-                } break;
-                case OP_REMOVE: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popEnterAnim;
-                    mManager.addFragment(f, false);
-                } break;
-                case OP_HIDE: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popEnterAnim;
-                    mManager.showFragment(f,
-                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                } break;
-                case OP_SHOW: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popExitAnim;
-                    mManager.hideFragment(f,
-                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                } break;
-                case OP_DETACH: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popEnterAnim;
-                    mManager.attachFragment(f,
-                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                } break;
-                case OP_ATTACH: {
-                    Fragment f = op.fragment;
-                    f.mNextAnim = popEnterAnim;
-                    mManager.detachFragment(f,
-                            FragmentManagerImpl.reverseTransit(transition), transitionStyle);
-                } break;
-                default: {
-                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
-                }
-            }
-
-            op = op.prev;
-        }
-
-        if (doStateMove) {
-            mManager.moveToState(mManager.mCurState,
-                    FragmentManagerImpl.reverseTransit(transition), transitionStyle, true);
-            state = null;
-        }
-
-        if (mIndex >= 0) {
-            mManager.freeBackStackIndex(mIndex);
-            mIndex = -1;
-        }
-        return state;
+    private static boolean isFragmentPostponed(Op op) {
+        final Fragment fragment = op.fragment;
+        return (fragment.mAdded && fragment.mView != null && !fragment.mDetached
+                && !fragment.mHidden && fragment.isPostponed());
     }
 
     @Override
@@ -1062,487 +895,6 @@
 
     @Override
     public boolean isEmpty() {
-        return mNumOp == 0;
-    }
-
-    /**
-     * When custom fragment transitions are used, this sets up the state for each transition
-     * and begins the transition. A different transition is started for each fragment container
-     * and consists of up to 3 different transitions: the exit transition, a shared element
-     * transition and an enter transition.
-     *
-     * <p>The exit transition operates against the leaf nodes of the first fragment
-     * with a view that was removed. If no such fragment was removed, then no exit
-     * transition is executed. The exit transition comes from the outgoing fragment.</p>
-     *
-     * <p>The enter transition operates against the last fragment that was added. If
-     * that fragment does not have a view or no fragment was added, then no enter
-     * transition is executed. The enter transition comes from the incoming fragment.</p>
-     *
-     * <p>The shared element transition operates against all views and comes either
-     * from the outgoing fragment or the incoming fragment, depending on whether this
-     * is going forward or popping the back stack. When going forward, the incoming
-     * fragment's enter shared element transition is used, but when going back, the
-     * outgoing fragment's return shared element transition is used. Shared element
-     * transitions only operate if there is both an incoming and outgoing fragment.</p>
-     *
-     * @param firstOutFragments The list of first fragments to be removed, keyed on the
-     *                          container ID.
-     * @param lastInFragments The list of last fragments to be added, keyed on the
-     *                        container ID.
-     * @param isBack true if this is popping the back stack or false if this is a
-     *               forward operation.
-     * @return The TransitionState used to complete the operation of the transition
-     * in {@link #setNameOverrides(BackStackRecord.TransitionState, java.util.ArrayList,
-     * java.util.ArrayList)}.
-     */
-    private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
-            SparseArray<Fragment> lastInFragments, boolean isBack) {
-        TransitionState state = new TransitionState();
-
-        // Adding a non-existent target view makes sure that the transitions don't target
-        // any views by default. They'll only target the views we tell add. If we don't
-        // add any, then no views will be targeted.
-        state.nonExistentView = new View(mManager.mHost.getContext());
-
-        boolean anyTransitionStarted = false;
-        // Go over all leaving fragments.
-        for (int i = 0; i < firstOutFragments.size(); i++) {
-            int containerId = firstOutFragments.keyAt(i);
-            if (configureTransitions(containerId, state, isBack, firstOutFragments,
-                    lastInFragments)) {
-                anyTransitionStarted = true;
-            }
-        }
-
-        // Now go over all entering fragments that didn't have a leaving fragment.
-        for (int i = 0; i < lastInFragments.size(); i++) {
-            int containerId = lastInFragments.keyAt(i);
-            if (firstOutFragments.get(containerId) == null &&
-                configureTransitions(containerId, state, isBack, firstOutFragments,
-                        lastInFragments)) {
-                anyTransitionStarted = true;
-            }
-        }
-
-        if (!anyTransitionStarted) {
-            state = null;
-        }
-
-        return state;
-    }
-
-    private static Object getEnterTransition(Fragment inFragment, boolean isBack) {
-        if (inFragment == null) {
-            return null;
-        }
-        return FragmentTransitionCompat21.cloneTransition(isBack ?
-                inFragment.getReenterTransition() : inFragment.getEnterTransition());
-    }
-
-    private static Object getExitTransition(Fragment outFragment, boolean isBack) {
-        if (outFragment == null) {
-            return null;
-        }
-        return FragmentTransitionCompat21.cloneTransition(isBack ?
-                outFragment.getReturnTransition() : outFragment.getExitTransition());
-    }
-
-    private static Object getSharedElementTransition(Fragment inFragment, Fragment outFragment,
-            boolean isBack) {
-        if (inFragment == null || outFragment == null) {
-            return null;
-        }
-        return FragmentTransitionCompat21.wrapSharedElementTransition(isBack ?
-                outFragment.getSharedElementReturnTransition() :
-                inFragment.getSharedElementEnterTransition());
-    }
-
-    private static Object captureExitingViews(Object exitTransition, Fragment outFragment,
-            ArrayList<View> exitingViews, ArrayMap<String, View> namedViews, View nonExistentView) {
-        if (exitTransition != null) {
-            exitTransition = FragmentTransitionCompat21.captureExitingViews(exitTransition,
-                    outFragment.getView(), exitingViews, namedViews, nonExistentView);
-        }
-        return exitTransition;
-    }
-
-    private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment,
-            boolean isBack) {
-        ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
-        if (mSharedElementSourceNames != null) {
-            FragmentTransitionCompat21.findNamedViews(namedViews, outFragment.getView());
-            if (isBack) {
-                namedViews.retainAll(mSharedElementTargetNames);
-            } else {
-                namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames,
-                        namedViews);
-            }
-        }
-
-        if (isBack) {
-            if (outFragment.mEnterTransitionCallback != null) {
-                outFragment.mEnterTransitionCallback.onMapSharedElements(
-                        mSharedElementTargetNames, namedViews);
-            }
-            setBackNameOverrides(state, namedViews, false);
-        } else {
-            if (outFragment.mExitTransitionCallback != null) {
-                outFragment.mExitTransitionCallback.onMapSharedElements(
-                        mSharedElementTargetNames, namedViews);
-            }
-            setNameOverrides(state, namedViews, false);
-        }
-
-        return namedViews;
-    }
-
-    /**
-     * Configures custom transitions for a specific fragment container.
-     *
-     * @param containerId The container ID of the fragments to configure the transition for.
-     * @param state The Transition State keeping track of the executing transitions.
-     * @param firstOutFragments The list of first fragments to be removed, keyed on the
-     *                          container ID.
-     * @param lastInFragments The list of last fragments to be added, keyed on the
-     *                        container ID.
-     * @param isBack true if this is popping the back stack or false if this is a
-     *               forward operation.
-     */
-    private boolean configureTransitions(int containerId, TransitionState state, boolean isBack,
-            SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
-        ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
-        if (sceneRoot == null) {
-            return false;
-        }
-        final Fragment inFragment = lastInFragments.get(containerId);
-        Fragment outFragment = firstOutFragments.get(containerId);
-
-        Object enterTransition = getEnterTransition(inFragment, isBack);
-        Object sharedElementTransition = getSharedElementTransition(inFragment, outFragment,
-                isBack);
-        Object exitTransition = getExitTransition(outFragment, isBack);
-        ArrayMap<String, View> namedViews = null;
-        ArrayList<View> sharedElementTargets = new ArrayList<View>();
-        if (sharedElementTransition != null) {
-            namedViews = remapSharedElements(state, outFragment, isBack);
-            if (namedViews.isEmpty()) {
-                sharedElementTransition = null;
-                namedViews = null;
-            } else {
-                // Notify the start of the transition.
-                SharedElementCallback callback = isBack ?
-                        outFragment.mEnterTransitionCallback :
-                        inFragment.mEnterTransitionCallback;
-                if (callback != null) {
-                    ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
-                    ArrayList<View> views = new ArrayList<View>(namedViews.values());
-                    callback.onSharedElementStart(names, views, null);
-                }
-                prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
-                        inFragment, outFragment, isBack, sharedElementTargets, enterTransition,
-                        exitTransition);
-            }
-        }
-        if (enterTransition == null && sharedElementTransition == null &&
-                exitTransition == null) {
-            return false; // no transitions!
-        }
-
-        ArrayList<View> exitingViews = new ArrayList<View>();
-        exitTransition = captureExitingViews(exitTransition, outFragment, exitingViews,
-                namedViews, state.nonExistentView);
-
-        // Set the epicenter of the exit transition
-        if (mSharedElementTargetNames != null && namedViews != null) {
-            View epicenterView = namedViews.get(mSharedElementTargetNames.get(0));
-            if (epicenterView != null) {
-                if (exitTransition != null) {
-                    FragmentTransitionCompat21.setEpicenter(exitTransition, epicenterView);
-                }
-                if (sharedElementTransition != null) {
-                    FragmentTransitionCompat21.setEpicenter(sharedElementTransition,
-                            epicenterView);
-                }
-            }
-        }
-
-        FragmentTransitionCompat21.ViewRetriever viewRetriever =
-                new FragmentTransitionCompat21.ViewRetriever() {
-                    @Override
-                    public View getView() {
-                        return inFragment.getView();
-                    }
-                };
-
-        ArrayList<View> enteringViews = new ArrayList<View>();
-        ArrayMap<String, View> renamedViews = new ArrayMap<String, View>();
-
-        boolean allowOverlap = true;
-        if (inFragment != null) {
-            allowOverlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
-                    inFragment.getAllowEnterTransitionOverlap();
-        }
-        Object transition = FragmentTransitionCompat21.mergeTransitions(enterTransition,
-                exitTransition, sharedElementTransition, allowOverlap);
-
-        if (transition != null) {
-            FragmentTransitionCompat21.addTransitionTargets(enterTransition,
-                    sharedElementTransition, exitTransition, sceneRoot, viewRetriever,
-                    state.nonExistentView, state.enteringEpicenterView, state.nameOverrides,
-                    enteringViews, exitingViews, namedViews, renamedViews, sharedElementTargets);
-            excludeHiddenFragmentsAfterEnter(sceneRoot, state, containerId, transition);
-
-            // We want to exclude hidden views later, so we need a non-null list in the
-            // transition now.
-            FragmentTransitionCompat21.excludeTarget(transition, state.nonExistentView, true);
-            // Now exclude all currently hidden fragments.
-            excludeHiddenFragments(state, containerId, transition);
-
-            FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
-
-            FragmentTransitionCompat21.cleanupTransitions(sceneRoot, state.nonExistentView,
-                    enterTransition, enteringViews, exitTransition, exitingViews,
-                    sharedElementTransition, sharedElementTargets,
-                    transition, state.hiddenFragmentViews, renamedViews);
-        }
-        return transition != null;
-    }
-
-    private void prepareSharedElementTransition(final TransitionState state, final View sceneRoot,
-            final Object sharedElementTransition, final Fragment inFragment,
-            final Fragment outFragment, final boolean isBack,
-            final ArrayList<View> sharedElementTargets, final Object enterTransition,
-            final Object exitTransition) {
-        if (sharedElementTransition != null) {
-            sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                    new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-
-                    // Remove the exclude for the shared elements from the exiting fragment.
-                    FragmentTransitionCompat21.removeTargets(sharedElementTransition,
-                            sharedElementTargets);
-                    // keep the nonExistentView as excluded so the list doesn't get emptied
-                    sharedElementTargets.remove(state.nonExistentView);
-                    FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
-                            exitTransition, sharedElementTransition, sharedElementTargets, false);
-                    sharedElementTargets.clear();
-
-                    ArrayMap<String, View> namedViews = mapSharedElementsIn(
-                            state, isBack, inFragment);
-                    FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
-                            state.nonExistentView, namedViews, sharedElementTargets);
-
-                    setEpicenterIn(namedViews, state);
-
-                    callSharedElementEnd(state, inFragment, outFragment, isBack,
-                            namedViews);
-
-                    // Exclude the shared elements from the entering fragment.
-                    FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
-                            exitTransition, sharedElementTransition, sharedElementTargets, true);
-                    return true;
-                }
-            });
-        }
-    }
-
-    void callSharedElementEnd(TransitionState state, Fragment inFragment,
-            Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) {
-        SharedElementCallback sharedElementCallback = isBack ?
-                outFragment.mEnterTransitionCallback :
-                inFragment.mEnterTransitionCallback;
-        if (sharedElementCallback != null) {
-            ArrayList<String> names = new ArrayList<String>(namedViews.keySet());
-            ArrayList<View> views = new ArrayList<View>(namedViews.values());
-            sharedElementCallback.onSharedElementEnd(names, views, null);
-        }
-    }
-
-    void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) {
-        if (mSharedElementTargetNames != null && !namedViews.isEmpty()) {
-            // now we know the epicenter of the entering transition.
-            View epicenter = namedViews
-                    .get(mSharedElementTargetNames.get(0));
-            if (epicenter != null) {
-                state.enteringEpicenterView.epicenter = epicenter;
-            }
-        }
-    }
-
-    ArrayMap<String, View> mapSharedElementsIn(TransitionState state,
-            boolean isBack, Fragment inFragment) {
-        // Now map the shared elements in the incoming fragment
-        ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack);
-
-        // remap shared elements and set the name mapping used
-        // in the shared element transition.
-        if (isBack) {
-            if (inFragment.mExitTransitionCallback != null) {
-                inFragment.mExitTransitionCallback.onMapSharedElements(
-                        mSharedElementTargetNames, namedViews);
-            }
-            setBackNameOverrides(state, namedViews, true);
-        } else {
-            if (inFragment.mEnterTransitionCallback != null) {
-                inFragment.mEnterTransitionCallback.onMapSharedElements(
-                        mSharedElementTargetNames, namedViews);
-            }
-            setNameOverrides(state, namedViews, true);
-        }
-        return namedViews;
-    }
-
-    /**
-     * Remaps a name-to-View map, substituting different names for keys.
-     *
-     * @param inMap A list of keys found in the map, in the order in toGoInMap
-     * @param toGoInMap A list of keys to use for the new map, in the order of inMap
-     * @param namedViews The current mapping
-     * @return A copy of namedViews with the keys coming from toGoInMap.
-     */
-    private static ArrayMap<String, View> remapNames(ArrayList<String> inMap,
-            ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) {
-        if (namedViews.isEmpty()) {
-            return namedViews;
-        }
-        ArrayMap<String, View> remappedViews = new ArrayMap<String, View>();
-        int numKeys = inMap.size();
-        for (int i = 0; i < numKeys; i++) {
-            View view = namedViews.get(inMap.get(i));
-            if (view != null) {
-                remappedViews.put(toGoInMap.get(i), view);
-            }
-        }
-        return remappedViews;
-    }
-
-    /**
-     * Maps shared elements to views in the entering fragment.
-     *
-     * @param state The transition State as returned from {@link #beginTransition(
-     * android.util.SparseArray, android.util.SparseArray, boolean)}.
-     * @param inFragment The last fragment to be added.
-     * @param isBack true if this is popping the back stack or false if this is a
-     *               forward operation.
-     */
-    private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state,
-            Fragment inFragment, boolean isBack) {
-        ArrayMap<String, View> namedViews = new ArrayMap<String, View>();
-        View root = inFragment.getView();
-        if (root != null) {
-            if (mSharedElementSourceNames != null) {
-                FragmentTransitionCompat21.findNamedViews(namedViews, root);
-                if (isBack) {
-                    namedViews = remapNames(mSharedElementSourceNames,
-                            mSharedElementTargetNames, namedViews);
-                } else {
-                    namedViews.retainAll(mSharedElementTargetNames);
-                }
-            }
-        }
-        return namedViews;
-    }
-
-    private void excludeHiddenFragmentsAfterEnter(final View sceneRoot, final TransitionState state,
-            final int containerId, final Object transition) {
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                excludeHiddenFragments(state, containerId, transition);
-                return true;
-            }
-        });
-    }
-
-    void excludeHiddenFragments(TransitionState state, int containerId, Object transition) {
-        if (mManager.mAdded != null) {
-            for (int i = 0; i < mManager.mAdded.size(); i++) {
-                Fragment fragment = mManager.mAdded.get(i);
-                if (fragment.mView != null && fragment.mContainer != null &&
-                        fragment.mContainerId == containerId) {
-                    if (fragment.mHidden) {
-                        if (!state.hiddenFragmentViews.contains(fragment.mView)) {
-                            FragmentTransitionCompat21.excludeTarget(transition, fragment.mView,
-                                    true);
-                            state.hiddenFragmentViews.add(fragment.mView);
-                        }
-                    } else {
-                        FragmentTransitionCompat21.excludeTarget(transition, fragment.mView,
-                                false);
-                        state.hiddenFragmentViews.remove(fragment.mView);
-                    }
-                }
-            }
-        }
-    }
-
-    private static void setNameOverride(ArrayMap<String, String> overrides,
-            String source, String target) {
-        if (source != null && target != null) {
-            for (int index = 0; index < overrides.size(); index++) {
-                if (source.equals(overrides.valueAt(index))) {
-                    overrides.setValueAt(index, target);
-                    return;
-                }
-            }
-            overrides.put(source, target);
-        }
-    }
-
-    private static void setNameOverrides(TransitionState state, ArrayList<String> sourceNames,
-            ArrayList<String> targetNames) {
-        if (sourceNames != null) {
-            for (int i = 0; i < sourceNames.size(); i++) {
-                String source = sourceNames.get(i);
-                String target = targetNames.get(i);
-                setNameOverride(state.nameOverrides, source, target);
-            }
-        }
-    }
-
-    private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
-            boolean isEnd) {
-        int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
-        for (int i = 0; i < count; i++) {
-            String source = mSharedElementSourceNames.get(i);
-            String originalTarget = mSharedElementTargetNames.get(i);
-            View view = namedViews.get(originalTarget);
-            if (view != null) {
-                String target = FragmentTransitionCompat21.getTransitionName(view);
-                if (isEnd) {
-                    setNameOverride(state.nameOverrides, source, target);
-                } else {
-                    setNameOverride(state.nameOverrides, target, source);
-                }
-            }
-        }
-    }
-
-    private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
-            boolean isEnd) {
-        int count = namedViews.size();
-        for (int i = 0; i < count; i++) {
-            String source = namedViews.keyAt(i);
-            String target = FragmentTransitionCompat21.getTransitionName(namedViews.valueAt(i));
-            if (isEnd) {
-                setNameOverride(state.nameOverrides, source, target);
-            } else {
-                setNameOverride(state.nameOverrides, target, source);
-            }
-        }
-    }
-
-    public class TransitionState {
-        public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>();
-        public ArrayList<View> hiddenFragmentViews = new ArrayList<View>();
-
-        public FragmentTransitionCompat21.EpicenterView enteringEpicenterView =
-                new FragmentTransitionCompat21.EpicenterView();
-        public View nonExistentView;
+        return mOps.isEmpty();
     }
 }
diff --git a/fragment/java/android/support/v4/app/Fragment.java b/fragment/java/android/support/v4/app/Fragment.java
index f9fcc45..8613435 100644
--- a/fragment/java/android/support/v4/app/Fragment.java
+++ b/fragment/java/android/support/v4/app/Fragment.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.app.Activity;
 import android.content.ComponentCallbacks;
 import android.content.Context;
@@ -24,6 +26,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.CallSuper;
@@ -52,8 +55,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 final class FragmentState implements Parcelable {
     final String mClassName;
     final int mIndex;
@@ -193,15 +194,6 @@
     
     int mState = INITIALIZING;
     
-    // Non-null if the fragment's view hierarchy is currently animating away,
-    // meaning we need to wait a bit on completely destroying it.  This is the
-    // view that is animating.
-    View mAnimatingAway;
-
-    // If mAnimatingAway != null, this is the state we should move to once the
-    // animation is done.
-    int mStateAfterAnimating;
-
     // When instantiated from saved state, this is the saved state.
     Bundle mSavedFragmentState;
     SparseArray<Parcelable> mSavedViewState;
@@ -296,9 +288,6 @@
     // Used to verify that subclasses call through to super class.
     boolean mCalled;
     
-    // If app has requested a specific animation, this is the one to use.
-    int mNextAnim;
-    
     // The parent container of the fragment after dynamically added to UI.
     ViewGroup mContainer;
     
@@ -319,17 +308,18 @@
     boolean mLoadersStarted;
     boolean mCheckedForLoaderManager;
 
-    Object mEnterTransition = null;
-    Object mReturnTransition = USE_DEFAULT_TRANSITION;
-    Object mExitTransition = null;
-    Object mReenterTransition = USE_DEFAULT_TRANSITION;
-    Object mSharedElementEnterTransition = null;
-    Object mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
-    Boolean mAllowReturnTransitionOverlap;
-    Boolean mAllowEnterTransitionOverlap;
+    // The animation and transition information for the fragment. This will be null
+    // unless the elements are explicitly accessed and should remain null for Fragments
+    // without Views.
+    AnimationInfo mAnimationInfo;
 
-    SharedElementCallback mEnterTransitionCallback = null;
-    SharedElementCallback mExitTransitionCallback = null;
+    // True if the View was added, and its animation has yet to be run. This could
+    // also indicate that the fragment view hasn't been made visible, even if there is no
+    // animation for this fragment.
+    boolean mIsNewlyAdded;
+
+    // True if mHidden has been changed and the animation should be scheduled.
+    boolean mHiddenChanged;
 
     /**
      * State information that has been retrieved from a fragment instance
@@ -1702,7 +1692,7 @@
      *                 when added not as a pop from the back stack.
      */
     public void setEnterSharedElementCallback(SharedElementCallback callback) {
-        mEnterTransitionCallback = callback;
+        ensureAnimationInfo().mEnterTransitionCallback = callback;
     }
 
     /**
@@ -1713,7 +1703,7 @@
      *                 when added as a pop from the back stack.
      */
     public void setExitSharedElementCallback(SharedElementCallback callback) {
-        mExitTransitionCallback = callback;
+        ensureAnimationInfo().mExitTransitionCallback = callback;
     }
 
     /**
@@ -1727,7 +1717,7 @@
      * @param transition The Transition to use to move Views into the initial Scene.
      */
     public void setEnterTransition(Object transition) {
-        mEnterTransition = transition;
+        ensureAnimationInfo().mEnterTransition = transition;
     }
 
     /**
@@ -1740,7 +1730,10 @@
      * @return the Transition to use to move Views into the initial Scene.
      */
     public Object getEnterTransition() {
-        return mEnterTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mEnterTransition;
     }
 
     /**
@@ -1758,7 +1751,7 @@
      *                   android.transition.Transition.
      */
     public void setReturnTransition(Object transition) {
-        mReturnTransition = transition;
+        ensureAnimationInfo().mReturnTransition = transition;
     }
 
     /**
@@ -1774,8 +1767,11 @@
      *         is preparing to close.
      */
     public Object getReturnTransition() {
-        return mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
-                : mReturnTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
+                : mAnimationInfo.mReturnTransition;
     }
 
     /**
@@ -1792,7 +1788,7 @@
      *                   must be an android.transition.Transition.
      */
     public void setExitTransition(Object transition) {
-        mExitTransition = transition;
+        ensureAnimationInfo().mExitTransition = transition;
     }
 
     /**
@@ -1808,7 +1804,10 @@
      *         is being closed not due to popping the back stack.
      */
     public Object getExitTransition() {
-        return mExitTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mExitTransition;
     }
 
     /**
@@ -1825,7 +1824,7 @@
      *                   must be an android.transition.Transition.
      */
     public void setReenterTransition(Object transition) {
-        mReenterTransition = transition;
+        ensureAnimationInfo().mReenterTransition = transition;
     }
 
     /**
@@ -1841,8 +1840,11 @@
      *                   previously-started Activity.
      */
     public Object getReenterTransition() {
-        return mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
-                : mReenterTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
+                : mAnimationInfo.mReenterTransition;
     }
 
     /**
@@ -1855,7 +1857,7 @@
      *                   Scene.  <code>transition</code> must be an android.transition.Transition.
      */
     public void setSharedElementEnterTransition(Object transition) {
-        mSharedElementEnterTransition = transition;
+        ensureAnimationInfo().mSharedElementEnterTransition = transition;
     }
 
     /**
@@ -1868,7 +1870,10 @@
      *                   Scene.
      */
     public Object getSharedElementEnterTransition() {
-        return mSharedElementEnterTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mSharedElementEnterTransition;
     }
 
     /**
@@ -1884,7 +1889,7 @@
      *                   Scene. <code>transition</code> must be an android.transition.Transition.
      */
     public void setSharedElementReturnTransition(Object transition) {
-        mSharedElementReturnTransition = transition;
+        ensureAnimationInfo().mSharedElementReturnTransition = transition;
     }
 
     /**
@@ -1900,8 +1905,12 @@
      *                   Scene.
      */
     public Object getSharedElementReturnTransition() {
-        return mSharedElementReturnTransition == USE_DEFAULT_TRANSITION ?
-                getSharedElementEnterTransition() : mSharedElementReturnTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mSharedElementReturnTransition == USE_DEFAULT_TRANSITION
+                ? getSharedElementEnterTransition()
+                : mAnimationInfo.mSharedElementReturnTransition;
     }
 
     /**
@@ -1913,7 +1922,7 @@
      *              wait until the exiting transition completes.
      */
     public void setAllowEnterTransitionOverlap(boolean allow) {
-        mAllowEnterTransitionOverlap = allow;
+        ensureAnimationInfo().mAllowEnterTransitionOverlap = allow;
     }
 
     /**
@@ -1925,7 +1934,8 @@
      * when it should wait until the exiting transition completes.
      */
     public boolean getAllowEnterTransitionOverlap() {
-        return (mAllowEnterTransitionOverlap == null) ? true : mAllowEnterTransitionOverlap;
+        return (mAnimationInfo == null || mAnimationInfo.mAllowEnterTransitionOverlap == null)
+                ? true : mAnimationInfo.mAllowEnterTransitionOverlap;
     }
 
     /**
@@ -1937,7 +1947,7 @@
      *              return transition completes.
      */
     public void setAllowReturnTransitionOverlap(boolean allow) {
-        mAllowReturnTransitionOverlap = allow;
+        ensureAnimationInfo().mAllowReturnTransitionOverlap = allow;
     }
 
     /**
@@ -1949,7 +1959,83 @@
      *         return transition completes.
      */
     public boolean getAllowReturnTransitionOverlap() {
-        return (mAllowReturnTransitionOverlap == null) ? true : mAllowReturnTransitionOverlap;
+        return (mAnimationInfo == null || mAnimationInfo.mAllowReturnTransitionOverlap == null)
+                ? true : mAnimationInfo.mAllowReturnTransitionOverlap;
+    }
+
+    /**
+     * Postpone the entering Fragment transition until {@link #startPostponedEnterTransition()}
+     * or {@link FragmentManager#executePendingTransactions()} has been called.
+     * <p>
+     * This method gives the Fragment the ability to delay Fragment animations
+     * until all data is loaded. Until then, the added, shown, and
+     * attached Fragments will be INVISIBLE and removed, hidden, and detached Fragments won't
+     * be have their Views removed. The transaction runs when all postponed added Fragments in the
+     * transaction have called {@link #startPostponedEnterTransition()}.
+     * <p>
+     * This method should be called before being added to the FragmentTransaction or
+     * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}.
+     * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to
+     * start the transitions.
+     * <p>
+     * When a FragmentTransaction is started that may affect a postponed FragmentTransaction,
+     * based on which containers are in their operations, the postponed FragmentTransaction
+     * will have its start triggered. The early triggering may result in faulty or nonexistent
+     * animations in the postponed transaction. FragmentTransactions that operate only on
+     * independent containers will not interfere with each other's postponement.
+     * <p>
+     * Calling postponeEnterTransition on Fragments with a null View will not postpone the
+     * transition. Likewise, postponement only works if FragmentTransaction optimizations are
+     * enabled.
+     *
+     * @see Activity#postponeEnterTransition()
+     * @see FragmentTransaction#setAllowOptimization(boolean)
+     */
+    public void postponeEnterTransition() {
+        ensureAnimationInfo().mEnterTransitionPostponed = true;
+    }
+
+    /**
+     * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
+     * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
+     * or {@link FragmentManager#executePendingTransactions()} to complete the FragmentTransaction.
+     * If postponement was interrupted with {@link FragmentManager#executePendingTransactions()},
+     * before {@code startPostponedEnterTransition()}, animations may not run or may execute
+     * improperly.
+     *
+     * @see Activity#startPostponedEnterTransition()
+     */
+    public void startPostponedEnterTransition() {
+        if (mFragmentManager == null || mFragmentManager.mHost == null) {
+            ensureAnimationInfo().mEnterTransitionPostponed = false;
+        } else if (Looper.myLooper() != mFragmentManager.mHost.getHandler().getLooper()) {
+            mFragmentManager.mHost.getHandler().postAtFrontOfQueue(new Runnable() {
+                @Override
+                public void run() {
+                    callStartTransitionListener();
+                }
+            });
+        } else {
+            callStartTransitionListener();
+        }
+    }
+
+    /**
+     * Calls the start transition listener. This must be called on the UI thread.
+     */
+    private void callStartTransitionListener() {
+        final OnStartEnterTransitionListener listener;
+        if (mAnimationInfo == null) {
+            listener = null;
+        } else {
+            mAnimationInfo.mEnterTransitionPostponed = false;
+            listener = mAnimationInfo.mStartEnterTransitionListener;
+            mAnimationInfo.mStartEnterTransitionListener = null;
+        }
+        if (listener != null) {
+            listener.onStartEnterTransition();
+        }
     }
 
     /**
@@ -2010,8 +2096,8 @@
                     writer.print(" mTargetRequestCode=");
                     writer.println(mTargetRequestCode);
         }
-        if (mNextAnim != 0) {
-            writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim);
+        if (getNextAnim() != 0) {
+            writer.print(prefix); writer.print("mNextAnim="); writer.println(getNextAnim());
         }
         if (mContainer != null) {
             writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
@@ -2022,10 +2108,13 @@
         if (mInnerView != null) {
             writer.print(prefix); writer.print("mInnerView="); writer.println(mView);
         }
-        if (mAnimatingAway != null) {
-            writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway);
-            writer.print(prefix); writer.print("mStateAfterAnimating=");
-                    writer.println(mStateAfterAnimating);
+        if (getAnimatingAway() != null) {
+            writer.print(prefix);
+            writer.print("mAnimatingAway=");
+            writer.println(getAnimatingAway());
+            writer.print(prefix);
+            writer.print("mStateAfterAnimating=");
+            writer.println(getStateAfterAnimating());
         }
         if (mLoaderManager != null) {
             writer.print(prefix); writer.println("Loader Manager:");
@@ -2355,4 +2444,164 @@
         }
     }
 
+    void setOnStartEnterTransitionListener(OnStartEnterTransitionListener listener) {
+        ensureAnimationInfo();
+        if (listener == mAnimationInfo.mStartEnterTransitionListener) {
+            return;
+        }
+        if (listener != null && mAnimationInfo.mStartEnterTransitionListener != null) {
+            throw new IllegalStateException("Trying to set a replacement "
+                    + "startPostponedEnterTransition on " + this);
+        }
+        if (mAnimationInfo.mEnterTransitionPostponed) {
+            mAnimationInfo.mStartEnterTransitionListener = listener;
+        }
+        if (listener != null) {
+            listener.startListening();
+        }
+    }
+
+    private AnimationInfo ensureAnimationInfo() {
+        if (mAnimationInfo == null) {
+            mAnimationInfo = new AnimationInfo();
+        }
+        return mAnimationInfo;
+    }
+
+    int getNextAnim() {
+        if (mAnimationInfo == null) {
+            return 0;
+        }
+        return mAnimationInfo.mNextAnim;
+    }
+
+    void setNextAnim(int animResourceId) {
+        if (mAnimationInfo == null && animResourceId == 0) {
+            return; // no change!
+        }
+        ensureAnimationInfo().mNextAnim = animResourceId;
+    }
+
+    int getNextTransition() {
+        if (mAnimationInfo == null) {
+            return 0;
+        }
+        return mAnimationInfo.mNextTransition;
+    }
+
+    void setNextTransition(int nextTransition, int nextTransitionStyle) {
+        if (mAnimationInfo == null && nextTransition == 0 && nextTransitionStyle == 0) {
+            return; // no change!
+        }
+        ensureAnimationInfo();
+        mAnimationInfo.mNextTransition = nextTransition;
+        mAnimationInfo.mNextTransitionStyle = nextTransitionStyle;
+    }
+
+    int getNextTransitionStyle() {
+        if (mAnimationInfo == null) {
+            return 0;
+        }
+        return mAnimationInfo.mNextTransitionStyle;
+    }
+
+    SharedElementCallback getEnterTransitionCallback() {
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mEnterTransitionCallback;
+    }
+
+    SharedElementCallback getExitTransitionCallback() {
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mExitTransitionCallback;
+    }
+
+    View getAnimatingAway() {
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mAnimatingAway;
+    }
+
+    void setAnimatingAway(View view) {
+        ensureAnimationInfo().mAnimatingAway = view;
+    }
+
+    int getStateAfterAnimating() {
+        if (mAnimationInfo == null) {
+            return 0;
+        }
+        return mAnimationInfo.mStateAfterAnimating;
+    }
+
+    void setStateAfterAnimating(int state) {
+        ensureAnimationInfo().mStateAfterAnimating = state;
+    }
+
+    boolean isPostponed() {
+        if (mAnimationInfo == null) {
+            return false;
+        }
+        return mAnimationInfo.mEnterTransitionPostponed;
+    }
+
+    /**
+     * Used internally to be notified when {@link #startPostponedEnterTransition()} has
+     * been called. This listener will only be called once and then be removed from the
+     * listeners.
+     */
+    interface OnStartEnterTransitionListener {
+        void onStartEnterTransition();
+        void startListening();
+    }
+
+    /**
+     * Contains all the animation and transition information for a fragment. This will only
+     * be instantiated for Fragments that have Views.
+     */
+    static class AnimationInfo {
+        // Non-null if the fragment's view hierarchy is currently animating away,
+        // meaning we need to wait a bit on completely destroying it.  This is the
+        // view that is animating.
+        View mAnimatingAway;
+
+        // If mAnimatingAway != null, this is the state we should move to once the
+        // animation is done.
+        int mStateAfterAnimating;
+
+        // If app has requested a specific animation, this is the one to use.
+        int mNextAnim;
+
+        // If app has requested a specific transition, this is the one to use.
+        int mNextTransition;
+
+        // If app has requested a specific transition style, this is the one to use.
+        int mNextTransitionStyle;
+
+        private Object mEnterTransition = null;
+        private Object mReturnTransition = USE_DEFAULT_TRANSITION;
+        private Object mExitTransition = null;
+        private Object mReenterTransition = USE_DEFAULT_TRANSITION;
+        private Object mSharedElementEnterTransition = null;
+        private Object mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
+        private Boolean mAllowReturnTransitionOverlap;
+        private Boolean mAllowEnterTransitionOverlap;
+
+        SharedElementCallback mEnterTransitionCallback = null;
+        SharedElementCallback mExitTransitionCallback = null;
+
+        // True when postponeEnterTransition has been called and startPostponeEnterTransition
+        // hasn't been called yet.
+        boolean mEnterTransitionPostponed;
+
+        // Listener to wait for startPostponeEnterTransition. After being called, it will
+        // be set to null
+        OnStartEnterTransitionListener mStartEnterTransitionListener;
+
+        // True if the View was added, and its animation has yet to be run.
+        boolean mIsNewlyAdded;
+    }
 }
diff --git a/fragment/java/android/support/v4/app/FragmentActivity.java b/fragment/java/android/support/v4/app/FragmentActivity.java
index 4542962..50fa584 100644
--- a/fragment/java/android/support/v4/app/FragmentActivity.java
+++ b/fragment/java/android/support/v4/app/FragmentActivity.java
@@ -16,12 +16,15 @@
 
 package android.support.v4.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -45,8 +48,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Base class for activities that want to use the support-based
  * {@link android.support.v4.app.Fragment} and
@@ -85,9 +86,6 @@
     static final String REQUEST_FRAGMENT_WHO_TAG = "android:support:request_fragment_who";
     static final int MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS = 0xffff - 1;
 
-    // This is the SDK API version of Honeycomb (3.0).
-    private static final int HONEYCOMB = 11;
-
     static final int MSG_REALLY_STOPPED = 1;
     static final int MSG_RESUME_PENDING = 2;
 
@@ -361,7 +359,7 @@
         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             boolean show = super.onCreatePanelMenu(featureId, menu);
             show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
-            if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+            if (android.os.Build.VERSION.SDK_INT >= 11) {
                 return show;
             }
             // Prior to Honeycomb, the framework can't invalidate the options
@@ -658,7 +656,7 @@
      * onPrepareOptionsMenu the next time the menu is requested.
      */
     public void supportInvalidateOptionsMenu() {
-        if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+        if (android.os.Build.VERSION.SDK_INT >= 11) {
             // If we are running on HC or greater, we can use the framework
             // API to invalidate the options menu.
             ActivityCompatHoneycomb.invalidateOptionsMenu(this);
@@ -681,7 +679,7 @@
      * @param args additional arguments to the dump request.
      */
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) {
+        if (Build.VERSION.SDK_INT >= 11) {
             // XXX This can only work if we can call the super-class impl. :/
             //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args);
         }
diff --git a/fragment/java/android/support/v4/app/FragmentHostCallback.java b/fragment/java/android/support/v4/app/FragmentHostCallback.java
index 75fde03..dad181a 100644
--- a/fragment/java/android/support/v4/app/FragmentHostCallback.java
+++ b/fragment/java/android/support/v4/app/FragmentHostCallback.java
@@ -55,7 +55,8 @@
     private boolean mLoadersStarted;
 
     public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
-        this(null /*activity*/, context, handler, windowAnimations);
+        this(context instanceof Activity ? (Activity) context : null, context, handler,
+                windowAnimations);
     }
 
     FragmentHostCallback(FragmentActivity activity) {
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index 3baf4f9..ac217ff 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -16,13 +16,14 @@
 
 package android.support.v4.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -38,20 +39,20 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.Animation.AnimationListener;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.ScaleAnimation;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -60,8 +61,6 @@
 import java.util.Arrays;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Static library support version of the framework's {@link android.app.FragmentManager}.
  * Used to write apps that run on platforms prior to Android 3.0.  When running
@@ -176,6 +175,9 @@
      * {@link FragmentTransaction#commitNow()} instead. This can help avoid
      * unwanted side effects when other code in your app has pending committed
      * transactions that expect different timing.</p>
+     * <p>
+     * This also forces the start of any postponed Transactions where
+     * {@link Fragment#postponeEnterTransition()} has been called.
      *
      * @return Returns true if there were any pending transactions to be
      * executed.
@@ -223,7 +225,7 @@
     /**
      * Like {@link #popBackStack()}, but performs the operation immediately
      * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards.
+     * afterwards without forcing the start of postponed Transactions.
      * @return Returns true if there was something popped, else false.
      */
     public abstract boolean popBackStackImmediate();
@@ -246,7 +248,7 @@
     /**
      * Like {@link #popBackStack(String, int)}, but performs the operation immediately
      * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards.
+     * afterwards without forcing the start of postponed Transactions.
      * @return Returns true if there was something popped, else false.
      */
     public abstract boolean popBackStackImmediate(String name, int flags);
@@ -270,7 +272,7 @@
     /**
      * Like {@link #popBackStack(int, int)}, but performs the operation immediately
      * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards.
+     * afterwards without forcing the start of postponed Transactions.
      * @return Returns true if there was something popped, else false.
      */
     public abstract boolean popBackStackImmediate(int id, int flags);
@@ -501,7 +503,7 @@
         }
     }
 
-    ArrayList<Runnable> mPendingActions;
+    ArrayList<OpGenerator> mPendingActions;
     Runnable[] mTmpActions;
     boolean mExecutingActions;
 
@@ -531,10 +533,18 @@
     String mNoTransactionsBecause;
     boolean mHavePendingDeferredStart;
 
+    // Temporary vars for optimizing execution of BackStackRecords:
+    ArrayList<BackStackRecord> mTmpRecords;
+    ArrayList<Boolean> mTmpIsPop;
+    ArrayList<Fragment> mTmpAddedFragments;
+
     // Temporary vars for state save and restore.
     Bundle mStateBundle = null;
     SparseArray<Parcelable> mStateArray = null;
 
+    // Postponed transactions.
+    ArrayList<StartEnterTransitionListener> mPostponedTransactions;
+
     Runnable mExecCommit = new Runnable() {
         @Override
         public void run() {
@@ -591,39 +601,31 @@
 
     @Override
     public boolean executePendingTransactions() {
-        return execPendingActions();
+        boolean updates = execPendingActions();
+        forcePostponedTransactions();
+        return updates;
     }
 
     @Override
     public void popBackStack() {
-        enqueueAction(new Runnable() {
-            @Override public void run() {
-                popBackStackState(mHost.getHandler(), null, -1, 0);
-            }
-        }, false);
+        enqueueAction(new PopBackStackState(null, -1, 0), false);
     }
 
     @Override
     public boolean popBackStackImmediate() {
         checkStateLoss();
-        executePendingTransactions();
-        return popBackStackState(mHost.getHandler(), null, -1, 0);
+        return popBackStackImmediate(null, -1, 0);
     }
 
     @Override
     public void popBackStack(final String name, final int flags) {
-        enqueueAction(new Runnable() {
-            @Override public void run() {
-                popBackStackState(mHost.getHandler(), name, -1, flags);
-            }
-        }, false);
+        enqueueAction(new PopBackStackState(name, -1, flags), false);
     }
 
     @Override
     public boolean popBackStackImmediate(String name, int flags) {
         checkStateLoss();
-        executePendingTransactions();
-        return popBackStackState(mHost.getHandler(), name, -1, flags);
+        return popBackStackImmediate(name, -1, flags);
     }
 
     @Override
@@ -631,21 +633,42 @@
         if (id < 0) {
             throw new IllegalArgumentException("Bad id: " + id);
         }
-        enqueueAction(new Runnable() {
-            @Override public void run() {
-                popBackStackState(mHost.getHandler(), null, id, flags);
-            }
-        }, false);
+        enqueueAction(new PopBackStackState(null, id, flags), false);
     }
 
     @Override
     public boolean popBackStackImmediate(int id, int flags) {
         checkStateLoss();
-        executePendingTransactions();
+        execPendingActions();
         if (id < 0) {
             throw new IllegalArgumentException("Bad id: " + id);
         }
-        return popBackStackState(mHost.getHandler(), null, id, flags);
+        return popBackStackImmediate(null, id, flags);
+    }
+
+    /**
+     * Used by all public popBackStackImmediate methods, this executes pending transactions and
+     * returns true if the pop action did anything, regardless of what other pending
+     * transactions did.
+     *
+     * @return true if the pop operation did anything or false otherwise.
+     */
+    private boolean popBackStackImmediate(String name, int id, int flags) {
+        execPendingActions();
+        ensureExecReady(true);
+
+        boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags);
+        if (executePop) {
+            mExecutingActions = true;
+            try {
+                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+            } finally {
+                cleanupExec();
+            }
+        }
+
+        doPendingDeferredStart();
+        return executePop;
     }
 
     @Override
@@ -821,7 +844,7 @@
             if (N > 0) {
                 writer.print(prefix); writer.println("Pending Actions:");
                 for (int i=0; i<N; i++) {
-                    Runnable r = mPendingActions.get(i);
+                    OpGenerator r = mPendingActions.get(i);
                     writer.print(prefix); writer.print("  #"); writer.print(i);
                             writer.print(": "); writer.println(r);
                 }
@@ -882,14 +905,14 @@
 
     Animation loadAnimation(Fragment fragment, int transit, boolean enter,
             int transitionStyle) {
-        Animation animObj = fragment.onCreateAnimation(transit, enter,
-                fragment.mNextAnim);
+        Animation animObj = fragment.onCreateAnimation(transit, enter, fragment.getNextAnim());
         if (animObj != null) {
             return animObj;
         }
 
-        if (fragment.mNextAnim != 0) {
-            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(), fragment.mNextAnim);
+        if (fragment.getNextAnim() != 0) {
+            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(),
+                    fragment.getNextAnim());
             if (anim != null) {
                 return anim;
             }
@@ -1010,13 +1033,13 @@
             if (f.mFromLayout && !f.mInLayout) {
                 return;
             }
-            if (f.mAnimatingAway != null) {
+            if (f.getAnimatingAway() != null) {
                 // The fragment is currently being animated...  but!  Now we
                 // want to move our state back up.  Give up on waiting for the
                 // animation, move to whatever the final state should be once
                 // the animation is done, and then we can proceed from there.
-                f.mAnimatingAway = null;
-                moveToState(f, f.mStateAfterAnimating, 0, 0, true);
+                f.setAnimatingAway(null);
+                moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
             }
             switch (f.mState) {
                 case Fragment.INITIALIZING:
@@ -1120,15 +1143,13 @@
                                     f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                                 }
                                 if (container != null) {
-                                    Animation anim = loadAnimation(f, transit, true,
-                                            transitionStyle);
-                                    if (anim != null) {
-                                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
-                                        f.mView.startAnimation(anim);
-                                    }
                                     container.addView(f.mView);
+                                    f.mIsNewlyAdded = true;
                                 }
-                                if (f.mHidden) f.mView.setVisibility(View.GONE);
+                                if (f.mHidden) {
+                                    f.mView.setVisibility(View.GONE);
+                                    f.mIsNewlyAdded = false; // No animation
+                                }
                                 f.onViewCreated(f.mView, f.mSavedFragmentState);
                             } else {
                                 f.mInnerView = null;
@@ -1188,23 +1209,24 @@
                         f.performDestroyView();
                         if (f.mView != null && f.mContainer != null) {
                             Animation anim = null;
-                            if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
+                            if (mCurState > Fragment.INITIALIZING && !mDestroyed
+                                    && f.mView.getVisibility() == View.VISIBLE) {
                                 anim = loadAnimation(f, transit, false,
                                         transitionStyle);
                             }
                             if (anim != null) {
                                 final Fragment fragment = f;
-                                f.mAnimatingAway = f.mView;
-                                f.mStateAfterAnimating = newState;
+                                f.setAnimatingAway(f.mView);
+                                f.setStateAfterAnimating(newState);
                                 final View viewToAnimate = f.mView;
                                 anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                         viewToAnimate, anim) {
                                     @Override
                                     public void onAnimationEnd(Animation animation) {
                                         super.onAnimationEnd(animation);
-                                        if (fragment.mAnimatingAway != null) {
-                                            fragment.mAnimatingAway = null;
-                                            moveToState(fragment, fragment.mStateAfterAnimating,
+                                        if (fragment.getAnimatingAway() != null) {
+                                            fragment.setAnimatingAway(null);
+                                            moveToState(fragment, fragment.getStateAfterAnimating(),
                                                     0, 0, false);
                                         }
                                     }
@@ -1220,24 +1242,24 @@
                 case Fragment.CREATED:
                     if (newState < Fragment.CREATED) {
                         if (mDestroyed) {
-                            if (f.mAnimatingAway != null) {
+                            if (f.getAnimatingAway() != null) {
                                 // The fragment's containing activity is
                                 // being destroyed, but this fragment is
                                 // currently animating away.  Stop the
                                 // animation right now -- it is not needed,
                                 // and we can't wait any more on destroying
                                 // the fragment.
-                                View v = f.mAnimatingAway;
-                                f.mAnimatingAway = null;
+                                View v = f.getAnimatingAway();
+                                f.setAnimatingAway(null);
                                 v.clearAnimation();
                             }
                         }
-                        if (f.mAnimatingAway != null) {
+                        if (f.getAnimatingAway() != null) {
                             // We are waiting for the fragment's view to finish
                             // animating away.  Just make a note of the state
                             // the fragment now should move to once the animation
                             // is done.
-                            f.mStateAfterAnimating = newState;
+                            f.setStateAfterAnimating(newState);
                             newState = Fragment.CREATED;
                         } else {
                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
@@ -1273,26 +1295,117 @@
         moveToState(f, mCurState, 0, 0, false);
     }
 
-    void moveToState(int newState, boolean always) {
-        moveToState(newState, 0, 0, always);
+    /**
+     * Fragments that have been shown or hidden don't have their visibility changed or
+     * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)}
+     * calls. After fragments are brought to their final state in
+     * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
+     * hidden must have their visibility changed and their animations started here.
+     *
+     * @param fragment The fragment with mHiddenChanged = true that should change its View's
+     *                 visibility and start the show or hide animation.
+     */
+    void completeShowHideFragment(final Fragment fragment) {
+        if (fragment.mView != null) {
+            Animation anim = loadAnimation(fragment, fragment.getNextTransition(),
+                    !fragment.mHidden, fragment.getNextTransitionStyle());
+            if (anim != null) {
+                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+                fragment.mView.startAnimation(anim);
+                setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
+                anim.start();
+            }
+            final int visibility = fragment.mHidden ? View.GONE : View.VISIBLE;
+            fragment.mView.setVisibility(visibility);
+        }
+        if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
+            mNeedMenuInvalidate = true;
+        }
+        fragment.mHiddenChanged = false;
+        fragment.onHiddenChanged(fragment.mHidden);
     }
 
-    void moveToState(int newState, int transit, int transitStyle, boolean always) {
-        if (mHost == null && newState != Fragment.INITIALIZING) {
-            throw new IllegalStateException("No host");
-        }
-
-        if (!always && mCurState == newState) {
+    /**
+     * Moves a fragment to its expected final state or the fragment manager's state, depending
+     * on whether the fragment manager's state is raised properly.
+     *
+     * @param f The fragment to change.
+     */
+    void moveFragmentToExpectedState(Fragment f) {
+        if (f == null) {
             return;
         }
+        int nextState = mCurState;
+        if (f.mRemoving) {
+            if (f.isInBackStack()) {
+                nextState = Fragment.CREATED;
+            } else {
+                nextState = Fragment.INITIALIZING;
+            }
+        }
+        moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
+
+        if (f.mView != null) {
+            // Move the view if it is out of order
+            Fragment underFragment = findFragmentUnder(f);
+            if (underFragment != null) {
+                final View underView = underFragment.mView;
+                // make sure this fragment is in the right order.
+                final ViewGroup container = f.mContainer;
+                int underIndex = container.indexOfChild(underView);
+                int viewIndex = container.indexOfChild(f.mView);
+                if (viewIndex < underIndex) {
+                    container.removeViewAt(viewIndex);
+                    container.addView(f.mView, underIndex);
+                }
+            }
+            if (f.mIsNewlyAdded && f.mContainer != null) {
+                // Make it visible and run the animations
+                f.mView.setVisibility(View.VISIBLE);
+                f.mIsNewlyAdded = false;
+                // run animations:
+                Animation anim = loadAnimation(f, f.getNextTransition(), true,
+                        f.getNextTransitionStyle());
+                if (anim != null) {
+                    setHWLayerAnimListenerIfAlpha(f.mView, anim);
+                    f.mView.startAnimation(anim);
+                }
+            }
+        }
+        if (f.mHiddenChanged) {
+            completeShowHideFragment(f);
+        }
+    }
+
+    void moveToState(int newState) {
+        if (mHost == null && newState != Fragment.INITIALIZING) {
+            throw new IllegalStateException("No activity");
+        }
 
         mCurState = newState;
+
         if (mActive != null) {
             boolean loadersRunning = false;
-            for (int i=0; i<mActive.size(); i++) {
+
+            // Must add them in the proper order. mActive fragments may be out of order
+            if (mAdded != null) {
+                final int numAdded = mAdded.size();
+                for (int i = 0; i < numAdded; i++) {
+                    Fragment f = mAdded.get(i);
+                    moveFragmentToExpectedState(f);
+                    if (f.mLoaderManager != null) {
+                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
+                    }
+                }
+            }
+
+            // Now iterate through all active fragments. These will include those that are removed
+            // and detached.
+            final int numActive = mActive.size();
+            for (int i = 0; i < numActive; i++) {
                 Fragment f = mActive.get(i);
-                if (f != null) {
-                    moveToState(f, newState, transit, transitStyle, false);
+                if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
+                    moveFragmentToExpectedState(f);
                     if (f.mLoaderManager != null) {
                         loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                     }
@@ -1368,6 +1481,9 @@
             mAdded.add(fragment);
             fragment.mAdded = true;
             fragment.mRemoving = false;
+            if (fragment.mView == null) {
+                fragment.mHiddenChanged = false;
+            }
             if (fragment.mHasMenu && fragment.mMenuVisible) {
                 mNeedMenuInvalidate = true;
             }
@@ -1377,7 +1493,7 @@
         }
     }
 
-    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
+    public void removeFragment(Fragment fragment) {
         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
         final boolean inactive = !fragment.isInBackStack();
         if (!fragment.mDetached || inactive) {
@@ -1389,52 +1505,42 @@
             }
             fragment.mAdded = false;
             fragment.mRemoving = true;
-            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
-                    transition, transitionStyle, false);
         }
     }
 
-    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
+    /**
+     * Marks a fragment as hidden to be later animated in with
+     * {@link #completeShowHideFragment(Fragment)}.
+     *
+     * @param fragment The fragment to be shown.
+     */
+    public void hideFragment(Fragment fragment) {
         if (DEBUG) Log.v(TAG, "hide: " + fragment);
         if (!fragment.mHidden) {
             fragment.mHidden = true;
-            if (fragment.mView != null) {
-                Animation anim = loadAnimation(fragment, transition, false,
-                        transitionStyle);
-                if (anim != null) {
-                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
-                    fragment.mView.startAnimation(anim);
-                }
-                fragment.mView.setVisibility(View.GONE);
-            }
-            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
-                mNeedMenuInvalidate = true;
-            }
-            fragment.onHiddenChanged(true);
+            // Toggle hidden changed so that if a fragment goes through show/hide/show
+            // it doesn't go through the animation.
+            fragment.mHiddenChanged = !fragment.mHiddenChanged;
         }
     }
 
-    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
+    /**
+     * Marks a fragment as shown to be later animated in with
+     * {@link #completeShowHideFragment(Fragment)}.
+     *
+     * @param fragment The fragment to be shown.
+     */
+    public void showFragment(Fragment fragment) {
         if (DEBUG) Log.v(TAG, "show: " + fragment);
         if (fragment.mHidden) {
             fragment.mHidden = false;
-            if (fragment.mView != null) {
-                Animation anim = loadAnimation(fragment, transition, true,
-                        transitionStyle);
-                if (anim != null) {
-                    setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
-                    fragment.mView.startAnimation(anim);
-                }
-                fragment.mView.setVisibility(View.VISIBLE);
-            }
-            if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
-                mNeedMenuInvalidate = true;
-            }
-            fragment.onHiddenChanged(false);
+            // Toggle hidden changed so that if a fragment goes through show/hide/show
+            // it doesn't go through the animation.
+            fragment.mHiddenChanged = !fragment.mHiddenChanged;
         }
     }
 
-    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+    public void detachFragment(Fragment fragment) {
         if (DEBUG) Log.v(TAG, "detach: " + fragment);
         if (!fragment.mDetached) {
             fragment.mDetached = true;
@@ -1448,12 +1554,11 @@
                     mNeedMenuInvalidate = true;
                 }
                 fragment.mAdded = false;
-                moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false);
             }
         }
     }
 
-    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+    public void attachFragment(Fragment fragment) {
         if (DEBUG) Log.v(TAG, "attach: " + fragment);
         if (fragment.mDetached) {
             fragment.mDetached = false;
@@ -1470,7 +1575,6 @@
                 if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
                 }
-                moveToState(fragment, mCurState, transition, transitionStyle, false);
             }
         }
     }
@@ -1551,7 +1655,7 @@
      * @param allowStateLoss whether to allow loss of state information
      * @throws IllegalStateException if the activity has been destroyed
      */
-    public void enqueueAction(Runnable action, boolean allowStateLoss) {
+    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
         if (!allowStateLoss) {
             checkStateLoss();
         }
@@ -1560,10 +1664,25 @@
                 throw new IllegalStateException("Activity has been destroyed");
             }
             if (mPendingActions == null) {
-                mPendingActions = new ArrayList<Runnable>();
+                mPendingActions = new ArrayList<>();
             }
             mPendingActions.add(action);
-            if (mPendingActions.size() == 1) {
+            scheduleCommit();
+        }
+    }
+
+    /**
+     * Schedules the execution when one hasn't been scheduled already. This should happen
+     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
+     * a postponed transaction has been started with
+     * {@link Fragment#startPostponedEnterTransition()}
+     */
+    private void scheduleCommit() {
+        synchronized (this) {
+            boolean postponeReady =
+                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
+            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
+            if (postponeReady || pendingReady) {
                 mHost.getHandler().removeCallbacks(mExecCommit);
                 mHost.getHandler().post(mExecCommit);
             }
@@ -1626,7 +1745,13 @@
         }
     }
 
-    public void execSingleAction(Runnable action, boolean allowStateLoss) {
+    /**
+     * Broken out from exec*, this prepares for gathering and executing operations.
+     *
+     * @param allowStateLoss true if state loss should be ignored or false if it should be
+     *                       checked.
+     */
+    private void ensureExecReady(boolean allowStateLoss) {
         if (mExecutingActions) {
             throw new IllegalStateException("FragmentManager is already executing transactions");
         }
@@ -1639,50 +1764,51 @@
             checkStateLoss();
         }
 
-        mExecutingActions = true;
-        action.run();
-        mExecutingActions = false;
+        if (mTmpRecords == null) {
+            mTmpRecords = new ArrayList<>();
+            mTmpIsPop = new ArrayList<>();
+        }
+        executePostponedTransaction(null, null);
+    }
+
+    public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
+        ensureExecReady(allowStateLoss);
+        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
+            mExecutingActions = true;
+            try {
+                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+            } finally {
+                cleanupExec();
+            }
+        }
 
         doPendingDeferredStart();
     }
 
     /**
+     * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
+     * used in executing operations.
+     */
+    private void cleanupExec() {
+        mExecutingActions = false;
+        mTmpIsPop.clear();
+        mTmpRecords.clear();
+    }
+
+    /**
      * Only call from main thread!
      */
     public boolean execPendingActions() {
-        if (mExecutingActions) {
-            throw new IllegalStateException("FragmentManager is already executing transactions");
-        }
-
-        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
-            throw new IllegalStateException("Must be called from main thread of fragment host");
-        }
+        ensureExecReady(true);
 
         boolean didSomething = false;
-
-        while (true) {
-            int numActions;
-
-            synchronized (this) {
-                if (mPendingActions == null || mPendingActions.size() == 0) {
-                    break;
-                }
-
-                numActions = mPendingActions.size();
-                if (mTmpActions == null || mTmpActions.length < numActions) {
-                    mTmpActions = new Runnable[numActions];
-                }
-                mPendingActions.toArray(mTmpActions);
-                mPendingActions.clear();
-                mHost.getHandler().removeCallbacks(mExecCommit);
-            }
-
+        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
             mExecutingActions = true;
-            for (int i=0; i<numActions; i++) {
-                mTmpActions[i].run();
-                mTmpActions[i] = null;
+            try {
+                optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
+            } finally {
+                cleanupExec();
             }
-            mExecutingActions = false;
             didSomething = true;
         }
 
@@ -1691,6 +1817,386 @@
         return didSomething;
     }
 
+    /**
+     * Complete the execution of transactions that have previously been postponed, but are
+     * now ready.
+     */
+    private void executePostponedTransaction(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isRecordPop) {
+        int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
+        for (int i = 0; i < numPostponed; i++) {
+            StartEnterTransitionListener listener = mPostponedTransactions.get(i);
+            if (records != null && !listener.mIsBack) {
+                int index = records.indexOf(listener.mRecord);
+                if (index != -1 && isRecordPop.get(index)) {
+                    listener.cancelTransaction();
+                    continue;
+                }
+            }
+            if (listener.isReady() || (records != null
+                    && listener.mRecord.interactsWith(records, 0, records.size()))) {
+                mPostponedTransactions.remove(i);
+                i--;
+                numPostponed--;
+                int index;
+                if (records != null && !listener.mIsBack
+                        && (index = records.indexOf(listener.mRecord)) != -1
+                        && isRecordPop.get(index)) {
+                    // This is popping a postponed transaction
+                    listener.cancelTransaction();
+                } else {
+                    listener.completeTransaction();
+                }
+            }
+        }
+    }
+
+    /**
+     * Optimizes BackStackRecord operations. This method merges operations of proximate records
+     * that allow optimization. See {@link FragmentTransaction#setAllowOptimization(boolean)}.
+     * <p>
+     * For example, a transaction that adds to the back stack and then another that pops that
+     * back stack record will be optimized.
+     * <p>
+     * Likewise, two transactions committed that are executed at the same time will be optimized
+     * as well as two pop operations executed together.
+     *
+     * @param records The records pending execution
+     * @param isRecordPop The direction that these records are being run.
+     */
+    private void optimizeAndExecuteOps(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isRecordPop) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+
+        if (isRecordPop == null || records.size() != isRecordPop.size()) {
+            throw new IllegalStateException("Internal error with the back stack records");
+        }
+
+        // Force start of any postponed transactions that interact with scheduled transactions:
+        executePostponedTransaction(records, isRecordPop);
+
+        final int numRecords = records.size();
+        int startIndex = 0;
+        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
+            final boolean canOptimize = records.get(recordNum).mAllowOptimization;
+            if (!canOptimize) {
+                // execute all previous transactions
+                if (startIndex != recordNum) {
+                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
+                }
+                // execute all unoptimized together
+                int optimizeEnd;
+                for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
+                    if (records.get(optimizeEnd).mAllowOptimization) {
+                        break;
+                    }
+                }
+                executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
+                startIndex = optimizeEnd;
+                recordNum = optimizeEnd - 1;
+            }
+        }
+        if (startIndex != numRecords) {
+            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
+        }
+    }
+
+    /**
+     * Optimizes a subset of a list of BackStackRecords, all of which either allow optimization or
+     * do not allow optimization.
+     * @param records A list of BackStackRecords that are to be optimized
+     * @param isRecordPop The direction that these records are being run.
+     * @param startIndex The index of the first record in <code>records</code> to be optimized
+     * @param endIndex One more than the final record index in <code>records</code> to optimize.
+     */
+    private void executeOpsTogether(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+        final boolean allowOptimization = records.get(startIndex).mAllowOptimization;
+        boolean addToBackStack = false;
+        if (mTmpAddedFragments == null) {
+            mTmpAddedFragments = new ArrayList<>();
+        } else {
+            mTmpAddedFragments.clear();
+        }
+        if (mAdded != null) {
+            mTmpAddedFragments.addAll(mAdded);
+        }
+        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+            final BackStackRecord record = records.get(recordNum);
+            final boolean isPop = isRecordPop.get(recordNum);
+            if (!isPop) {
+                record.expandReplaceOps(mTmpAddedFragments);
+            }
+            final int bumpAmount = isPop ? -1 : 1;
+            record.bumpBackStackNesting(bumpAmount);
+            addToBackStack = addToBackStack || record.mAddToBackStack;
+        }
+        mTmpAddedFragments.clear();
+
+        if (!allowOptimization) {
+            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
+                    false);
+        }
+        executeOps(records, isRecordPop, startIndex, endIndex);
+
+        int postponeIndex = endIndex;
+        if (allowOptimization) {
+            moveFragmentsToInvisible();
+            postponeIndex = postponePostponableTransactions(records, isRecordPop,
+                    startIndex, endIndex);
+        }
+
+        if (postponeIndex != startIndex && allowOptimization) {
+            // need to run something now
+            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
+                    postponeIndex, true);
+            moveToState(mCurState);
+        }
+
+        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
+            final BackStackRecord record = records.get(recordNum);
+            final boolean isPop = isRecordPop.get(recordNum);
+            if (isPop && record.mIndex >= 0) {
+                freeBackStackIndex(record.mIndex);
+                record.mIndex = -1;
+            }
+        }
+        if (addToBackStack) {
+            reportBackStackChanged();
+        }
+    }
+
+    /**
+     * Examine all transactions and determine which ones are marked as postponed. Those will
+     * have their operations rolled back and moved to the end of the record list (up to endIndex).
+     * It will also add the postponed transaction to the queue.
+     *
+     * @param records A list of BackStackRecords that should be checked.
+     * @param isRecordPop The direction that these records are being run.
+     * @param startIndex The index of the first record in <code>records</code> to be checked
+     * @param endIndex One more than the final record index in <code>records</code> to be checked.
+     * @return The index of the first postponed transaction or endIndex if no transaction was
+     * postponed.
+     */
+    private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+        int postponeIndex = endIndex;
+        for (int i = endIndex - 1; i >= startIndex; i--) {
+            final BackStackRecord record = records.get(i);
+            final boolean isPop = isRecordPop.get(i);
+            boolean isPostponed = record.isPostponed()
+                    && !record.interactsWith(records, i + 1, endIndex);
+            if (isPostponed) {
+                if (mPostponedTransactions == null) {
+                    mPostponedTransactions = new ArrayList<>();
+                }
+                StartEnterTransitionListener listener =
+                        new StartEnterTransitionListener(record, isPop);
+                mPostponedTransactions.add(listener);
+                record.setOnStartPostponedListener(listener);
+
+                // roll back the transaction
+                if (isPop) {
+                    record.executeOps();
+                } else {
+                    record.executePopOps();
+                }
+
+                // move to the end
+                postponeIndex--;
+                if (i != postponeIndex) {
+                    records.remove(i);
+                    records.add(postponeIndex, record);
+                }
+
+                // different views may be visible now
+                moveFragmentsToInvisible();
+            }
+        }
+        return postponeIndex;
+    }
+
+    /**
+     * When a postponed transaction is ready to be started, this completes the transaction,
+     * removing, hiding, or showing views as well as starting the animations and transitions.
+     * <p>
+     * {@code runtransitions} is set to false when the transaction postponement was interrupted
+     * abnormally -- normally by a new transaction being started that affects the postponed
+     * transaction.
+     *
+     * @param record The transaction to run
+     * @param isPop true if record is popping or false if it is adding
+     * @param runTransitions true if the fragment transition should be run or false otherwise.
+     * @param moveToState true if the state should be changed after executing the operations.
+     *                    This is false when the transaction is canceled when a postponed
+     *                    transaction is popped.
+     */
+    private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions,
+            boolean moveToState) {
+        ArrayList<BackStackRecord> records = new ArrayList<>(1);
+        ArrayList<Boolean> isRecordPop = new ArrayList<>(1);
+        records.add(record);
+        isRecordPop.add(isPop);
+        executeOps(records, isRecordPop, 0, 1);
+        if (runTransitions) {
+            FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
+        }
+        if (moveToState) {
+            moveToState(mCurState);
+        } else if (mActive != null) {
+            final int numActive = mActive.size();
+            for (int i = 0; i < numActive; i++) {
+                // Allow added fragments to be removed during the pop since we aren't going
+                // to move them to the final state with moveToState(mCurState).
+                Fragment fragment = mActive.get(i);
+                if (fragment.mView != null && fragment.mIsNewlyAdded
+                        && record.interactsWith(fragment.mContainerId)) {
+                    fragment.mIsNewlyAdded = false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Find a fragment within the fragment's container whose View should be below the passed
+     * fragment. {@code null} is returned when the fragment has no View or if there should be
+     * no fragment with a View below the given fragment.
+     *
+     * As an example, if mAdded has two Fragments with Views sharing the same container:
+     * FragmentA
+     * FragmentB
+     *
+     * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
+     * had no View, null would be returned.
+     *
+     * @param f The fragment that may be on top of another fragment.
+     * @return The fragment with a View under f, if one exists or null if f has no View or
+     * there are no fragments with Views in the same container.
+     */
+    private Fragment findFragmentUnder(Fragment f) {
+        final ViewGroup container = f.mContainer;
+        final View view = f.mView;
+
+        if (container == null || view == null) {
+            return null;
+        }
+
+        final int fragmentIndex = mAdded.indexOf(f);
+        for (int i = fragmentIndex - 1; i >= 0; i--) {
+            Fragment underFragment = mAdded.get(i);
+            if (underFragment.mContainer == container && underFragment.mView != null) {
+                // Found the fragment under this one
+                return underFragment;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Run the operations in the BackStackRecords, either to push or pop.
+     *
+     * @param records The list of records whose operations should be run.
+     * @param isRecordPop The direction that these records are being run.
+     * @param startIndex The index of the first entry in records to run.
+     * @param endIndex One past the index of the final entry in records to run.
+     */
+    private static void executeOps(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
+        for (int i = startIndex; i < endIndex; i++) {
+            final BackStackRecord record = records.get(i);
+            final boolean isPop = isRecordPop.get(i);
+            if (isPop) {
+                record.executePopOps();
+            } else {
+                record.executeOps();
+            }
+        }
+    }
+
+    /**
+     * Ensure that fragments that are added are moved to at least the CREATED state.
+     * Any newly-added Views are made INVISIBLE so that the Transaction can be postponed
+     * with {@link Fragment#postponeEnterTransition()}.
+     */
+    private void moveFragmentsToInvisible() {
+        if (mCurState < Fragment.CREATED) {
+            return;
+        }
+        // We want to leave the fragment in the started state
+        final int state = Math.min(mCurState, Fragment.STARTED);
+        final int numAdded = mAdded == null ? 0 : mAdded.size();
+        for (int i = 0; i < numAdded; i++) {
+            Fragment fragment = mAdded.get(i);
+            if (fragment.mState < state) {
+                moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(),
+                        false);
+                if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
+                    fragment.mView.setVisibility(View.INVISIBLE);
+                }
+            }
+        }
+    }
+
+    /**
+     * Starts all postponed transactions regardless of whether they are ready or not.
+     */
+    private void forcePostponedTransactions() {
+        if (mPostponedTransactions != null) {
+            while (!mPostponedTransactions.isEmpty()) {
+                mPostponedTransactions.remove(0).completeTransaction();
+            }
+        }
+    }
+
+    /**
+     * Ends the animations of fragments so that they immediately reach the end state.
+     * This is used prior to saving the state so that the correct state is saved.
+     */
+    private void endAnimatingAwayFragments() {
+        final int numFragments = mActive == null ? 0 : mActive.size();
+        for (int i = 0; i < numFragments; i++) {
+            Fragment fragment = mActive.get(i);
+            if (fragment != null && fragment.getAnimatingAway() != null) {
+                // Give up waiting for the animation and just end it.
+                final int stateAfterAnimating = fragment.getStateAfterAnimating();
+                final View animatingAway = fragment.getAnimatingAway();
+                fragment.setAnimatingAway(null);
+                animatingAway.clearAnimation();
+                moveToState(fragment, stateAfterAnimating, 0, 0, false);
+            }
+        }
+    }
+
+    /**
+     * Adds all records in the pending actions to records and whether they are add or pop
+     * operations to isPop. After executing, the pending actions will be empty.
+     *
+     * @param records All pending actions will generate BackStackRecords added to this.
+     *                This contains the transactions, in order, to execute.
+     * @param isPop All pending actions will generate booleans to add to this. This contains
+     *              an entry for each entry in records to indicate whether or not it is a
+     *              pop action.
+     */
+    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
+            ArrayList<Boolean> isPop) {
+        int numActions;
+        synchronized (this) {
+            if (mPendingActions == null || mPendingActions.size() == 0) {
+                return false;
+            }
+
+            numActions = mPendingActions.size();
+            for (int i = 0; i < numActions; i++) {
+                mPendingActions.get(i).generateOps(records, isPop);
+            }
+            mPendingActions.clear();
+            mHost.getHandler().removeCallbacks(mExecCommit);
+        }
+        return numActions > 0;
+    }
+
     void doPendingDeferredStart() {
         if (mHavePendingDeferredStart) {
             boolean loadersRunning = false;
@@ -1724,23 +2230,18 @@
     }
 
     @SuppressWarnings("unused")
-    boolean popBackStackState(Handler handler, String name, int id, int flags) {
+    boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+            String name, int id, int flags) {
         if (mBackStack == null) {
             return false;
         }
-        if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
-            int last = mBackStack.size()-1;
+        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
+            int last = mBackStack.size() - 1;
             if (last < 0) {
                 return false;
             }
-            final BackStackRecord bss = mBackStack.remove(last);
-            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
-            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
-            if (mCurState >= Fragment.CREATED) {
-                bss.calculateBackFragments(firstOutFragments, lastInFragments);
-            }
-            bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
-            reportBackStackChanged();
+            records.add(mBackStack.remove(last));
+            isRecordPop.add(true);
         } else {
             int index = -1;
             if (name != null || id >= 0) {
@@ -1777,26 +2278,10 @@
             if (index == mBackStack.size()-1) {
                 return false;
             }
-            final ArrayList<BackStackRecord> states
-                    = new ArrayList<BackStackRecord>();
-            for (int i=mBackStack.size()-1; i>index; i--) {
-                states.add(mBackStack.remove(i));
+            for (int i = mBackStack.size() - 1; i > index; i--) {
+                records.add(mBackStack.remove(i));
+                isRecordPop.add(true);
             }
-            final int LAST = states.size()-1;
-            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
-            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
-            if (mCurState >= Fragment.CREATED) {
-                for (int i = 0; i <= LAST; i++) {
-                    states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
-                }
-            }
-            BackStackRecord.TransitionState state = null;
-            for (int i=0; i<=LAST; i++) {
-                if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
-                state = states.get(i).popFromBackStack(i == LAST, state,
-                        firstOutFragments, lastInFragments);
-            }
-            reportBackStackChanged();
         }
         return true;
     }
@@ -1895,6 +2380,8 @@
     Parcelable saveAllState() {
         // Make sure all pending operations have now been executed to get
         // our state update-to-date.
+        forcePostponedTransactions();
+        endAnimatingAwayFragments();
         execPendingActions();
 
         if (HONEYCOMB) {
@@ -2146,26 +2633,26 @@
 
     public void dispatchCreate() {
         mStateSaved = false;
-        moveToState(Fragment.CREATED, false);
+        moveToState(Fragment.CREATED);
     }
 
     public void dispatchActivityCreated() {
         mStateSaved = false;
-        moveToState(Fragment.ACTIVITY_CREATED, false);
+        moveToState(Fragment.ACTIVITY_CREATED);
     }
 
     public void dispatchStart() {
         mStateSaved = false;
-        moveToState(Fragment.STARTED, false);
+        moveToState(Fragment.STARTED);
     }
 
     public void dispatchResume() {
         mStateSaved = false;
-        moveToState(Fragment.RESUMED, false);
+        moveToState(Fragment.RESUMED);
     }
 
     public void dispatchPause() {
-        moveToState(Fragment.STARTED, false);
+        moveToState(Fragment.STARTED);
     }
 
     public void dispatchStop() {
@@ -2174,21 +2661,21 @@
         // them.
         mStateSaved = true;
 
-        moveToState(Fragment.STOPPED, false);
+        moveToState(Fragment.STOPPED);
     }
 
     public void dispatchReallyStop() {
-        moveToState(Fragment.ACTIVITY_CREATED, false);
+        moveToState(Fragment.ACTIVITY_CREATED);
     }
 
     public void dispatchDestroyView() {
-        moveToState(Fragment.CREATED, false);
+        moveToState(Fragment.CREATED);
     }
 
     public void dispatchDestroy() {
         mDestroyed = true;
         execPendingActions();
-        moveToState(Fragment.INITIALIZING, false);
+        moveToState(Fragment.INITIALIZING);
         mHost = null;
         mContainer = null;
         mParent = null;
@@ -2472,4 +2959,121 @@
         public static final int Fragment_name = 0;
         public static final int Fragment_tag = 2;
     }
+
+    /**
+     * An add or pop transaction to be scheduled for the UI thread.
+     */
+    interface OpGenerator {
+        /**
+         * Generate transactions to add to {@code records} and whether or not the transaction is
+         * an add or pop to {@code isRecordPop}.
+         *
+         * records and isRecordPop must be added equally so that each transaction in records
+         * matches the boolean for whether or not it is a pop in isRecordPop.
+         *
+         * @param records A list to add transactions to.
+         * @param isRecordPop A list to add whether or not the transactions added to records is
+         *                    a pop transaction.
+         * @return true if something was added or false otherwise.
+         */
+        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
+    }
+
+    /**
+     * A pop operation OpGenerator. This will be run on the UI thread and will generate the
+     * transactions that will be popped if anything can be popped.
+     */
+    private class PopBackStackState implements OpGenerator {
+        final String mName;
+        final int mId;
+        final int mFlags;
+
+        PopBackStackState(String name, int id, int flags) {
+            mName = name;
+            mId = id;
+            mFlags = flags;
+        }
+
+        @Override
+        public boolean generateOps(ArrayList<BackStackRecord> records,
+                ArrayList<Boolean> isRecordPop) {
+            return popBackStackState(records, isRecordPop, mName, mId, mFlags);
+        }
+    }
+
+    /**
+     * A listener for a postponed transaction. This waits until
+     * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
+     * that interacts with this one, based on interactions with the fragment container.
+     */
+    static class StartEnterTransitionListener
+            implements Fragment.OnStartEnterTransitionListener {
+        private final boolean mIsBack;
+        private final BackStackRecord mRecord;
+        private int mNumPostponed;
+
+        StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
+            mIsBack = isBack;
+            mRecord = record;
+        }
+
+        /**
+         * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
+         * number of Fragments that are postponed. This may cause the transaction to schedule
+         * to finish running and run transitions and animations.
+         */
+        @Override
+        public void onStartEnterTransition() {
+            mNumPostponed--;
+            if (mNumPostponed != 0) {
+                return;
+            }
+            mRecord.mManager.scheduleCommit();
+        }
+
+        /**
+         * Called from {@link Fragment#
+         * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
+         * increases the number of fragments that are postponed as part of this transaction.
+         */
+        @Override
+        public void startListening() {
+            mNumPostponed++;
+        }
+
+        /**
+         * @return true if there are no more postponed fragments as part of the transaction.
+         */
+        public boolean isReady() {
+            return mNumPostponed == 0;
+        }
+
+        /**
+         * Completes the transaction and start the animations and transitions. This may skip
+         * the transitions if this is called before all fragments have called
+         * {@link Fragment#startPostponedEnterTransition()}.
+         */
+        public void completeTransaction() {
+            final boolean canceled;
+            canceled = mNumPostponed > 0;
+            FragmentManagerImpl manager = mRecord.mManager;
+            final int numAdded = manager.mAdded.size();
+            for (int i = 0; i < numAdded; i++) {
+                final Fragment fragment = manager.mAdded.get(i);
+                fragment.setOnStartEnterTransitionListener(null);
+                if (canceled && fragment.isPostponed()) {
+                    fragment.startPostponedEnterTransition();
+                }
+            }
+            mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
+        }
+
+        /**
+         * Cancels this transaction instead of completing it. That means that the state isn't
+         * changed, so the pop results in no change to the state.
+         */
+        public void cancelTransaction() {
+            mRecord.mManager.completeExecute(mRecord, mIsBack, false, false);
+        }
+    }
 }
diff --git a/fragment/java/android/support/v4/app/FragmentTransaction.java b/fragment/java/android/support/v4/app/FragmentTransaction.java
index 745f7e8..9be99b2 100644
--- a/fragment/java/android/support/v4/app/FragmentTransaction.java
+++ b/fragment/java/android/support/v4/app/FragmentTransaction.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.support.annotation.AnimRes;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
@@ -23,14 +25,11 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StringRes;
 import android.support.annotation.StyleRes;
-import android.support.v4.util.Pair;
 import android.view.View;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Static library support version of the framework's {@link android.app.FragmentTransaction}.
  * Used to write apps that run on platforms prior to Android 3.0.  When running
@@ -282,6 +281,30 @@
     public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
 
     /**
+     * Sets whether or not to allow optimizing operations within and across
+     * transactions. Optimizing fragment transaction's operations can eliminate
+     * operations that cancel. For example, if two transactions are executed
+     * together, one that adds a fragment A and the next replaces it with fragment B,
+     * the operations will cancel and only fragment B will be added. That means that
+     * fragment A may not go through the creation/destruction lifecycle.
+     * <p>
+     * The side effect of optimization is that fragments may have state changes
+     * out of the expected order. For example, one transaction adds fragment A,
+     * a second adds fragment B, then a third removes fragment A. Without optimization,
+     * fragment B could expect that while it is being created, fragment A will also
+     * exist because fragment A will be removed after fragment B was added.
+     * With optimization, fragment B cannot expect fragment A to exist when
+     * it has been created because fragment A's add/remove will be optimized out.
+     * <p>
+     * The default is {@code true}.
+     *
+     * @param allowOptimization {@code true} to enable optimizing operations
+     *                          or {@code false} to disable optimizing
+     *                          operations on this transaction.
+     */
+    public abstract FragmentTransaction setAllowOptimization(boolean allowOptimization);
+
+    /**
      * Schedules a commit of this transaction.  The commit does
      * not happen immediately; it will be scheduled as work on the main thread
      * to be done the next time that thread is ready.
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
new file mode 100644
index 0000000..408bfe0
--- /dev/null
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.graphics.Rect;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.view.ViewCompat;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the Fragment Transition functionality for both optimized and unoptimized
+ * Fragment Transactions. With optimized fragment transactions, all Views have been
+ * added to the View hierarchy prior to calling startTransitions. With unoptimized
+ * fragment transactions, Views will be removed and added after calling startTransitions.
+ */
+class FragmentTransition {
+    /**
+     * The inverse of all BackStackRecord operation commands. This assumes that
+     * REPLACE operations have already been replaced by add/remove operations.
+     */
+    private static final int[] INVERSE_OPS = {
+            BackStackRecord.OP_NULL,   // inverse of OP_NULL (error)
+            BackStackRecord.OP_REMOVE, // inverse of OP_ADD
+            BackStackRecord.OP_NULL,   // inverse of OP_REPLACE (error)
+            BackStackRecord.OP_ADD,    // inverse of OP_REMOVE
+            BackStackRecord.OP_SHOW,   // inverse of OP_HIDE
+            BackStackRecord.OP_HIDE,   // inverse of OP_SHOW
+            BackStackRecord.OP_ATTACH, // inverse of OP_DETACH
+            BackStackRecord.OP_DETACH, // inverse of OP_ATTACH
+    };
+
+    /**
+     * The main entry point for Fragment Transitions, this starts the transitions
+     * set on the leaving Fragment's {@link Fragment#getExitTransition()}, the
+     * entering Fragment's {@link Fragment#getEnterTransition()} and
+     * {@link Fragment#getSharedElementEnterTransition()}. When popping,
+     * the leaving Fragment's {@link Fragment#getReturnTransition()} and
+     * {@link Fragment#getSharedElementReturnTransition()} and the entering
+     * {@link Fragment#getReenterTransition()} will be run.
+     * <p>
+     * With optimized Fragment Transitions, all Views have been added to the
+     * View hierarchy prior to calling this method. The incoming Fragment's Views
+     * will be INVISIBLE. With unoptimized Fragment Transitions, this method
+     * is called before any change has been made to the hierarchy. That means
+     * that the added Fragments have not created their Views yet and the hierarchy
+     * is unknown.
+     *
+     * @param fragmentManager The executing FragmentManagerImpl
+     * @param records The list of transactions being executed.
+     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+     * @param startIndex The first index into records and isRecordPop to execute as
+     *                   part of this transition.
+     * @param endIndex One past the last index into records and isRecordPop to execute
+     *                 as part of this transition.
+     * @param isOptimized true if this is an optimized transaction, meaning that the
+     *                    Views of incoming fragments have been added. false if the
+     *                    transaction has yet to be run and Views haven't been created.
+     */
+    static void startTransitions(FragmentManagerImpl fragmentManager,
+            ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+            int startIndex, int endIndex, boolean isOptimized) {
+        if (fragmentManager.mCurState < Fragment.CREATED) {
+            return;
+        }
+        SparseArray<FragmentContainerTransition> transitioningFragments =
+                new SparseArray<>();
+        for (int i = startIndex; i < endIndex; i++) {
+            final BackStackRecord record = records.get(i);
+            final boolean isPop = isRecordPop.get(i);
+            if (isPop) {
+                calculatePopFragments(record, transitioningFragments, isOptimized);
+            } else {
+                calculateFragments(record, transitioningFragments, isOptimized);
+            }
+        }
+
+        if (transitioningFragments.size() != 0) {
+            final View nonExistentView = new View(fragmentManager.mHost.getContext());
+            final int numContainers = transitioningFragments.size();
+            for (int i = 0; i < numContainers; i++) {
+                int containerId = transitioningFragments.keyAt(i);
+                ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
+                        records, isRecordPop, startIndex, endIndex);
+
+                FragmentContainerTransition containerTransition = transitioningFragments.valueAt(i);
+
+                if (isOptimized) {
+                    configureTransitionsOptimized(fragmentManager, containerId,
+                            containerTransition, nonExistentView, nameOverrides);
+                } else {
+                    configureTransitionsUnoptimized(fragmentManager, containerId,
+                            containerTransition, nonExistentView, nameOverrides);
+                }
+            }
+        }
+    }
+
+    /**
+     * Iterates through the transactions that affect a given fragment container
+     * and tracks the shared element names across transactions. This is most useful
+     * in pop transactions where the names of shared elements are known.
+     *
+     * @param containerId The container ID that is executing the transition.
+     * @param records The list of transactions being executed.
+     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
+     * @param startIndex The first index into records and isRecordPop to execute as
+     *                   part of this transition.
+     * @param endIndex One past the last index into records and isRecordPop to execute
+     *                 as part of this transition.
+     * @return A map from the initial shared element name to the final shared element name
+     * before any onMapSharedElements is run.
+     */
+    private static ArrayMap<String, String> calculateNameOverrides(int containerId,
+            ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
+            int startIndex, int endIndex) {
+        ArrayMap<String, String> nameOverrides = new ArrayMap<>();
+        for (int recordNum = endIndex - 1; recordNum >= startIndex; recordNum--) {
+            final BackStackRecord record = records.get(recordNum);
+            if (!record.interactsWith(containerId)) {
+                continue;
+            }
+            final boolean isPop = isRecordPop.get(recordNum);
+            if (record.mSharedElementSourceNames != null) {
+                final int numSharedElements = record.mSharedElementSourceNames.size();
+                final ArrayList<String> sources;
+                final ArrayList<String> targets;
+                if (isPop) {
+                    targets = record.mSharedElementSourceNames;
+                    sources = record.mSharedElementTargetNames;
+                } else {
+                    sources = record.mSharedElementSourceNames;
+                    targets = record.mSharedElementTargetNames;
+                }
+                for (int i = 0; i < numSharedElements; i++) {
+                    String sourceName = sources.get(i);
+                    String targetName = targets.get(i);
+                    String previousTarget = nameOverrides.remove(targetName);
+                    if (previousTarget != null) {
+                        nameOverrides.put(sourceName, previousTarget);
+                    } else {
+                        nameOverrides.put(sourceName, targetName);
+                    }
+                }
+            }
+        }
+        return nameOverrides;
+    }
+
+    /**
+     * Configures a transition for a single fragment container for which the transaction was
+     * optimized. That means that all Fragment Views have been added and incoming fragment
+     * Views are marked invisible.
+     *
+     * @param fragmentManager The executing FragmentManagerImpl
+     * @param containerId The container ID that is executing the transition.
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+     *                        prevent transitions from acting on other Views when there is no
+     *                        other target.
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     */
+    private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
+            int containerId, FragmentContainerTransition fragments,
+            View nonExistentView, ArrayMap<String, String> nameOverrides) {
+        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        if (sceneRoot == null) {
+            return;
+        }
+        final Fragment inFragment = fragments.lastIn;
+        final Fragment outFragment = fragments.firstOut;
+        final boolean inIsPop = fragments.lastInIsPop;
+        final boolean outIsPop = fragments.firstOutIsPop;
+
+        ArrayList<View> sharedElementsIn = new ArrayList<>();
+        ArrayList<View> sharedElementsOut = new ArrayList<>();
+        Object enterTransition = getEnterTransition(inFragment, inIsPop);
+        Object exitTransition = getExitTransition(outFragment, outIsPop);
+
+        Object sharedElementTransition = configureSharedElementsOptimized(sceneRoot,
+                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+                enterTransition, exitTransition);
+
+        if (enterTransition == null && sharedElementTransition == null
+                && exitTransition == null) {
+            return; // no transitions!
+        }
+
+        ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+                outFragment, sharedElementsOut, nonExistentView);
+
+        ArrayList<View> enteringViews = configureEnteringExitingViews(enterTransition,
+                inFragment, sharedElementsIn, nonExistentView);
+
+        setViewVisibility(enteringViews, View.INVISIBLE);
+
+        Object transition = mergeTransitions(enterTransition, exitTransition,
+                sharedElementTransition, inFragment, inIsPop);
+
+        if (transition != null) {
+            ArrayList<String> inNames =
+                    FragmentTransitionCompat21.prepareSetNameOverridesOptimized(sharedElementsIn);
+            FragmentTransitionCompat21.scheduleRemoveTargets(transition,
+                    enterTransition, enteringViews, exitTransition, exitingViews,
+                    sharedElementTransition, sharedElementsIn);
+            FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
+            FragmentTransitionCompat21.setNameOverridesOptimized(sceneRoot, sharedElementsOut,
+                    sharedElementsIn, inNames, nameOverrides);
+            setViewVisibility(enteringViews, View.VISIBLE);
+            FragmentTransitionCompat21.swapSharedElementTargets(sharedElementTransition,
+                    sharedElementsOut, sharedElementsIn);
+        }
+    }
+
+    /**
+     * Configures a transition for a single fragment container for which the transaction was
+     * not optimized. That means that the transaction has not been executed yet, so incoming
+     * Views are not yet known.
+     *
+     * @param fragmentManager The executing FragmentManagerImpl
+     * @param containerId The container ID that is executing the transition.
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+     *                        prevent transitions from acting on other Views when there is no
+     *                        other target.
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     */
+    private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
+            int containerId, FragmentContainerTransition fragments,
+            View nonExistentView, ArrayMap<String, String> nameOverrides) {
+        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        if (sceneRoot == null) {
+            return;
+        }
+        final Fragment inFragment = fragments.lastIn;
+        final Fragment outFragment = fragments.firstOut;
+        final boolean inIsPop = fragments.lastInIsPop;
+        final boolean outIsPop = fragments.firstOutIsPop;
+
+        Object enterTransition = getEnterTransition(inFragment, inIsPop);
+        Object exitTransition = getExitTransition(outFragment, outIsPop);
+
+        ArrayList<View> sharedElementsOut = new ArrayList<>();
+        ArrayList<View> sharedElementsIn = new ArrayList<>();
+
+        Object sharedElementTransition = configureSharedElementsUnoptimized(sceneRoot,
+                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
+                enterTransition, exitTransition);
+
+        if (enterTransition == null && sharedElementTransition == null
+                && exitTransition == null) {
+            return; // no transitions!
+        }
+
+        ArrayList<View> exitingViews = configureEnteringExitingViews(exitTransition,
+                outFragment, sharedElementsOut, nonExistentView);
+
+        if (exitingViews == null || exitingViews.isEmpty()) {
+            exitTransition = null;
+        }
+
+        // Ensure the entering transition doesn't target anything until the views are made
+        // visible
+        FragmentTransitionCompat21.addTarget(enterTransition, nonExistentView);
+
+        Object transition = mergeTransitions(enterTransition, exitTransition,
+                sharedElementTransition, inFragment, fragments.lastInIsPop);
+
+        if (transition != null) {
+            final ArrayList<View> enteringViews = new ArrayList<>();
+            FragmentTransitionCompat21.scheduleRemoveTargets(transition,
+                    enterTransition, enteringViews, exitTransition, exitingViews,
+                    sharedElementTransition, sharedElementsIn);
+            scheduleTargetChange(sceneRoot, inFragment, nonExistentView, sharedElementsIn,
+                    enterTransition, enteringViews, exitTransition, exitingViews);
+            FragmentTransitionCompat21.setNameOverridesUnoptimized(sceneRoot, sharedElementsIn,
+                    nameOverrides);
+
+            FragmentTransitionCompat21.beginDelayedTransition(sceneRoot, transition);
+            FragmentTransitionCompat21.scheduleNameReset(sceneRoot, sharedElementsIn,
+                    nameOverrides);
+        }
+    }
+
+    /**
+     * This method is used for fragment transitions for unoptimized transactions to change the
+     * enter and exit transition targets after the call to
+     * {@link FragmentTransitionCompat21#beginDelayedTransition(ViewGroup, Object)}. The exit
+     * transition must ensure that it does not target any Views and the enter transition must start
+     * targeting the Views of the incoming Fragment.
+     *
+     * @param sceneRoot The fragment container View
+     * @param inFragment The last fragment that is entering
+     * @param nonExistentView A view that does not exist in the hierarchy that is used as a
+     *                        transition target to ensure no View is targeted.
+     * @param sharedElementsIn The shared element Views of the incoming fragment
+     * @param enterTransition The enter transition of the incoming fragment
+     * @param enteringViews The entering Views of the incoming fragment
+     * @param exitTransition The exit transition of the outgoing fragment
+     * @param exitingViews The exiting views of the outgoing fragment
+     */
+    private static void scheduleTargetChange(final ViewGroup sceneRoot,
+            final Fragment inFragment, final View nonExistentView,
+            final ArrayList<View> sharedElementsIn,
+            final Object enterTransition, final ArrayList<View> enteringViews,
+            final Object exitTransition, final ArrayList<View> exitingViews) {
+
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+
+                        if (enterTransition != null) {
+                            FragmentTransitionCompat21.removeTarget(enterTransition,
+                                    nonExistentView);
+                            ArrayList<View> views = configureEnteringExitingViews(
+                                    enterTransition, inFragment, sharedElementsIn, nonExistentView);
+                            enteringViews.addAll(views);
+                        }
+
+                        if (exitingViews != null) {
+                            ArrayList<View> tempExiting = new ArrayList<>();
+                            tempExiting.add(nonExistentView);
+                            FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
+                                    tempExiting);
+                            exitingViews.clear();
+                            exitingViews.add(nonExistentView);
+                        }
+
+                        return true;
+                    }
+                });
+    }
+
+    /**
+     * Returns a TransitionSet containing the shared element transition. The wrapping TransitionSet
+     * targets all shared elements to ensure that no other Views are targeted. The shared element
+     * transition can then target any or all shared elements without worrying about accidentally
+     * targeting entering or exiting Views.
+     *
+     * @param inFragment The incoming fragment
+     * @param outFragment the outgoing fragment
+     * @param isPop True if this is a pop transaction or false if it is a normal (add) transaction.
+     * @return A TransitionSet wrapping the shared element transition or null if no such transition
+     * exists.
+     */
+    private static Object getSharedElementTransition(Fragment inFragment,
+            Fragment outFragment, boolean isPop) {
+        if (inFragment == null || outFragment == null) {
+            return null;
+        }
+        Object transition = FragmentTransitionCompat21.cloneTransition(isPop
+                ? outFragment.getSharedElementReturnTransition()
+                : inFragment.getSharedElementEnterTransition());
+        return FragmentTransitionCompat21.wrapTransitionInSet(transition);
+    }
+
+    /**
+     * Returns a clone of the enter transition or null if no such transition exists.
+     */
+    private static Object getEnterTransition(Fragment inFragment, boolean isPop) {
+        if (inFragment == null) {
+            return null;
+        }
+        return FragmentTransitionCompat21.cloneTransition(isPop
+                ? inFragment.getReenterTransition()
+                : inFragment.getEnterTransition());
+    }
+
+    /**
+     * Returns a clone of the exit transition or null if no such transition exists.
+     */
+    private static Object getExitTransition(Fragment outFragment, boolean isPop) {
+        if (outFragment == null) {
+            return null;
+        }
+        return FragmentTransitionCompat21.cloneTransition(isPop
+                ? outFragment.getReturnTransition()
+                : outFragment.getExitTransition());
+    }
+
+    /**
+     * Configures the shared elements of an optimized fragment transaction's transition.
+     * This retrieves the shared elements of the outgoing and incoming fragments, maps the
+     * views, and sets up the epicenter on the transitions.
+     * <p>
+     * The epicenter of exit and shared element transitions is the first shared element
+     * in the outgoing fragment. The epicenter of the entering transition is the first shared
+     * element in the incoming fragment.
+     *
+     * @param sceneRoot The fragment container View
+     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+     *                        prevent transitions from acting on other Views when there is no
+     *                        other target.
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+     *                          fragment
+     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+     *                         fragment
+     * @param enterTransition The transition used for entering Views, modified by applying the
+     *                        epicenter
+     * @param exitTransition The transition used for exiting Views, modified by applying the
+     *                       epicenter
+     * @return The shared element transition or null if no shared elements exist
+     */
+    private static Object configureSharedElementsOptimized(final ViewGroup sceneRoot,
+            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
+            final FragmentContainerTransition fragments,
+            final ArrayList<View> sharedElementsOut,
+            final ArrayList<View> sharedElementsIn,
+            final Object enterTransition, final Object exitTransition) {
+        final Fragment inFragment = fragments.lastIn;
+        final Fragment outFragment = fragments.firstOut;
+        if (inFragment != null) {
+            inFragment.getView().setVisibility(View.VISIBLE);
+        }
+        if (inFragment == null || outFragment == null) {
+            return null; // no shared element without a fragment
+        }
+
+        final boolean inIsPop = fragments.lastInIsPop;
+        Object sharedElementTransition = nameOverrides.isEmpty() ? null
+                : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+        final ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+                sharedElementTransition, fragments);
+
+        final ArrayMap<String, View> inSharedElements = captureInSharedElements(nameOverrides,
+                sharedElementTransition, fragments);
+
+        if (nameOverrides.isEmpty()) {
+            sharedElementTransition = null;
+        } else {
+            sharedElementsOut.addAll(outSharedElements.values());
+            sharedElementsIn.addAll(inSharedElements.values());
+        }
+
+        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+            // don't call onSharedElementStart/End since there is no transition
+            return null;
+        }
+
+        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+        final Rect epicenter;
+        final View epicenterView;
+        if (sharedElementTransition != null) {
+            sharedElementsIn.add(nonExistentView);
+            FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
+                    nonExistentView, sharedElementsOut);
+            final boolean outIsPop = fragments.firstOutIsPop;
+            final BackStackRecord outTransaction = fragments.firstOutTransaction;
+            setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+                    outTransaction);
+            epicenter = new Rect();
+            epicenterView = getInEpicenterView(inSharedElements, fragments,
+                    enterTransition, inIsPop);
+            if (epicenterView != null) {
+                FragmentTransitionCompat21.setEpicenter(enterTransition, epicenter);
+            }
+        } else {
+            epicenter = null;
+            epicenterView = null;
+        }
+
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                        callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+                                inSharedElements, false);
+                        if (epicenterView != null) {
+                            FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
+                        }
+                        return true;
+                    }
+                });
+        return sharedElementTransition;
+    }
+
+    /**
+     * Configures the shared elements of an unoptimized fragment transaction's transition.
+     * This retrieves the shared elements of the incoming fragments, and schedules capturing
+     * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
+     * on the transitions.
+     * <p>
+     * The epicenter of exit and shared element transitions is the first shared element
+     * in the outgoing fragment. The epicenter of the entering transition is the first shared
+     * element in the incoming fragment.
+     *
+     * @param sceneRoot The fragment container View
+     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
+     *                        prevent transitions from acting on other Views when there is no
+     *                        other target.
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
+     *                          fragment
+     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
+     *                         fragment
+     * @param enterTransition The transition used for entering Views, modified by applying the
+     *                        epicenter
+     * @param exitTransition The transition used for exiting Views, modified by applying the
+     *                       epicenter
+     * @return The shared element transition or null if no shared elements exist
+     */
+    private static Object configureSharedElementsUnoptimized(final ViewGroup sceneRoot,
+            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
+            final FragmentContainerTransition fragments,
+            final ArrayList<View> sharedElementsOut,
+            final ArrayList<View> sharedElementsIn,
+            final Object enterTransition, final Object exitTransition) {
+        final Fragment inFragment = fragments.lastIn;
+        final Fragment outFragment = fragments.firstOut;
+
+        if (inFragment == null || outFragment == null) {
+            return null; // no transition
+        }
+
+        final boolean inIsPop = fragments.lastInIsPop;
+        Object sharedElementTransition = nameOverrides.isEmpty() ? null
+                : getSharedElementTransition(inFragment, outFragment, inIsPop);
+
+        ArrayMap<String, View> outSharedElements = captureOutSharedElements(nameOverrides,
+                sharedElementTransition, fragments);
+
+        if (nameOverrides.isEmpty()) {
+            sharedElementTransition = null;
+        } else {
+            sharedElementsOut.addAll(outSharedElements.values());
+        }
+
+        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
+            // don't call onSharedElementStart/End since there is no transition
+            return null;
+        }
+
+        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
+
+        final Rect inEpicenter;
+        if (sharedElementTransition != null) {
+            inEpicenter = new Rect();
+            FragmentTransitionCompat21.setSharedElementTargets(sharedElementTransition,
+                    nonExistentView, sharedElementsOut);
+            final boolean outIsPop = fragments.firstOutIsPop;
+            final BackStackRecord outTransaction = fragments.firstOutTransaction;
+            setOutEpicenter(sharedElementTransition, exitTransition, outSharedElements, outIsPop,
+                    outTransaction);
+            if (enterTransition != null) {
+                FragmentTransitionCompat21.setEpicenter(enterTransition, inEpicenter);
+            }
+        } else {
+            inEpicenter = null;
+        }
+
+        final Object finalSharedElementTransition = sharedElementTransition;
+
+        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                        ArrayMap<String, View> inSharedElements = captureInSharedElements(
+                                nameOverrides, finalSharedElementTransition, fragments);
+
+                        if (inSharedElements != null) {
+                            sharedElementsIn.addAll(inSharedElements.values());
+                            sharedElementsIn.add(nonExistentView);
+                        }
+
+                        callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+                                inSharedElements, false);
+                        if (finalSharedElementTransition != null) {
+                            FragmentTransitionCompat21.swapSharedElementTargets(
+                                    finalSharedElementTransition, sharedElementsOut,
+                                    sharedElementsIn);
+
+                            final View inEpicenterView = getInEpicenterView(inSharedElements,
+                                    fragments, enterTransition, inIsPop);
+                            if (inEpicenterView != null) {
+                                FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
+                                        inEpicenter);
+                            }
+                        }
+                        return true;
+                    }
+                });
+        return sharedElementTransition;
+    }
+
+    /**
+     * Finds the shared elements in the outgoing fragment. It also calls
+     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+     * of the shared element mapping. {@code nameOverrides} is updated to match the
+     * actual transition name of the mapped shared elements.
+     *
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     * @param sharedElementTransition The shared element transition
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @return The mapping of shared element names to the Views in the hierarchy or null
+     * if there is no shared element transition.
+     */
+    private static ArrayMap<String, View> captureOutSharedElements(
+            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
+            FragmentContainerTransition fragments) {
+        if (nameOverrides.isEmpty() || sharedElementTransition == null) {
+            nameOverrides.clear();
+            return null;
+        }
+        final Fragment outFragment = fragments.firstOut;
+        final ArrayMap<String, View> outSharedElements = new ArrayMap<>();
+        FragmentTransitionCompat21.findNamedViews(outSharedElements, outFragment.getView());
+
+        final SharedElementCallback sharedElementCallback;
+        final ArrayList<String> names;
+        final BackStackRecord outTransaction = fragments.firstOutTransaction;
+        if (fragments.firstOutIsPop) {
+            sharedElementCallback = outFragment.getEnterTransitionCallback();
+            names = outTransaction.mSharedElementTargetNames;
+        } else {
+            sharedElementCallback = outFragment.getExitTransitionCallback();
+            names = outTransaction.mSharedElementSourceNames;
+        }
+
+        outSharedElements.retainAll(names);
+        if (sharedElementCallback != null) {
+            sharedElementCallback.onMapSharedElements(names, outSharedElements);
+            for (int i = names.size() - 1; i >= 0; i--) {
+                String name = names.get(i);
+                View view = outSharedElements.get(name);
+                if (view == null) {
+                    nameOverrides.remove(name);
+                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
+                    String targetValue = nameOverrides.remove(name);
+                    nameOverrides.put(ViewCompat.getTransitionName(view), targetValue);
+                }
+            }
+        } else {
+            nameOverrides.retainAll(outSharedElements.keySet());
+        }
+        return outSharedElements;
+    }
+
+    /**
+     * Finds the shared elements in the incoming fragment. It also calls
+     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
+     * of the shared element mapping. {@code nameOverrides} is updated to match the
+     * actual transition name of the mapped shared elements.
+     *
+     * @param nameOverrides A map of the shared element names from the starting fragment to
+     *                      the final fragment's Views as given in
+     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
+     * @param sharedElementTransition The shared element transition
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @return The mapping of shared element names to the Views in the hierarchy or null
+     * if there is no shared element transition.
+     */
+    private static ArrayMap<String, View> captureInSharedElements(
+            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
+            FragmentContainerTransition fragments) {
+        Fragment inFragment = fragments.lastIn;
+        final View fragmentView = inFragment.getView();
+        if (nameOverrides.isEmpty() || sharedElementTransition == null || fragmentView == null) {
+            nameOverrides.clear();
+            return null;
+        }
+        final ArrayMap<String, View> inSharedElements = new ArrayMap<>();
+        FragmentTransitionCompat21.findNamedViews(inSharedElements, fragmentView);
+
+        final SharedElementCallback sharedElementCallback;
+        final ArrayList<String> names;
+        final BackStackRecord inTransaction = fragments.lastInTransaction;
+        if (fragments.lastInIsPop) {
+            sharedElementCallback = inFragment.getExitTransitionCallback();
+            names = inTransaction.mSharedElementSourceNames;
+        } else {
+            sharedElementCallback = inFragment.getEnterTransitionCallback();
+            names = inTransaction.mSharedElementTargetNames;
+        }
+
+        inSharedElements.retainAll(names);
+        if (sharedElementCallback != null) {
+            sharedElementCallback.onMapSharedElements(names, inSharedElements);
+            for (int i = names.size() - 1; i >= 0; i--) {
+                String name = names.get(i);
+                View view = inSharedElements.get(name);
+                if (view == null) {
+                    String key = findKeyForValue(nameOverrides, name);
+                    if (key != null) {
+                        nameOverrides.remove(key);
+                    }
+                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
+                    String key = findKeyForValue(nameOverrides, name);
+                    if (key != null) {
+                        nameOverrides.put(key, ViewCompat.getTransitionName(view));
+                    }
+                }
+            }
+        } else {
+            retainValues(nameOverrides, inSharedElements);
+        }
+        return inSharedElements;
+    }
+
+    /**
+     * Utility to find the String key in {@code map} that maps to {@code value}.
+     */
+    private static String findKeyForValue(ArrayMap<String, String> map, String value) {
+        final int numElements = map.size();
+        for (int i = 0; i < numElements; i++) {
+            if (value.equals(map.valueAt(i))) {
+                return map.keyAt(i);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the View in the incoming Fragment that should be used as the epicenter.
+     *
+     * @param inSharedElements The mapping of shared element names to Views in the
+     *                         incoming fragment.
+     * @param fragments A structure holding the transitioning fragments in this container.
+     * @param enterTransition The transition used for the incoming Fragment's views
+     * @param inIsPop Is the incoming fragment being added as a pop transaction?
+     */
+    private static View getInEpicenterView(ArrayMap<String, View> inSharedElements,
+            FragmentContainerTransition fragments,
+            Object enterTransition, boolean inIsPop) {
+        BackStackRecord inTransaction = fragments.lastInTransaction;
+        if (enterTransition != null && inTransaction.mSharedElementSourceNames != null
+                && !inTransaction.mSharedElementSourceNames.isEmpty()) {
+            final String targetName = inIsPop
+                    ? inTransaction.mSharedElementSourceNames.get(0)
+                    : inTransaction.mSharedElementTargetNames.get(0);
+            return inSharedElements.get(targetName);
+        }
+        return null;
+    }
+
+    /**
+     * Sets the epicenter for the exit transition.
+     *
+     * @param sharedElementTransition The shared element transition
+     * @param exitTransition The transition for the outgoing fragment's views
+     * @param outSharedElements Shared elements in the outgoing fragment
+     * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
+     * @param outTransaction The transaction that caused the fragment to be removed.
+     */
+    private static void setOutEpicenter(Object sharedElementTransition,
+            Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
+            BackStackRecord outTransaction) {
+        if (outTransaction.mSharedElementSourceNames != null
+                && !outTransaction.mSharedElementSourceNames.isEmpty()) {
+            final String sourceName = outIsPop
+                    ? outTransaction.mSharedElementTargetNames.get(0)
+                    : outTransaction.mSharedElementSourceNames.get(0);
+            final View outEpicenterView = outSharedElements.get(sourceName);
+            FragmentTransitionCompat21.setEpicenter(sharedElementTransition, outEpicenterView);
+
+            if (exitTransition != null) {
+                FragmentTransitionCompat21.setEpicenter(exitTransition, outEpicenterView);
+            }
+        }
+    }
+
+    /**
+     * A utility to retain only the mappings in {@code nameOverrides} that have a value
+     * that has a key in {@code namedViews}. This is a useful equivalent to
+     * {@link ArrayMap#retainAll(Collection)} for values.
+     */
+    private static void retainValues(ArrayMap<String, String> nameOverrides,
+            ArrayMap<String, View> namedViews) {
+        for (int i = nameOverrides.size() - 1; i >= 0; i--) {
+            final String targetName = nameOverrides.valueAt(i);
+            if (!namedViews.containsKey(targetName)) {
+                nameOverrides.removeAt(i);
+            }
+        }
+    }
+
+    /**
+     * Calls the {@link SharedElementCallback#onSharedElementStart(List, List, List)} or
+     * {@link SharedElementCallback#onSharedElementEnd(List, List, List)} on the appropriate
+     * incoming or outgoing fragment.
+     *
+     * @param inFragment The incoming fragment
+     * @param outFragment The outgoing fragment
+     * @param isPop Is the incoming fragment part of a pop transaction?
+     * @param sharedElements The shared element Views
+     * @param isStart Call the start or end call on the SharedElementCallback
+     */
+    private static void callSharedElementStartEnd(Fragment inFragment, Fragment outFragment,
+            boolean isPop, ArrayMap<String, View> sharedElements, boolean isStart) {
+        SharedElementCallback sharedElementCallback = isPop
+                ? outFragment.getEnterTransitionCallback()
+                : inFragment.getEnterTransitionCallback();
+        if (sharedElementCallback != null) {
+            ArrayList<View> views = new ArrayList<>();
+            ArrayList<String> names = new ArrayList<>();
+            final int count = sharedElements == null ? 0 : sharedElements.size();
+            for (int i = 0; i < count; i++) {
+                names.add(sharedElements.keyAt(i));
+                views.add(sharedElements.valueAt(i));
+            }
+            if (isStart) {
+                sharedElementCallback.onSharedElementStart(names, views, null);
+            } else {
+                sharedElementCallback.onSharedElementEnd(names, views, null);
+            }
+        }
+    }
+
+    private static ArrayList<View> configureEnteringExitingViews(Object transition,
+            Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
+        ArrayList<View> viewList = null;
+        if (transition != null) {
+            viewList = new ArrayList<>();
+            View root = fragment.getView();
+            FragmentTransitionCompat21.captureTransitioningViews(viewList, root);
+            if (sharedElements != null) {
+                viewList.removeAll(sharedElements);
+            }
+            if (!viewList.isEmpty()) {
+                viewList.add(nonExistentView);
+                FragmentTransitionCompat21.addTargets(transition, viewList);
+            }
+        }
+        return viewList;
+    }
+
+    /**
+     * Sets the visibility of all Views in {@code views} to {@code visibility}.
+     */
+    private static void setViewVisibility(ArrayList<View> views, int visibility) {
+        if (views == null) {
+            return;
+        }
+        for (int i = views.size() - 1; i >= 0; i--) {
+            final View view = views.get(i);
+            view.setVisibility(visibility);
+        }
+    }
+
+    /**
+     * Merges exit, shared element, and enter transitions so that they act together or
+     * sequentially as defined in the fragments.
+     */
+    private static Object mergeTransitions(Object enterTransition,
+            Object exitTransition, Object sharedElementTransition, Fragment inFragment,
+            boolean isPop) {
+        boolean overlap = true;
+        if (enterTransition != null && exitTransition != null && inFragment != null) {
+            overlap = isPop ? inFragment.getAllowReturnTransitionOverlap() :
+                    inFragment.getAllowEnterTransitionOverlap();
+        }
+
+        // Wrap the transitions. Explicit targets like in enter and exit will cause the
+        // views to be targeted regardless of excluded views. If that happens, then the
+        // excluded fragments views (hidden fragments) will still be in the transition.
+
+        Object transition;
+        if (overlap) {
+            // Regular transition -- do it all together
+            transition = FragmentTransitionCompat21.mergeTransitionsTogether(exitTransition,
+                    enterTransition, sharedElementTransition);
+        } else {
+            // First do exit, then enter, but allow shared element transition to happen
+            // during both.
+            transition = FragmentTransitionCompat21.mergeTransitionsInSequence(exitTransition,
+                    enterTransition, sharedElementTransition);
+        }
+        return transition;
+    }
+
+    /**
+     * Finds the first removed fragment and last added fragments when going forward.
+     * If none of the fragments have transitions, then both lists will be empty.
+     *
+     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+     *                               and last fragments to be added. This will be modified by
+     *                               this method.
+     */
+    public static void calculateFragments(BackStackRecord transaction,
+            SparseArray<FragmentContainerTransition> transitioningFragments,
+            boolean isOptimized) {
+        final int numOps = transaction.mOps.size();
+        for (int opNum = 0; opNum < numOps; opNum++) {
+            final BackStackRecord.Op op = transaction.mOps.get(opNum);
+            addToFirstInLastOut(transaction, op, transitioningFragments, false, isOptimized);
+        }
+    }
+
+    /**
+     * Finds the first removed fragment and last added fragments when popping the back stack.
+     * If none of the fragments have transitions, then both lists will be empty.
+     *
+     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
+     *                               and last fragments to be added. This will be modified by
+     *                               this method.
+     */
+    public static void calculatePopFragments(BackStackRecord transaction,
+            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isOptimized) {
+        if (!transaction.mManager.mContainer.onHasView()) {
+            return; // nothing to see, so no transitions
+        }
+        final int numOps = transaction.mOps.size();
+        for (int opNum = numOps - 1; opNum >= 0; opNum--) {
+            final BackStackRecord.Op op = transaction.mOps.get(opNum);
+            addToFirstInLastOut(transaction, op, transitioningFragments, true, isOptimized);
+        }
+    }
+
+    /**
+     * Examines the {@code command} and may set the first out or last in fragment for the fragment's
+     * container.
+     *
+     * @param transaction The executing transaction
+     * @param op The operation being run.
+     * @param transitioningFragments A structure holding the first in and last out fragments
+     *                               for each fragment container.
+     * @param isPop Is the operation a pop?
+     * @param isOptimizedTransaction True if the operations have been partially executed and the
+     *                               added fragments have Views in the hierarchy or false if the
+     *                               operations haven't been executed yet.
+     */
+    private static void addToFirstInLastOut(BackStackRecord transaction, BackStackRecord.Op op,
+            SparseArray<FragmentContainerTransition> transitioningFragments, boolean isPop,
+            boolean isOptimizedTransaction) {
+        final Fragment fragment = op.fragment;
+        final int containerId = fragment.mContainerId;
+        if (containerId == 0) {
+            return; // no container, no transition
+        }
+        final int command = isPop ? INVERSE_OPS[op.cmd] : op.cmd;
+        boolean setLastIn = false;
+        boolean wasRemoved = false;
+        boolean setFirstOut = false;
+        boolean wasAdded = false;
+        switch (command) {
+            case BackStackRecord.OP_SHOW:
+                if (isOptimizedTransaction) {
+                    setLastIn = fragment.mHiddenChanged && !fragment.mHidden && fragment.mAdded;
+                } else {
+                    setLastIn = fragment.mHidden;
+                }
+                wasAdded = true;
+                break;
+            case BackStackRecord.OP_ADD:
+            case BackStackRecord.OP_ATTACH:
+                if (isOptimizedTransaction) {
+                    setLastIn = fragment.mIsNewlyAdded;
+                } else {
+                    setLastIn = !fragment.mAdded && !fragment.mHidden;
+                }
+                wasAdded = true;
+                break;
+            case BackStackRecord.OP_HIDE:
+                if (isOptimizedTransaction) {
+                    setFirstOut = fragment.mHiddenChanged && fragment.mAdded && fragment.mHidden;
+                } else {
+                    setFirstOut = fragment.mAdded && !fragment.mHidden;
+                }
+                wasRemoved = true;
+                break;
+            case BackStackRecord.OP_REMOVE:
+            case BackStackRecord.OP_DETACH:
+                if (isOptimizedTransaction) {
+                    setFirstOut = !fragment.mAdded && fragment.mView != null
+                            && fragment.mView.getVisibility() == View.VISIBLE;
+                } else {
+                    setFirstOut = fragment.mAdded && !fragment.mHidden;
+                }
+                wasRemoved = true;
+                break;
+        }
+        FragmentContainerTransition containerTransition = transitioningFragments.get(containerId);
+        if (setLastIn) {
+            containerTransition =
+                    ensureContainer(containerTransition, transitioningFragments, containerId);
+            containerTransition.lastIn = fragment;
+            containerTransition.lastInIsPop = isPop;
+            containerTransition.lastInTransaction = transaction;
+        }
+        if (!isOptimizedTransaction && wasAdded) {
+            if (containerTransition != null && containerTransition.firstOut == fragment) {
+                containerTransition.firstOut = null;
+            }
+
+            /**
+             * Ensure that fragments that are entering are at least at the CREATED state
+             * so that they may load Transitions using TransitionInflater.
+             */
+            FragmentManagerImpl manager = transaction.mManager;
+            if (fragment.mState < Fragment.CREATED && manager.mCurState >= Fragment.CREATED
+                    && !transaction.mAllowOptimization) {
+                manager.makeActive(fragment);
+                manager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+            }
+        }
+        if (setFirstOut && (containerTransition == null || containerTransition.firstOut == null)) {
+            containerTransition =
+                    ensureContainer(containerTransition, transitioningFragments, containerId);
+            containerTransition.firstOut = fragment;
+            containerTransition.firstOutIsPop = isPop;
+            containerTransition.firstOutTransaction = transaction;
+        }
+
+        if (!isOptimizedTransaction && wasRemoved
+                && (containerTransition != null && containerTransition.lastIn == fragment)) {
+            containerTransition.lastIn = null;
+        }
+    }
+
+    /**
+     * Ensures that a FragmentContainerTransition has been added to the SparseArray. If so,
+     * it returns the existing one. If not, one is created and added to the SparseArray and
+     * returned.
+     */
+    private static FragmentContainerTransition ensureContainer(
+            FragmentContainerTransition containerTransition,
+            SparseArray<FragmentContainerTransition> transitioningFragments, int containerId) {
+        if (containerTransition == null) {
+            containerTransition = new FragmentContainerTransition();
+            transitioningFragments.put(containerId, containerTransition);
+        }
+        return containerTransition;
+    }
+
+    /**
+     * Tracks the last fragment added and first fragment removed for fragment transitions.
+     * This also tracks which fragments are changed by push or pop transactions.
+     */
+    static class FragmentContainerTransition {
+        /**
+         * The last fragment added/attached/shown in its container
+         */
+        public Fragment lastIn;
+
+        /**
+         * true when lastIn was added during a pop transaction or false if added with a push
+         */
+        public boolean lastInIsPop;
+
+        /**
+         * The transaction that included the last in fragment
+         */
+        public BackStackRecord lastInTransaction;
+
+        /**
+         * The first fragment with a View that was removed/detached/hidden in its container.
+         */
+        public Fragment firstOut;
+
+        /**
+         * true when firstOut was removed during a pop transaction or false otherwise
+         */
+        public boolean firstOutIsPop;
+
+        /**
+         * The transaction that included the first out fragment
+         */
+        public BackStackRecord firstOutTransaction;
+    }
+}
diff --git a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java b/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
index 147251c..57c5094 100644
--- a/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
+++ b/fragment/jellybean/android/support/v4/app/BaseFragmentActivityJB.java
@@ -16,16 +16,20 @@
 
 package android.support.v4.app;
 
+import android.annotation.TargetApi;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 
 /**
  * Base class for {@code FragmentActivity} to be able to use v16 APIs.
  *
  * @hide
  */
+@RequiresApi(16)
+@TargetApi(16)
 abstract class BaseFragmentActivityJB extends BaseFragmentActivityHoneycomb {
 
     // We need to keep track of whether startActivityForResult originated from a Fragment, so we
diff --git a/fragment/tests/java/android/support/v4/BaseInstrumentationTestCase.java b/fragment/tests/java/android/support/v4/BaseInstrumentationTestCase.java
deleted file mode 100644
index 5f9ce85..0000000
--- a/fragment/tests/java/android/support/v4/BaseInstrumentationTestCase.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public abstract class BaseInstrumentationTestCase<A extends Activity> {
-    @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
-
-    protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        mActivityTestRule = new ActivityTestRule<A>(activityClass);
-    }
-}
diff --git a/fragment/tests/java/android/support/v4/BaseTestActivity.java b/fragment/tests/java/android/support/v4/BaseTestActivity.java
deleted file mode 100755
index e0682ce..0000000
--- a/fragment/tests/java/android/support/v4/BaseTestActivity.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public abstract class BaseTestActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        final int contentView = getContentViewLayoutResId();
-        if (contentView > 0) {
-            setContentView(contentView);
-        }
-        onContentViewSet();
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-    }
-
-    protected abstract int getContentViewLayoutResId();
-
-    protected void onContentViewSet() {}
-}
diff --git a/fragment/tests/java/android/support/v4/app/ChildFragmentStateTest.java b/fragment/tests/java/android/support/v4/app/ChildFragmentStateTest.java
index 188c3b1..357e8b7 100644
--- a/fragment/tests/java/android/support/v4/app/ChildFragmentStateTest.java
+++ b/fragment/tests/java/android/support/v4/app/ChildFragmentStateTest.java
@@ -17,27 +17,31 @@
 
 package android.support.v4.app;
 
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
 import android.support.v4.app.test.FragmentTestActivity.ParentFragment;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
 @SmallTest
-public class ChildFragmentStateTest extends ActivityInstrumentationTestCase2<FragmentTestActivity> {
-    public ChildFragmentStateTest() {
-        super(FragmentTestActivity.class);
-    }
+public class ChildFragmentStateTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
+    @Test
     @UiThreadTest
     public void testChildFragmentOrdering() throws Throwable {
         FragmentTestActivity.ParentFragment parent = new ParentFragment();
-        FragmentManager fm = getActivity().getSupportFragmentManager();
+        FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
         fm.beginTransaction().add(parent, "parent").commit();
         fm.executePendingTransactions();
         assertTrue(parent.wasAttachedInTime);
diff --git a/fragment/tests/java/android/support/v4/app/CountCallsFragment.java b/fragment/tests/java/android/support/v4/app/CountCallsFragment.java
new file mode 100644
index 0000000..bf9cadf
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/CountCallsFragment.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Counts the number of onCreateView, onHiddenChanged (onHide, onShow), onAttach, and onDetach
+ * calls.
+ */
+public class CountCallsFragment extends StrictViewFragment {
+    public int onCreateViewCount = 0;
+    public int onDestroyViewCount = 0;
+    public int onHideCount = 0;
+    public int onShowCount = 0;
+    public int onAttachCount = 0;
+    public int onDetachCount = 0;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        onCreateViewCount++;
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    @Override
+    public void onHiddenChanged(boolean hidden) {
+        if (hidden) {
+            onHideCount++;
+        } else {
+            onShowCount++;
+        }
+        super.onHiddenChanged(hidden);
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        onAttachCount++;
+        super.onAttach(context);
+    }
+
+    @Override
+    public void onDetach() {
+        onDetachCount++;
+        super.onDetach();
+    }
+
+    @Override
+    public void onDestroyView() {
+        onDestroyViewCount++;
+        super.onDestroyView();
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
new file mode 100644
index 0000000..c82b61d
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.os.SystemClock;
+
+import org.mockito.exceptions.base.MockitoAssertionError;
+import org.mockito.internal.verification.api.VerificationData;
+import org.mockito.invocation.Invocation;
+import org.mockito.verification.VerificationMode;
+
+import java.util.List;
+
+public class CtsMockitoUtils {
+    private CtsMockitoUtils() {}
+
+    public static VerificationMode within(long timeout) {
+        return new Within(timeout);
+    }
+
+    public static class Within implements VerificationMode {
+        private static final long TIME_SLICE = 50;
+        private final long mTimeout;
+
+        public Within(long timeout) {
+            mTimeout = timeout;
+        }
+
+        @Override
+        public void verify(VerificationData data) {
+            long timeout = mTimeout;
+            MockitoAssertionError errorToRethrow = null;
+            // Loop in the same way we do in PollingCheck, sleeping and then testing for the target
+            // invocation
+            while (timeout > 0) {
+                SystemClock.sleep(TIME_SLICE);
+
+                try {
+                    final List<Invocation> actualInvocations = data.getAllInvocations();
+                    // Iterate over all invocations so far to see if we have a match
+                    for (Invocation invocation : actualInvocations) {
+                        if (data.getWanted().matches(invocation)) {
+                            // Found our match within our timeout. Mark all invocations as verified
+                            markAllInvocationsAsVerified(data);
+                            // and return
+                            return;
+                        }
+                    }
+                } catch (MockitoAssertionError assertionError) {
+                    errorToRethrow = assertionError;
+                }
+
+                timeout -= TIME_SLICE;
+            }
+
+            if (errorToRethrow != null) {
+                throw errorToRethrow;
+            }
+
+            throw new MockitoAssertionError("Timed out while waiting " + mTimeout + "ms for "
+                    + data.getWanted().toString());
+        }
+
+        private void markAllInvocationsAsVerified(VerificationData data) {
+            for (Invocation invocation : data.getAllInvocations()) {
+                invocation.markVerified();
+                data.getWanted().captureArgumentsFrom(invocation);
+            }
+        }
+    }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
new file mode 100644
index 0000000..923ad6d
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.os.Parcelable;
+import android.support.annotation.AnimRes;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.util.Pair;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentAnimationTest {
+    // These are pretend resource IDs for animators. We don't need real ones since we
+    // load them by overriding onCreateAnimator
+    @AnimRes
+    private static final int ENTER = 1;
+    @AnimRes
+    private static final int EXIT = 2;
+    @AnimRes
+    private static final int POP_ENTER = 3;
+    @AnimRes
+    private static final int POP_EXIT = 4;
+
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setupContainer() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+    }
+
+    // Ensure that adding and popping a Fragment uses the enter and popExit animators
+    @Test
+    public void addAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that removing and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void removeAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Ensure that showing and popping a Fragment uses the enter and popExit animators
+    @Test
+    public void showAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .show(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void hideAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .hide(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Ensure that attaching and popping a Fragment uses the enter and popExit animators
+    @Test
+    public void attachAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .attach(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
+    @Test
+    public void detachAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .detach(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPopEnter(fragment);
+    }
+
+    // Replace should exit the existing fragments and enter the added fragment, then
+    // popping should popExit the removed fragment and popEnter the added fragments
+    @Test
+    public void replaceAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .add(R.id.fragmentContainer, fragment2, "2")
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final AnimatorFragment fragment3 = new AnimatorFragment();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment3)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment1, 1, false, EXIT);
+        assertFragmentAnimation(fragment2, 1, false, EXIT);
+        assertFragmentAnimation(fragment3, 1, true, ENTER);
+
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
+        final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
+        final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
+        int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
+        assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
+        assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
+    }
+
+    // Ensure that adding and popping a Fragment uses the enter and popExit animators,
+    // but the animators are delayed when an entering Fragment is postponed.
+    @Test
+    public void postponedAddAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fragment.postponeEnterTransition();
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment, 0);
+        fragment.startPostponedEnterTransition();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertEnterPopExit(fragment);
+    }
+
+    // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
+    // but the animators are delayed when an entering Fragment is postponed.
+    @Test
+    public void postponedRemoveAnimators() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment = new AnimatorFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPostponedPopEnter(fragment);
+    }
+
+    // Ensure that adding and popping a Fragment is postponed in both directions
+    // when the fragments have been marked for postponing.
+    @Test
+    public void postponedAddRemove() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fragment2.postponeEnterTransition();
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment2, 0);
+        assertNotNull(fragment1.getView());
+        assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+        assertTrue(fragment1.getView().isAttachedToWindow());
+
+        fragment2.startPostponedEnterTransition();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertExitPostponedPopEnter(fragment1);
+    }
+
+    // Popping a postponed transaction should result in no animators
+    @Test
+    public void popPostponed() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final AnimatorFragment fragment1 = new AnimatorFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertEquals(0, fragment1.numAnimators);
+
+        final AnimatorFragment fragment2 = new AnimatorFragment();
+        fragment2.postponeEnterTransition();
+
+        fm.beginTransaction()
+                .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponed(fragment2, 0);
+
+        // Now pop the postponed transaction
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertNotNull(fragment1.getView());
+        assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+        assertTrue(fragment1.getView().isAttachedToWindow());
+        assertTrue(fragment1.isAdded());
+
+        assertNull(fragment2.getView());
+        assertFalse(fragment2.isAdded());
+
+        assertEquals(0, fragment1.numAnimators);
+        assertEquals(0, fragment2.numAnimators);
+        assertNull(fragment1.animation);
+        assertNull(fragment2.animation);
+    }
+
+    // Make sure that if the state was saved while a Fragment was animating that its
+    // state is proper after restoring.
+    @Test
+    public void saveWhileAnimatingAway() throws Throwable {
+        final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(fc1, null);
+
+        final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+        StrictViewFragment fragment1 = new StrictViewFragment();
+        fragment1.setLayoutId(R.layout.scene1);
+        fm1.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        StrictViewFragment fragment2 = new StrictViewFragment();
+
+        fm1.beginTransaction()
+                .setCustomAnimations(0, 0, 0, R.anim.long_fade_out)
+                .replace(R.id.fragmentContainer, fragment2, "2")
+                .addToBackStack(null)
+                .commit();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fm1.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fm1.popBackStack();
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fm1.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        // Now fragment2 should be animating away
+        assertFalse(fragment2.isAdded());
+        assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
+
+        Pair<Parcelable, FragmentManagerNonConfig> state =
+                FragmentTestUtil.destroy(fc1);
+
+        final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(fc2, state);
+
+        final FragmentManager fm2 = fc2.getSupportFragmentManager();
+        Fragment fragment2restored = fm2.findFragmentByTag("2");
+        assertNull(fragment2restored);
+
+        Fragment fragment1restored = fm2.findFragmentByTag("1");
+        assertNotNull(fragment1restored);
+        assertNotNull(fragment1restored.getView());
+    }
+
+    private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, true, ENTER);
+
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertFragmentAnimation(fragment, 2, false, POP_EXIT);
+    }
+
+    private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, false, EXIT);
+
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.popBackStack();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
+
+        boolean isSameFragment = replacement == fragment;
+        int expectedAnimators = isSameFragment ? 2 : 1;
+        assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
+    }
+
+    private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
+        assertFragmentAnimation(fragment, 1, false, EXIT);
+
+        fragment.postponeEnterTransition();
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponed(fragment, 1);
+
+        fragment.startPostponedEnterTransition();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertFragmentAnimation(fragment, 2, true, POP_ENTER);
+    }
+
+    private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
+            boolean isEnter, int animatorResourceId) throws InterruptedException {
+        assertEquals(numAnimators, fragment.numAnimators);
+        assertEquals(isEnter, fragment.enter);
+        assertEquals(animatorResourceId, fragment.resourceId);
+        assertNotNull(fragment.animation);
+        assertTrue(FragmentTestUtil.waitForAnimationEnd(200, fragment.animation));
+        assertTrue(fragment.animation.hasStarted());
+    }
+
+    private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
+            throws InterruptedException {
+        assertTrue(fragment.mOnCreateViewCalled);
+        assertEquals(View.INVISIBLE, fragment.getView().getVisibility());
+        assertEquals(expectedAnimators, fragment.numAnimators);
+    }
+
+    public static class AnimatorFragment extends StrictViewFragment {
+        public int numAnimators;
+        public Animation animation;
+        public boolean enter;
+        public int resourceId;
+
+        @Override
+        public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+            if (nextAnim == 0) {
+                return null;
+            }
+            this.numAnimators++;
+            this.animation = new TranslateAnimation(-10, 0, 0, 0);
+            this.animation.setDuration(1);
+            this.resourceId = nextAnim;
+            this.enter = enter;
+            return this.animation;
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
index 6979ba7..02cca09 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
@@ -17,6 +17,16 @@
 
 package android.support.v4.app;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotEquals;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -25,17 +35,17 @@
 import android.support.annotation.Nullable;
 import android.support.fragment.test.R;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.EmptyFragmentTestActivity;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
-
 import android.widget.TextView;
+
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -44,15 +54,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertNotEquals;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class FragmentLifecycleTest {
diff --git a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
new file mode 100644
index 0000000..39dbf11
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static org.junit.Assert.*;
+
+import android.app.Instrumentation;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentOptimizationTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private ViewGroup mContainer;
+    private FragmentManager mFM;
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setup() {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        mContainer = (ViewGroup) mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        mFM = mActivityRule.getActivity().getSupportFragmentManager();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    // Test that when you add and replace a fragment that only the replace's add
+    // actually creates a View.
+    @Test
+    public void addReplace() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        assertEquals(0, fragment1.onCreateViewCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.popBackStack();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+    }
+
+    // Test that it is possible to merge a transaction that starts with pop and adds
+    // the same view back again.
+    @Test
+    public void startWithPop() throws Throwable {
+        // Start with a single fragment on the back stack
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        mFM.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // Now pop and add
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.popBackStack();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+    // Popping the back stack in the middle of other operations doesn't fool it.
+    @Test
+    public void middlePop() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.popBackStack();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+        assertEquals(0, fragment1.onAttachCount);
+        assertEquals(1, fragment2.onCreateViewCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(1, fragment2.onDetachCount);
+    }
+
+    // ensure that removing a view after adding it is optimized into no
+    // View being created. Hide still gets notified.
+    @Test
+    public void optimizeRemove() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final int[] id = new int[1];
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                id[0] = mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(0, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(0, fragment1.onAttachCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id[0],
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+        FragmentTestUtil.assertChildren(mContainer);
+        assertEquals(0, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(0, fragment1.onAttachCount);
+    }
+
+    // Ensure that removing and adding the same view results in no operation
+    @Test
+    public void optimizeAdd() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // should be optimized out
+        assertEquals(1, fragment1.onCreateViewCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // optimize out going back, too
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+    // detaching, then attaching results in on change. Hide still functions
+    @Test
+    public void optimizeAttach() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(1, fragment1.onAttachCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // can optimize out the detach/attach
+        assertEquals(0, fragment1.onDestroyViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // optimized out again, but not the show
+        assertEquals(0, fragment1.onDestroyViewCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(1, fragment1.onCreateViewCount);
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+    }
+
+    // attaching, then detaching shouldn't result in a View being created
+    @Test
+    public void optimizeDetach() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .detach(fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        // the add detach is not fully optimized out
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertTrue(fragment1.isDetached());
+        assertEquals(0, fragment1.onCreateViewCount);
+        FragmentTestUtil.assertChildren(mContainer);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer);
+        // can optimize out the attach/detach, and the hide call
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertTrue(fragment1.isHidden());
+        assertEquals(0, fragment1.onShowCount);
+
+        mFM.popBackStack(id, 0);
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer);
+
+        // we can optimize out the attach/detach on the way back
+        assertEquals(1, fragment1.onAttachCount);
+        assertEquals(0, fragment1.onDetachCount);
+        assertEquals(1, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+        assertFalse(fragment1.isHidden());
+    }
+
+    // show, then hide should optimize out
+    @Test
+    public void optimizeHide() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .hide(fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // optimize out hide/show
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        // still optimized out
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        // The show/hide can be optimized out and nothing should change.
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        // the detach/attach should not affect the show/hide, so show/hide should cancel each other
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(1, fragment1.onHideCount);
+    }
+
+    // hiding and showing the same view should optimize out
+    @Test
+    public void optimizeShow() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+                mFM.executePendingTransactions();
+            }
+        });
+
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+        // can optimize out the show/hide
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id,
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+        assertEquals(0, fragment1.onShowCount);
+        assertEquals(0, fragment1.onHideCount);
+    }
+
+    // The View order shouldn't be messed up by optimization -- a view that
+    // is optimized to not remove/add should be in its correct position after
+    // the transaction completes.
+    @Test
+    public void viewOrder() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        int id = mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2, fragment1);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, id, 0);
+        FragmentTestUtil.assertChildren(mContainer, fragment1);
+    }
+
+    // Popping an added transaction results in no operation
+    @Test
+    public void addPopBackStack() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer);
+
+        // Was never instantiated because it was popped before anything could happen
+        assertEquals(0, fragment1.onCreateViewCount);
+    }
+
+    // A non-back-stack transaction doesn't interfere with back stack add/pop
+    // optimization.
+    @Test
+    public void popNonBackStack() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .commit();
+                mFM.popBackStack();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        // It should be optimized with the replace, so no View creation
+        assertEquals(0, fragment1.onCreateViewCount);
+    }
+
+    // When optimization is disabled, the transaction prior to the disabled optimization
+    // transaction should all be run prior to running the non-optimized transaction.
+    @Test
+    public void noOptimization() throws Throwable {
+        final CountCallsFragment fragment1 = new CountCallsFragment();
+        final CountCallsFragment fragment2 = new CountCallsFragment();
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mFM.beginTransaction()
+                        .add(R.id.fragmentContainer, fragment1)
+                        .addToBackStack(null)
+                        .commit();
+                mFM.beginTransaction()
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .setAllowOptimization(false)
+                        .commit();
+                mFM.executePendingTransactions();
+            }
+        });
+        FragmentTestUtil.assertChildren(mContainer, fragment2);
+
+        // No optimization, so fragment1 should have created its View
+        assertEquals(1, fragment1.onCreateViewCount);
+    }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java b/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
index c8aaf66..f7c8a81 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
@@ -15,24 +15,24 @@
  */
 package android.support.v4.app;
 
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.BaseInstrumentationTestCase;
-import android.support.v4.app.test.FragmentResultActivity;
-import android.support.v4.app.test.FragmentTestActivity;
-import android.support.fragment.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static junit.framework.TestCase.fail;
 
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentResultActivity;
+import android.support.v4.app.test.FragmentTestActivity;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,23 +40,24 @@
  * Tests for Fragment startActivityForResult and startIntentSenderForResult.
  */
 @RunWith(AndroidJUnit4.class)
-public class FragmentReceiveResultTest extends BaseInstrumentationTestCase<FragmentTestActivity> {
+public class FragmentReceiveResultTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
     private FragmentTestActivity mActivity;
     private TestFragment mFragment;
 
-    public FragmentReceiveResultTest() {
-        super(FragmentTestActivity.class);
-    }
 
     @Before
-    public void setUp() {
-        mActivity = mActivityTestRule.getActivity();
+    public void setup() throws Throwable {
+        mActivity = mActivityRule.getActivity();
         mFragment = attachTestFragment();
     }
 
     @Test
     @SmallTest
-    public void testStartActivityForResultOk() {
+    public void testStartActivityForResultOk() throws Throwable {
         startActivityForResult(10, Activity.RESULT_OK, "content 10");
 
         assertTrue("Fragment should receive result", mFragment.mHasResult);
@@ -67,7 +68,7 @@
 
     @Test
     @SmallTest
-    public void testStartActivityForResultCanceled() {
+    public void testStartActivityForResultCanceled() throws Throwable {
         startActivityForResult(20, Activity.RESULT_CANCELED, "content 20");
 
         assertTrue("Fragment should receive result", mFragment.mHasResult);
@@ -78,7 +79,7 @@
 
     @Test
     @SmallTest
-    public void testStartIntentSenderForResultOk() {
+    public void testStartIntentSenderForResultOk() throws Throwable {
         startIntentSenderForResult(30, Activity.RESULT_OK, "content 30");
 
         assertTrue("Fragment should receive result", mFragment.mHasResult);
@@ -89,7 +90,7 @@
 
     @Test
     @SmallTest
-    public void testStartIntentSenderForResultCanceled() {
+    public void testStartIntentSenderForResultCanceled() throws Throwable {
         startIntentSenderForResult(40, Activity.RESULT_CANCELED, "content 40");
 
         assertTrue("Fragment should receive result", mFragment.mHasResult);
@@ -98,9 +99,9 @@
         assertEquals("content 40", mFragment.mResultContent);
     }
 
-    private TestFragment attachTestFragment() {
+    private TestFragment attachTestFragment() throws Throwable {
         final TestFragment fragment = new TestFragment();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().beginTransaction()
@@ -115,8 +116,8 @@
     }
 
     private void startActivityForResult(final int requestCode, final int resultCode,
-            final String content) {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            final String content) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Intent intent = new Intent(mActivity, FragmentResultActivity.class);
@@ -130,8 +131,8 @@
     }
 
     private void startIntentSenderForResult(final int requestCode, final int resultCode,
-            final String content) {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            final String content) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Intent intent = new Intent(mActivity, FragmentResultActivity.class);
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
index 89566eb..6aadeb7 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
@@ -15,76 +15,108 @@
  */
 package android.support.v4.app;
 
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.Activity;
 import android.app.Fragment;
+import android.app.Instrumentation;
 import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
 import android.support.v4.app.test.FragmentTestActivity.TestFragment;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
 import android.view.KeyEvent;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Test to prevent regressions in SupportFragmentManager fragment replace method. See b/24693644
  */
-public class FragmentReplaceTest extends
-        ActivityInstrumentationTestCase2<FragmentTestActivity> {
-    private FragmentTestActivity mActivity;
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class FragmentReplaceTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
 
+    private Instrumentation mInstrumentation;
 
-    public FragmentReplaceTest() {
-        super(FragmentTestActivity.class);
+    @Before
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
-    }
-
-    @UiThreadTest
+    @Test
     public void testReplaceFragment() throws Throwable {
-        mActivity.getSupportFragmentManager().beginTransaction()
+        final FragmentActivity activity = mActivityRule.getActivity();
+        final FragmentManager fm = activity.getSupportFragmentManager();
+
+        fm.beginTransaction()
                 .add(R.id.content, TestFragment.create(R.layout.fragment_a))
                 .addToBackStack(null)
                 .commit();
-        mActivity.getSupportFragmentManager().executePendingTransactions();
-        assertNotNull(mActivity.findViewById(R.id.textA));
-        assertNull(mActivity.findViewById(R.id.textB));
-        assertNull(mActivity.findViewById(R.id.textC));
+        executePendingTransactions(fm);
+        assertNotNull(activity.findViewById(R.id.textA));
+        assertNull(activity.findViewById(R.id.textB));
+        assertNull(activity.findViewById(R.id.textC));
 
 
-        mActivity.getSupportFragmentManager().beginTransaction()
+        fm.beginTransaction()
                 .add(R.id.content, TestFragment.create(R.layout.fragment_b))
                 .addToBackStack(null)
                 .commit();
-        mActivity.getSupportFragmentManager().executePendingTransactions();
-        assertNotNull(mActivity.findViewById(R.id.textA));
-        assertNotNull(mActivity.findViewById(R.id.textB));
-        assertNull(mActivity.findViewById(R.id.textC));
+        executePendingTransactions(fm);
+        assertNotNull(activity.findViewById(R.id.textA));
+        assertNotNull(activity.findViewById(R.id.textB));
+        assertNull(activity.findViewById(R.id.textC));
 
-        mActivity.getSupportFragmentManager().beginTransaction()
+        activity.getSupportFragmentManager().beginTransaction()
                 .replace(R.id.content, TestFragment.create(R.layout.fragment_c))
                 .addToBackStack(null)
                 .commit();
-        mActivity.getSupportFragmentManager().executePendingTransactions();
-        assertNull(mActivity.findViewById(R.id.textA));
-        assertNull(mActivity.findViewById(R.id.textB));
-        assertNotNull(mActivity.findViewById(R.id.textC));
+        executePendingTransactions(fm);
+        assertNull(activity.findViewById(R.id.textA));
+        assertNull(activity.findViewById(R.id.textB));
+        assertNotNull(activity.findViewById(R.id.textC));
+    }
+
+    private void executePendingTransactions(final FragmentManager fm) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                fm.executePendingTransactions();
+            }
+        });
     }
 
     @SdkSuppress(minSdkVersion = 11)
-    @UiThreadTest
+    @Test
     public void testBackPressWithFrameworkFragment() throws Throwable {
-        mActivity.getFragmentManager().beginTransaction()
+        final Activity activity = mActivityRule.getActivity();
+
+        activity.getFragmentManager().beginTransaction()
                 .add(R.id.content, new Fragment())
                 .addToBackStack(null)
                 .commit();
-        mActivity.getFragmentManager().executePendingTransactions();
-        assertEquals(1, mActivity.getFragmentManager().getBackStackEntryCount());
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.getFragmentManager().executePendingTransactions();
+            }
+        });
+        assertEquals(1, activity.getFragmentManager().getBackStackEntryCount());
 
-        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
 
-        assertEquals(0, mActivity.getFragmentManager().getBackStackEntryCount());
+        assertEquals(0, activity.getFragmentManager().getBackStackEntryCount());
     }
 }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTest.java b/fragment/tests/java/android/support/v4/app/FragmentTest.java
index 8c2c028..35f8e35 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTest.java
@@ -15,38 +15,51 @@
  */
 package android.support.v4.app;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.app.Instrumentation;
 import android.os.Bundle;
 import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Miscellaneous tests for fragments that aren't big enough to belong to their own classes.
  */
-public class FragmentTest extends
-        ActivityInstrumentationTestCase2<FragmentTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class FragmentTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
     private FragmentTestActivity mActivity;
+    private Instrumentation mInstrumentation;
 
-    public FragmentTest() {
-        super(FragmentTestActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
     @SmallTest
     @UiThreadTest
+    @Test
     public void testOnCreateOrder() throws Throwable {
         OrderFragment fragment1 = new OrderFragment();
         OrderFragment fragment2 = new OrderFragment();
@@ -60,10 +73,11 @@
     }
 
     @SmallTest
+    @Test
     public void testChildFragmentManagerGone() throws Throwable {
         final FragmentA fragmentA = new FragmentA();
         final FragmentB fragmentB = new FragmentB();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().beginTransaction()
@@ -71,8 +85,8 @@
                         .commitNow();
             }
         });
-        getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().beginTransaction()
@@ -85,7 +99,7 @@
         });
         // Wait for the middle of the animation
         Thread.sleep(150);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().beginTransaction()
@@ -98,8 +112,8 @@
         });
         // Wait for the middle of the animation
         Thread.sleep(150);
-        getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().popBackStack();
@@ -107,8 +121,8 @@
         });
         // Wait for the middle of the animation
         Thread.sleep(150);
-        getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().popBackStack();
@@ -118,6 +132,7 @@
 
     @MediumTest
     @UiThreadTest
+    @Test
     public void testViewOrder() throws Throwable {
         FragmentA fragmentA = new FragmentA();
         FragmentB fragmentB = new FragmentB();
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
new file mode 100644
index 0000000..ba5875a
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.util.Pair;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+
+public class FragmentTestUtil {
+    private static final Runnable DO_NOTHING = new Runnable() {
+        @Override
+        public void run() {
+        }
+    };
+
+    public static void waitForExecution(final ActivityTestRule<FragmentTestActivity> rule) {
+        // Wait for two cycles. When starting a postponed transition, it will post to
+        // the UI thread and then the execution will be added onto the queue after that.
+        // The two-cycle wait makes sure fragments have the opportunity to complete both
+        // before returning.
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(DO_NOTHING);
+        instrumentation.runOnMainSync(DO_NOTHING);
+    }
+
+    public static boolean executePendingTransactions(
+            final ActivityTestRule<FragmentTestActivity> rule) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final boolean[] ret = new boolean[1];
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                ret[0] =
+                        rule.getActivity().getSupportFragmentManager().executePendingTransactions();
+            }
+        });
+        return ret[0];
+    }
+
+    public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final boolean[] ret = new boolean[1];
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate();
+            }
+        });
+        return ret[0];
+    }
+
+    public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+            final int id, final int flags) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final boolean[] ret = new boolean[1];
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate(id,
+                        flags);
+            }
+        });
+        return ret[0];
+    }
+
+    public static boolean popBackStackImmediate(final ActivityTestRule<FragmentTestActivity> rule,
+            final String name, final int flags) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final boolean[] ret = new boolean[1];
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                ret[0] = rule.getActivity().getSupportFragmentManager().popBackStackImmediate(name,
+                        flags);
+            }
+        });
+        return ret[0];
+    }
+
+    public static void setContentView(final ActivityTestRule<FragmentTestActivity> rule,
+            final int layoutId) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                rule.getActivity().setContentView(layoutId);
+            }
+        });
+    }
+
+    public static void assertChildren(ViewGroup container, Fragment... fragments) {
+        final int numFragments = fragments == null ? 0 : fragments.length;
+        assertEquals("There aren't the correct number of fragment Views in its container",
+                numFragments, container.getChildCount());
+        for (int i = 0; i < numFragments; i++) {
+            assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+                    fragments[i].getView());
+        }
+    }
+
+    public static FragmentController createController(ActivityTestRule<FragmentTestActivity> rule) {
+        final FragmentController[] controller = new FragmentController[1];
+        final FragmentTestActivity activity = rule.getActivity();
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                Handler handler = new Handler();
+                HostCallbacks hostCallbacks = new HostCallbacks(activity, handler, 0);
+                controller[0] = FragmentController.createController(hostCallbacks);
+            }
+        });
+        return controller[0];
+    }
+
+    public static void resume(final FragmentController fragmentController,
+            final Pair<Parcelable, FragmentManagerNonConfig> savedState) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fragmentController.attachHost(null);
+                if (savedState != null) {
+                    fragmentController.restoreAllState(savedState.first, savedState.second);
+                }
+                fragmentController.dispatchCreate();
+                fragmentController.dispatchActivityCreated();
+                fragmentController.noteStateNotSaved();
+                fragmentController.execPendingActions();
+                fragmentController.dispatchStart();
+                fragmentController.reportLoaderStart();
+                fragmentController.dispatchResume();
+                fragmentController.execPendingActions();
+            }
+        });
+    }
+
+    public static Pair<Parcelable, FragmentManagerNonConfig> destroy(
+            final FragmentController fragmentController) {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Pair<Parcelable, FragmentManagerNonConfig>[] result = new Pair[1];
+        instrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fragmentController.dispatchPause();
+                final Parcelable savedState = fragmentController.saveAllState();
+                final FragmentManagerNonConfig nonConfig =
+                        fragmentController.retainNestedNonConfig();
+                fragmentController.dispatchStop();
+                fragmentController.doLoaderStop(false);
+                fragmentController.dispatchDestroy();
+                fragmentController.doLoaderDestroy();
+                result[0] = Pair.create(savedState, nonConfig);
+            }
+        });
+        return result[0];
+    }
+
+    public static boolean waitForAnimationEnd(long timeout, final Animation animation) {
+        long endTime = SystemClock.uptimeMillis() + timeout;
+        final boolean[] hasEnded = new boolean[1];
+        Runnable check = new Runnable() {
+            @Override
+            public void run() {
+                hasEnded[0] = animation.hasEnded();
+            }
+        };
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        do {
+            SystemClock.sleep(10);
+            instrumentation.runOnMainSync(check);
+        } while (!hasEnded[0] && SystemClock.uptimeMillis() < endTime);
+        return hasEnded[0];
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
index c42debd..117ca9d 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransactionTest.java
@@ -20,6 +20,7 @@
 
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
@@ -32,6 +33,7 @@
 /**
  * Tests usage of the {@link FragmentTransaction} class.
  */
+@MediumTest
 @RunWith(AndroidJUnit4.class)
 public class FragmentTransactionTest {
 
@@ -47,9 +49,9 @@
     }
 
     @Test
-    public void testAddTransactionWithValidFragment() {
+    public void testAddTransactionWithValidFragment() throws Throwable {
         final Fragment fragment = new CorrectFragment();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.getSupportFragmentManager().beginTransaction()
@@ -64,9 +66,9 @@
     }
 
     @Test
-    public void testAddTransactionWithPrivateFragment() {
+    public void testAddTransactionWithPrivateFragment() throws Throwable {
         final Fragment fragment = new PrivateFragment();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean exceptionThrown = false;
@@ -88,9 +90,9 @@
     }
 
     @Test
-    public void testAddTransactionWithPackagePrivateFragment() {
+    public void testAddTransactionWithPackagePrivateFragment() throws Throwable {
         final Fragment fragment = new PackagePrivateFragment();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean exceptionThrown = false;
@@ -112,9 +114,9 @@
     }
 
     @Test
-    public void testAddTransactionWithAnonymousFragment() {
+    public void testAddTransactionWithAnonymousFragment() throws Throwable {
         final Fragment fragment = new Fragment() {};
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean exceptionThrown = false;
@@ -136,9 +138,9 @@
     }
 
     @Test
-    public void testAddTransactionWithNonStaticFragment() {
+    public void testAddTransactionWithNonStaticFragment() throws Throwable {
         final Fragment fragment = new NonStaticFragment();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean exceptionThrown = false;
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
index 0b0c736..f0f2a02 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,366 +15,783 @@
  */
 package android.support.v4.app;
 
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
+import static junit.framework.Assert.assertNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.graphics.Rect;
 import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
 import android.support.v4.app.test.FragmentTestActivity;
-import android.support.v4.app.test.FragmentTestActivity.TestFragment;
-import android.support.v4.view.ViewCompat;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.transition.TransitionSet;
 import android.view.View;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 @MediumTest
-public class FragmentTransitionTest extends
-        ActivityInstrumentationTestCase2<FragmentTestActivity> {
-    private TestFragment mStartFragment;
-    private TestFragment mMidFragment;
-    private TestFragment mEndFragment;
-    private FragmentTestActivity mActivity;
+@RunWith(Parameterized.class)
+public class FragmentTransitionTest {
+    private final boolean mOptimize;
 
-    public FragmentTransitionTest() {
-        super(FragmentTestActivity.class);
+    @Parameterized.Parameters
+    public static Object[] data() {
+        return new Boolean[] {
+                false, true
+        };
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mStartFragment = null;
-        mMidFragment = null;
-        mEndFragment = null;
-        mActivity = getActivity();
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private FragmentManager mFragmentManager;
+
+    public FragmentTransitionTest(final boolean optimize) {
+        mOptimize = optimize;
     }
 
-    public void testFragmentTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        runTestOnUiThread(new Runnable() {
+    @Before
+    public void setup() throws Throwable {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mFragmentManager = mActivityRule.getActivity().getSupportFragmentManager();
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+    }
+
+    // Test that normal view transitions (enter, exit, reenter, return) run with
+    // a single fragment.
+    @Test
+    public void enterExitTransitions() throws Throwable {
+        // enter transition
+        TransitionFragment fragment = setupInitialFragment();
+        final View blue = findBlue();
+        final View green = findBlue();
+
+        // exit transition
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+
+        fragment.waitForTransition();
+        verifyAndClearTransition(fragment.exitTransition, null, green, blue);
+        verifyNoOtherTransitions(fragment);
+
+        // reenter transition
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        fragment.waitForTransition();
+        final View green2 = findGreen();
+        final View blue2 = findBlue();
+        verifyAndClearTransition(fragment.reenterTransition, null, green2, blue2);
+        verifyNoOtherTransitions(fragment);
+
+        // return transition
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        fragment.waitForTransition();
+        verifyAndClearTransition(fragment.returnTransition, null, green2, blue2);
+        verifyNoOtherTransitions(fragment);
+    }
+
+    // Test that shared elements transition from one fragment to the next
+    // and back during pop.
+    @Test
+    public void sharedElement() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        verifyTransition(fragment1, fragment2, "blueSquare");
+
+        // Now pop the back stack
+        verifyPopTransition(1, fragment2, fragment1);
+    }
+
+    // Test that shared element transitions through multiple fragments work together
+    @Test
+    public void intermediateFragment() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        final TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene3);
+
+        verifyTransition(fragment1, fragment2, "shared");
+
+        final TransitionFragment fragment3 = new TransitionFragment();
+        fragment3.setLayoutId(R.layout.scene2);
+
+        verifyTransition(fragment2, fragment3, "blueSquare");
+
+        // Should transfer backwards when popping multiple:
+        verifyPopTransition(2, fragment3, fragment1, fragment2);
+    }
+
+    // Adding/removing the same fragment multiple times shouldn't mess anything up
+    @Test
+    public void removeAdded() throws Throwable {
+        final TransitionFragment fragment1 = setupInitialFragment();
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+
+        final TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                final View sharedElement = mActivity.findViewById(R.id.hello);
-                assertEquals("source", ViewCompat.getTransitionName(sharedElement));
-
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .addSharedElement(sharedElement, "destination")
+                mFragmentManager.beginTransaction()
+                        .setAllowOptimization(mOptimize)
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .replace(R.id.fragmentContainer, fragment1)
+                        .replace(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
                         .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
             }
         });
-        waitForEnd(mEndFragment, TestFragment.ENTER);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.SHARED_ELEMENT_ENTER));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final View textView = mActivity.findViewById(R.id.hello);
-                assertEquals("destination", ViewCompat.getTransitionName(textView));
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForEnd(mStartFragment, TestFragment.REENTER);
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.REENTER));
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        // should be a normal transition from fragment1 to fragment2
+        fragment2.waitForTransition();
+        final View endBlue = findBlue();
+        final View endGreen = findGreen();
+        verifyAndClearTransition(fragment1.exitTransition, null, startBlue, startGreen);
+        verifyAndClearTransition(fragment2.enterTransition, null, endBlue, endGreen);
+        verifyNoOtherTransitions(fragment1);
+        verifyNoOtherTransitions(fragment2);
+
+        // Pop should also do the same thing
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        fragment1.waitForTransition();
+        final View popBlue = findBlue();
+        final View popGreen = findGreen();
+        verifyAndClearTransition(fragment1.reenterTransition, null, popBlue, popGreen);
+        verifyAndClearTransition(fragment2.returnTransition, null, endBlue, endGreen);
+        verifyNoOtherTransitions(fragment1);
+        verifyNoOtherTransitions(fragment2);
     }
 
-    public void testFirstOutLastInTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
+    // Make sure that shared elements on two different fragment containers don't interact
+    @Test
+    public void crossContainer() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+        TransitionFragment fragment1 = new TransitionFragment();
+        fragment1.setLayoutId(R.layout.scene1);
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene1);
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .add(R.id.fragmentContainer1, fragment1)
+                .add(R.id.fragmentContainer2, fragment2)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fragment1.waitForTransition();
+        final View greenSquare1 = findViewById(fragment1, R.id.greenSquare);
+        final View blueSquare1 = findViewById(fragment1, R.id.blueSquare);
+        verifyAndClearTransition(fragment1.enterTransition, null, greenSquare1, blueSquare1);
+        verifyNoOtherTransitions(fragment1);
+        fragment2.waitForTransition();
+        final View greenSquare2 = findViewById(fragment2, R.id.greenSquare);
+        final View blueSquare2 = findViewById(fragment2, R.id.blueSquare);
+        verifyAndClearTransition(fragment2.enterTransition, null, greenSquare2, blueSquare2);
+        verifyNoOtherTransitions(fragment2);
+
+        // Make sure the correct transitions are run when the target names
+        // are different in both shared elements. We may fool the system.
+        verifyCrossTransition(false, fragment1, fragment2);
+
+        // Make sure the correct transitions are run when the source names
+        // are different in both shared elements. We may fool the system.
+        verifyCrossTransition(true, fragment1, fragment2);
+    }
+
+    // Make sure that onSharedElementStart and onSharedElementEnd are called
+    @Test
+    public void callStartEndWithSharedElements() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        SharedElementCallback enterCallback = mock(SharedElementCallback.class);
+        fragment2.setEnterSharedElementCallback(enterCallback);
+
+        final View startBlue = findBlue();
+
+        verifyTransition(fragment1, fragment2, "blueSquare");
+
+        ArgumentCaptor<List> names = ArgumentCaptor.forClass(List.class);
+        ArgumentCaptor<List> views = ArgumentCaptor.forClass(List.class);
+        ArgumentCaptor<List> snapshots = ArgumentCaptor.forClass(List.class);
+        verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+                snapshots.capture());
+        assertEquals(1, names.getValue().size());
+        assertEquals(1, views.getValue().size());
+        assertNull(snapshots.getValue());
+        assertEquals("blueSquare", names.getValue().get(0));
+        assertEquals(startBlue, views.getValue().get(0));
+
+        final View endBlue = findBlue();
+
+        verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+                snapshots.capture());
+        assertEquals(1, names.getValue().size());
+        assertEquals(1, views.getValue().size());
+        assertNull(snapshots.getValue());
+        assertEquals("blueSquare", names.getValue().get(0));
+        assertEquals(endBlue, views.getValue().get(0));
+
+        // Now pop the back stack
+        reset(enterCallback);
+        verifyPopTransition(1, fragment2, fragment1);
+
+        verify(enterCallback).onSharedElementStart(names.capture(), views.capture(),
+                snapshots.capture());
+        assertEquals(1, names.getValue().size());
+        assertEquals(1, views.getValue().size());
+        assertNull(snapshots.getValue());
+        assertEquals("blueSquare", names.getValue().get(0));
+        assertEquals(endBlue, views.getValue().get(0));
+
+        final View reenterBlue = findBlue();
+
+        verify(enterCallback).onSharedElementEnd(names.capture(), views.capture(),
+                snapshots.capture());
+        assertEquals(1, names.getValue().size());
+        assertEquals(1, views.getValue().size());
+        assertNull(snapshots.getValue());
+        assertEquals("blueSquare", names.getValue().get(0));
+        assertEquals(reenterBlue, views.getValue().get(0));
+    }
+
+    // Make sure that onMapSharedElement works to change the shared element going out
+    @Test
+    public void onMapSharedElementOut() throws Throwable {
+        final TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+
+        final Rect startGreenBounds = getBoundsOnScreen(startGreen);
+
+        SharedElementCallback mapOut = new SharedElementCallback() {
+            @Override
+            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+                assertEquals(1, names.size());
+                assertEquals("blueSquare", names.get(0));
+                assertEquals(1, sharedElements.size());
+                assertEquals(startBlue, sharedElements.get("blueSquare"));
+                sharedElements.put("blueSquare", startGreen);
+            }
+        };
+        fragment1.setExitSharedElementCallback(mapOut);
+
+        mFragmentManager.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .setAllowOptimization(mOptimize)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View endBlue = findBlue();
+        final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+        verifyAndClearTransition(fragment2.sharedElementEnter, startGreenBounds, startGreen,
+                endBlue);
+
+        SharedElementCallback mapBack = new SharedElementCallback() {
+            @Override
+            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+                assertEquals(1, names.size());
+                assertEquals("blueSquare", names.get(0));
+                assertEquals(1, sharedElements.size());
+                final View expectedBlue = findViewById(fragment1, R.id.blueSquare);
+                assertEquals(expectedBlue, sharedElements.get("blueSquare"));
+                final View greenSquare = findViewById(fragment1, R.id.greenSquare);
+                sharedElements.put("blueSquare", greenSquare);
+            }
+        };
+        fragment1.setExitSharedElementCallback(mapBack);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View reenterGreen = findGreen();
+        verifyAndClearTransition(fragment2.sharedElementReturn, endBlueBounds, endBlue,
+                reenterGreen);
+    }
+
+    // Make sure that onMapSharedElement works to change the shared element target
+    @Test
+    public void onMapSharedElementIn() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        final TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+        SharedElementCallback mapIn = new SharedElementCallback() {
+            @Override
+            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+                assertEquals(1, names.size());
+                assertEquals("blueSquare", names.get(0));
+                assertEquals(1, sharedElements.size());
+                final View blueSquare = findViewById(fragment2, R.id.blueSquare);
+                assertEquals(blueSquare, sharedElements.get("blueSquare"));
+                final View greenSquare = findViewById(fragment2, R.id.greenSquare);
+                sharedElements.put("blueSquare", greenSquare);
+            }
+        };
+        fragment2.setEnterSharedElementCallback(mapIn);
+
+        mFragmentManager.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .setAllowOptimization(mOptimize)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View endGreen = findGreen();
+        final View endBlue = findBlue();
+        final Rect endGreenBounds = getBoundsOnScreen(endGreen);
+
+        verifyAndClearTransition(fragment2.sharedElementEnter, startBlueBounds, startBlue,
+                endGreen);
+
+        SharedElementCallback mapBack = new SharedElementCallback() {
+            @Override
+            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
+                assertEquals(1, names.size());
+                assertEquals("blueSquare", names.get(0));
+                assertEquals(1, sharedElements.size());
+                assertEquals(endBlue, sharedElements.get("blueSquare"));
+                sharedElements.put("blueSquare", endGreen);
+            }
+        };
+        fragment2.setEnterSharedElementCallback(mapBack);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View reenterBlue = findBlue();
+        verifyAndClearTransition(fragment2.sharedElementReturn, endGreenBounds, endGreen,
+                reenterBlue);
+    }
+
+    // Ensure that shared element transitions that have targets properly target the views
+    @Test
+    public void complexSharedElementTransition() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        ComplexTransitionFragment fragment2 = new ComplexTransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+        final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+        mFragmentManager.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .addSharedElement(startGreen, "greenSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View endBlue = findBlue();
+        final View endGreen = findGreen();
+        final Rect endBlueBounds = getBoundsOnScreen(endBlue);
+
+        verifyAndClearTransition(fragment2.sharedElementEnterTransition1, startBlueBounds,
+                startBlue, endBlue);
+        verifyAndClearTransition(fragment2.sharedElementEnterTransition2, startBlueBounds,
+                startGreen, endGreen);
+
+        // Now see if it works when popped
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View reenterBlue = findBlue();
+        final View reenterGreen = findGreen();
+
+        verifyAndClearTransition(fragment2.sharedElementReturnTransition1, endBlueBounds,
+                endBlue, reenterBlue);
+        verifyAndClearTransition(fragment2.sharedElementReturnTransition2, endBlueBounds,
+                endGreen, reenterGreen);
+    }
+
+    // Ensure that after transitions have executed that they don't have any targets or other
+    // unfortunate modifications.
+    @Test
+    public void transitionsEndUnchanged() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        verifyTransition(fragment1, fragment2, "blueSquare");
+        assertEquals(0, fragment1.exitTransition.getTargets().size());
+        assertEquals(0, fragment2.sharedElementEnter.getTargets().size());
+        assertEquals(0, fragment2.enterTransition.getTargets().size());
+        assertNull(fragment1.exitTransition.getEpicenterCallback());
+        assertNull(fragment2.enterTransition.getEpicenterCallback());
+        assertNull(fragment2.sharedElementEnter.getEpicenterCallback());
+
+        // Now pop the back stack
+        verifyPopTransition(1, fragment2, fragment1);
+
+        assertEquals(0, fragment2.returnTransition.getTargets().size());
+        assertEquals(0, fragment2.sharedElementReturn.getTargets().size());
+        assertEquals(0, fragment1.reenterTransition.getTargets().size());
+        assertNull(fragment2.returnTransition.getEpicenterCallback());
+        assertNull(fragment2.sharedElementReturn.getEpicenterCallback());
+        assertNull(fragment2.reenterTransition.getEpicenterCallback());
+    }
+
+    private TransitionFragment setupInitialFragment() throws Throwable {
+        TransitionFragment fragment1 = new TransitionFragment();
+        fragment1.setLayoutId(R.layout.scene1);
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.waitForTransition();
+        final View blueSquare1 = findBlue();
+        final View greenSquare1 = findGreen();
+        verifyAndClearTransition(fragment1.enterTransition, null, blueSquare1, greenSquare1);
+        verifyNoOtherTransitions(fragment1);
+        return fragment1;
+    }
+
+    private View findViewById(Fragment fragment, int id) {
+        return fragment.getView().findViewById(id);
+    }
+
+    private View findGreen() {
+        return mActivityRule.getActivity().findViewById(R.id.greenSquare);
+    }
+
+    private View findBlue() {
+        return mActivityRule.getActivity().findViewById(R.id.blueSquare);
+    }
+
+    private View findRed() {
+        return mActivityRule.getActivity().findViewById(R.id.redSquare);
+    }
+
+    private void verifyAndClearTransition(TargetTracking transition, Rect epicenter,
+            View... expected) {
+        if (epicenter == null) {
+            assertNull(transition.getCapturedEpicenter());
+        } else {
+            assertEquals(epicenter, transition.getCapturedEpicenter());
         }
-        launchStartFragment();
-        runTestOnUiThread(new Runnable() {
+        ArrayList<View> targets = transition.getTrackedTargets();
+        StringBuilder sb = new StringBuilder();
+        sb
+                .append("Expected: [")
+                .append(expected.length)
+                .append("] {");
+        boolean isFirst = true;
+        for (View view : expected) {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(view);
+        }
+        sb
+                .append("}, but got: [")
+                .append(targets.size())
+                .append("] {");
+        isFirst = true;
+        for (View view : targets) {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(view);
+        }
+        sb.append("}");
+        String errorMessage = sb.toString();
+
+        assertEquals(errorMessage, expected.length, targets.size());
+        for (View view : expected) {
+            assertTrue(errorMessage, targets.contains(view));
+        }
+        transition.clearTargets();
+    }
+
+    private void verifyNoOtherTransitions(TransitionFragment fragment) {
+        assertEquals(0, fragment.enterTransition.targets.size());
+        assertEquals(0, fragment.exitTransition.targets.size());
+        assertEquals(0, fragment.reenterTransition.targets.size());
+        assertEquals(0, fragment.returnTransition.targets.size());
+        assertEquals(0, fragment.sharedElementEnter.targets.size());
+        assertEquals(0, fragment.sharedElementReturn.targets.size());
+    }
+
+    private void verifyTransition(TransitionFragment from, TransitionFragment to,
+            String sharedElementName) throws Throwable {
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+        final View startRed = findRed();
+
+        final Rect startBlueRect = getBoundsOnScreen(startBlue);
+
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .addSharedElement(startBlue, sharedElementName)
+                .replace(R.id.fragmentContainer, to)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        to.waitForTransition();
+        final View endGreen = findGreen();
+        final View endBlue = findBlue();
+        final View endRed = findRed();
+        final Rect endBlueRect = getBoundsOnScreen(endBlue);
+
+        if (startRed != null) {
+            verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen, startRed);
+        } else {
+            verifyAndClearTransition(from.exitTransition, startBlueRect, startGreen);
+        }
+        verifyNoOtherTransitions(from);
+
+        if (endRed != null) {
+            verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen, endRed);
+        } else {
+            verifyAndClearTransition(to.enterTransition, endBlueRect, endGreen);
+        }
+        verifyAndClearTransition(to.sharedElementEnter, startBlueRect, startBlue, endBlue);
+        verifyNoOtherTransitions(to);
+    }
+
+    private void verifyCrossTransition(boolean swapSource,
+            TransitionFragment from1, TransitionFragment from2) throws Throwable {
+
+        final TransitionFragment to1 = new TransitionFragment();
+        to1.setLayoutId(R.layout.scene2);
+        final TransitionFragment to2 = new TransitionFragment();
+        to2.setLayoutId(R.layout.scene2);
+
+        final View fromExit1 = findViewById(from1, R.id.greenSquare);
+        final View fromShared1 = findViewById(from1, R.id.blueSquare);
+        final Rect fromSharedRect1 = getBoundsOnScreen(fromShared1);
+
+        final int fromExitId2 = swapSource ? R.id.blueSquare : R.id.greenSquare;
+        final int fromSharedId2 = swapSource ? R.id.greenSquare : R.id.blueSquare;
+        final View fromExit2 = findViewById(from2, fromExitId2);
+        final View fromShared2 = findViewById(from2, fromSharedId2);
+        final Rect fromSharedRect2 = getBoundsOnScreen(fromShared2);
+
+        final String sharedElementName = swapSource ? "blueSquare" : "greenSquare";
+
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mMidFragment = TestFragment.create(R.layout.fragment_middle);
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mMidFragment)
-                        .replace(R.id.content, mEndFragment)
+                mFragmentManager.beginTransaction()
+                        .setAllowOptimization(mOptimize)
+                        .addSharedElement(fromShared1, "blueSquare")
+                        .replace(R.id.fragmentContainer1, to1)
                         .addToBackStack(null)
                         .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForEnd(mEndFragment, TestFragment.ENTER);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
-
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
-
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
-        mStartFragment.clearNotifications();
-        mEndFragment.clearNotifications();
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForEnd(mEndFragment, TestFragment.RETURN);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
-        assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
-    }
-
-    public void testPopTwo() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mMidFragment = TestFragment.create(R.layout.fragment_middle);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mMidFragment)
+                mFragmentManager.beginTransaction()
+                        .setAllowOptimization(mOptimize)
+                        .addSharedElement(fromShared2, sharedElementName)
+                        .replace(R.id.fragmentContainer2, to2)
                         .addToBackStack(null)
                         .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
             }
         });
-        waitForEnd(mMidFragment, TestFragment.ENTER);
-        runTestOnUiThread(new Runnable() {
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        from1.waitForTransition();
+        from2.waitForTransition();
+        to1.waitForTransition();
+        to2.waitForTransition();
+
+        final View toEnter1 = findViewById(to1, R.id.greenSquare);
+        final View toShared1 = findViewById(to1, R.id.blueSquare);
+        final Rect toSharedRect1 = getBoundsOnScreen(toShared1);
+
+        final View toEnter2 = findViewById(to2, fromSharedId2);
+        final View toShared2 = findViewById(to2, fromExitId2);
+        final Rect toSharedRect2 = getBoundsOnScreen(toShared2);
+
+        verifyAndClearTransition(from1.exitTransition, fromSharedRect1, fromExit1);
+        verifyAndClearTransition(from2.exitTransition, fromSharedRect2, fromExit2);
+        verifyNoOtherTransitions(from1);
+        verifyNoOtherTransitions(from2);
+
+        verifyAndClearTransition(to1.enterTransition, toSharedRect1, toEnter1);
+        verifyAndClearTransition(to2.enterTransition, toSharedRect2, toEnter2);
+        verifyAndClearTransition(to1.sharedElementEnter, fromSharedRect1, fromShared1, toShared1);
+        verifyAndClearTransition(to2.sharedElementEnter, fromSharedRect2, fromShared2, toShared2);
+        verifyNoOtherTransitions(to1);
+        verifyNoOtherTransitions(to2);
+
+        // Now pop it back
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .addToBackStack(null)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
+                mFragmentManager.popBackStack();
+                mFragmentManager.popBackStack();
             }
         });
-        waitForEnd(mEndFragment, TestFragment.ENTER);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+        FragmentTestUtil.waitForExecution(mActivityRule);
 
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+        from1.waitForTransition();
+        from2.waitForTransition();
+        to1.waitForTransition();
+        to2.waitForTransition();
 
-        assertTrue(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertTrue(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+        final View returnEnter1 = findViewById(from1, R.id.greenSquare);
+        final View returnShared1 = findViewById(from1, R.id.blueSquare);
 
-        mStartFragment.clearNotifications();
-        mMidFragment.clearNotifications();
-        mEndFragment.clearNotifications();
+        final View returnEnter2 = findViewById(from2, fromExitId2);
+        final View returnShared2 = findViewById(from2, fromSharedId2);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                FragmentManager fm = mActivity.getSupportFragmentManager();
-                int id = fm.getBackStackEntryAt(0).getId();
-                fm.popBackStack(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
-                fm.executePendingTransactions();
-            }
-        });
-        waitForEnd(mEndFragment, TestFragment.RETURN);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.RETURN));
+        verifyAndClearTransition(to1.returnTransition, toSharedRect1, toEnter1);
+        verifyAndClearTransition(to2.returnTransition, toSharedRect2, toEnter2);
+        verifyAndClearTransition(to1.sharedElementReturn, toSharedRect1, toShared1, returnShared1);
+        verifyAndClearTransition(to2.sharedElementReturn, toSharedRect2, toShared2, returnShared2);
+        verifyNoOtherTransitions(to1);
+        verifyNoOtherTransitions(to2);
 
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
-        assertTrue(mStartFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
+        verifyAndClearTransition(from1.reenterTransition, fromSharedRect1, returnEnter1);
+        verifyAndClearTransition(from2.reenterTransition, fromSharedRect2, returnEnter2);
+        verifyNoOtherTransitions(from1);
+        verifyNoOtherTransitions(from2);
     }
 
-    public void testNullTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
+    private void verifyPopTransition(final int numPops, TransitionFragment from,
+            TransitionFragment to, TransitionFragment... others) throws Throwable {
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+        final View startRed = findRed();
+        final Rect startSharedRect = getBoundsOnScreen(startBlue);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < numPops; i++) {
+                    mFragmentManager.popBackStack();
+                }
+            }
+        });
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        to.waitForTransition();
+        final View endGreen = findGreen();
+        final View endBlue = findBlue();
+        final View endRed = findRed();
+        final Rect endSharedRect = getBoundsOnScreen(endBlue);
+
+        if (startRed != null) {
+            verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen, startRed);
+        } else {
+            verifyAndClearTransition(from.returnTransition, startSharedRect, startGreen);
         }
-        getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mStartFragment = TestFragment.create(R.layout.fragment_start);
-                mStartFragment.clearTransitions();
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mStartFragment)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForStart(mStartFragment, TestFragment.ENTER);
-        // No transitions
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
+        verifyAndClearTransition(from.sharedElementReturn, startSharedRect, startBlue, endBlue);
+        verifyNoOtherTransitions(from);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mMidFragment = TestFragment.create(R.layout.fragment_middle);
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mEndFragment.clearTransitions();
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mMidFragment)
-                        .replace(R.id.content, mEndFragment)
-                        .addToBackStack(null)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForStart(mEndFragment, TestFragment.ENTER);
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
-
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
-
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForStart(mEndFragment, TestFragment.RETURN);
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
-
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
-    }
-
-    public void testRemoveAdded() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
+        if (endRed != null) {
+            verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen, endRed);
+        } else {
+            verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen);
         }
-        launchStartFragment();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .replace(R.id.content, mStartFragment)
-                        .replace(R.id.content, mEndFragment)
-                        .addToBackStack(null)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        assertTrue(waitForEnd(mEndFragment, TestFragment.ENTER));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        assertTrue(waitForEnd(mStartFragment, TestFragment.REENTER));
-    }
+        verifyNoOtherTransitions(to);
 
-    public void testAddRemoved() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
+        if (others != null) {
+            for (TransitionFragment fragment : others) {
+                verifyNoOtherTransitions(fragment);
+            }
         }
-        launchStartFragment();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .replace(R.id.content, mStartFragment)
-                        .addToBackStack(null)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForStart(mStartFragment, TestFragment.ENTER);
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasStartCalled(TestFragment.EXIT));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForStart(mStartFragment, TestFragment.REENTER);
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mStartFragment.wasStartCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mEndFragment.wasStartCalled(TestFragment.RETURN));
     }
 
-    private void launchStartFragment() throws Throwable {
-        getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mStartFragment = TestFragment.create(R.layout.fragment_start);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mStartFragment)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        assertTrue(waitForEnd(mStartFragment, TestFragment.ENTER));
-        mStartFragment.clearNotifications();
+    private static Rect getBoundsOnScreen(View view) {
+        final int[] loc = new int[2];
+        view.getLocationOnScreen(loc);
+        return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
     }
 
-    private boolean waitForStart(TestFragment fragment, int key) throws InterruptedException {
-        boolean started = fragment.waitForStart(key);
-        getInstrumentation().waitForIdleSync();
-        return started;
-    }
+    public static class ComplexTransitionFragment extends TransitionFragment {
+        public final TrackingTransition sharedElementEnterTransition1 = new TrackingTransition();
+        public final TrackingTransition sharedElementEnterTransition2 = new TrackingTransition();
+        public final TrackingTransition sharedElementReturnTransition1 = new TrackingTransition();
+        public final TrackingTransition sharedElementReturnTransition2 = new TrackingTransition();
 
-    private boolean waitForEnd(TestFragment fragment, int key) throws InterruptedException {
-        if (!waitForStart(fragment, key)) {
-            return false;
+        public final TransitionSet sharedElementEnterTransition = new TransitionSet()
+                .addTransition(sharedElementEnterTransition1)
+                .addTransition(sharedElementEnterTransition2);
+        public final TransitionSet sharedElementReturnTransition = new TransitionSet()
+                .addTransition(sharedElementReturnTransition1)
+                .addTransition(sharedElementReturnTransition2);
+
+        public ComplexTransitionFragment() {
+            sharedElementEnterTransition1.addTarget(R.id.blueSquare);
+            sharedElementEnterTransition2.addTarget(R.id.greenSquare);
+            sharedElementReturnTransition1.addTarget(R.id.blueSquare);
+            sharedElementReturnTransition2.addTarget(R.id.greenSquare);
+            setSharedElementEnterTransition(sharedElementEnterTransition);
+            setSharedElementReturnTransition(sharedElementReturnTransition);
         }
-        final boolean ended = fragment.waitForEnd(key);
-        getInstrumentation().waitForIdleSync();
-        return ended;
+
     }
 }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
new file mode 100644
index 0000000..22a52ed
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class FragmentViewTests {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void setupInstrumentation() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    // Test that adding a fragment adds the Views in the proper order. Popping the back stack
+    // should remove the correct Views.
+    @Test
+    public void addFragments() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        // One fragment with a view
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1);
+
+        // Add another on top
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment2).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+        // Now add two in one transaction:
+        final StrictViewFragment fragment3 = new StrictViewFragment();
+        final StrictViewFragment fragment4 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment3)
+                .add(R.id.fragmentContainer, fragment4)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(1, container.getChildCount());
+        FragmentTestUtil.assertChildren(container, fragment1);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+    }
+
+    // Add fragments to multiple containers in the same transaction. Make sure that
+    // they pop correctly, too.
+    @Test
+    public void addTwoContainers() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+        ViewGroup container1 = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+        ViewGroup container2 = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer1, fragment1).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container1, fragment1);
+
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer2, fragment2).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container2, fragment2);
+
+        final StrictViewFragment fragment3 = new StrictViewFragment();
+        final StrictViewFragment fragment4 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment3)
+                .add(R.id.fragmentContainer2, fragment4)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container1, fragment1, fragment3);
+        FragmentTestUtil.assertChildren(container2, fragment2, fragment4);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container1, fragment1);
+        FragmentTestUtil.assertChildren(container2, fragment2);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container1, fragment1);
+        FragmentTestUtil.assertChildren(container2);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertEquals(0, container1.getChildCount());
+    }
+
+    // When you add a fragment that's has already been added, it should throw.
+    @Test
+    public void doubleAdd() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment1).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    fm.beginTransaction()
+                            .add(R.id.fragmentContainer, fragment1)
+                            .addToBackStack(null)
+                            .commit();
+                    fm.executePendingTransactions();
+                    fail("Adding a fragment that is already added should be an error");
+                } catch (IllegalStateException e) {
+                    // expected
+                }
+            }
+        });
+    }
+
+    // Make sure that removed fragments remove the right Views. Popping the back stack should
+    // add the Views back properly
+    @Test
+    public void removeFragments() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        final StrictViewFragment fragment3 = new StrictViewFragment();
+        final StrictViewFragment fragment4 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .add(R.id.fragmentContainer, fragment2, "2")
+                .add(R.id.fragmentContainer, fragment3, "3")
+                .add(R.id.fragmentContainer, fragment4, "4")
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3, fragment4);
+
+        // Remove a view
+        fm.beginTransaction().remove(fragment4).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        assertEquals(3, container.getChildCount());
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2, fragment3);
+
+        // remove another one
+        fm.beginTransaction().remove(fragment2).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1, fragment3);
+
+        // Now remove the remaining:
+        fm.beginTransaction()
+                .remove(fragment3)
+                .remove(fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        final Fragment replacement1 = fm.findFragmentByTag("1");
+        final Fragment replacement3 = fm.findFragmentByTag("3");
+        FragmentTestUtil.assertChildren(container, replacement1, replacement3);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        final Fragment replacement2 = fm.findFragmentByTag("2");
+        FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        final Fragment replacement4 = fm.findFragmentByTag("4");
+        FragmentTestUtil.assertChildren(container, replacement1, replacement3, replacement2,
+                replacement4);
+    }
+
+    // Removing a hidden fragment should remove the View and popping should bring it back hidden
+    @Test
+    public void removeHiddenView() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").hide(fragment1).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1);
+        assertTrue(fragment1.isHidden());
+
+        fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        final Fragment replacement1 = fm.findFragmentByTag("1");
+        FragmentTestUtil.assertChildren(container, replacement1);
+        assertTrue(replacement1.isHidden());
+        assertEquals(View.GONE, replacement1.getView().getVisibility());
+    }
+
+    // Removing a detached fragment should do nothing to the View and popping should bring
+    // the Fragment back detached
+    @Test
+    public void removeDetatchedView() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .detach(fragment1)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment1.isDetached());
+
+        fm.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        final Fragment replacement1 = fm.findFragmentByTag("1");
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(replacement1.isDetached());
+    }
+
+    // Unlike adding the same fragment twice, you should be able to add and then remove and then
+    // add the same fragment in one transaction.
+    @Test
+    public void addRemoveAdd() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .remove(fragment)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container);
+    }
+
+    // Removing a fragment that isn't in should throw
+    @Test
+    public void removeNothThere() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().remove(fragment).commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Removing a fragment that isn't in should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
+    @Test
+    public void hideFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+        fm.beginTransaction().hide(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertTrue(fragment.isHidden());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isHidden());
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+    }
+
+    // Hiding a hidden fragment should throw
+    @Test
+    public void doubleHide() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .hide(fragment)
+                .hide(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Hiding a hidden fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Hiding a non-existing fragment should throw
+    @Test
+    public void hideUnAdded() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .hide(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Hiding a non-existing fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Show a hidden fragment and its View should be VISIBLE. Then pop it and the View should be
+    // GONE.
+    @Test
+    public void showFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertTrue(fragment.isHidden());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+
+        fm.beginTransaction().show(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isHidden());
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertTrue(fragment.isHidden());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+    }
+
+    // Showing a shown fragment should throw
+    @Test
+    public void showShown() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .show(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Showing a visible fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Showing a non-existing fragment should throw
+    @Test
+    public void showUnAdded() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .show(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Showing a non-existing fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Detaching a fragment should remove the View from the hierarchy. Then popping it should
+    // bring it back VISIBLE
+    @Test
+    public void detachFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isDetached());
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+        fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isDetached());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isDetached());
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+    }
+
+    // Detaching a hidden fragment should remove the View from the hierarchy. Then popping it should
+    // bring it back hidden
+    @Test
+    public void detachHiddenFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isDetached());
+        assertTrue(fragment.isHidden());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+
+        fm.beginTransaction().detach(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isHidden());
+        assertTrue(fragment.isDetached());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertTrue(fragment.isHidden());
+        assertFalse(fragment.isDetached());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+    }
+
+    // Detaching a detached fragment should throw
+    @Test
+    public void detachDetatched() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .detach(fragment)
+                .detach(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Detaching a detached fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Detaching a non-existing fragment should throw
+    @Test
+    public void detachUnAdded() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .detach(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Detaching a non-existing fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Attaching a fragment should add the View back into the hierarchy. Then popping it should
+    // remove it again
+    @Test
+    public void attachFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isDetached());
+
+        fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertFalse(fragment.isDetached());
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isDetached());
+    }
+
+    // Attaching a hidden fragment should add the View as GONE the hierarchy. Then popping it should
+    // remove it again.
+    @Test
+    public void attachHiddenFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .hide(fragment)
+                .detach(fragment)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isDetached());
+        assertTrue(fragment.isHidden());
+
+        fm.beginTransaction().attach(fragment).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertTrue(fragment.isHidden());
+        assertFalse(fragment.isDetached());
+        assertEquals(View.GONE, fragment.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+        assertTrue(fragment.isDetached());
+        assertTrue(fragment.isHidden());
+    }
+
+    // Attaching an attached fragment should throw
+    @Test
+    public void attachAttached() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment)
+                .attach(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Attaching an attached fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Attaching a non-existing fragment should throw
+    @Test
+    public void attachUnAdded() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .attach(fragment)
+                .commit();
+        try {
+            FragmentTestUtil.executePendingTransactions(mActivityRule);
+            fail("Attaching a non-existing fragment should throw an exception");
+        } catch (Throwable t) {
+            // expected
+        }
+    }
+
+    // Simple replace of one fragment in a container. Popping should replace it back again
+    @Test
+    public void replaceOne() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment1, "1").commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment1);
+
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment2);
+        assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        Fragment replacement1 = fm.findFragmentByTag("1");
+        assertNotNull(replacement1);
+        FragmentTestUtil.assertChildren(container, replacement1);
+        assertFalse(replacement1.isHidden());
+        assertTrue(replacement1.isAdded());
+        assertFalse(replacement1.isDetached());
+        assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+    }
+
+    // Replace of multiple fragments in a container. Popping should replace it back again
+    @Test
+    public void replaceTwo() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .add(R.id.fragmentContainer, fragment2, "2")
+                .hide(fragment2)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+        final StrictViewFragment fragment3 = new StrictViewFragment();
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment3)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment3);
+        assertEquals(View.VISIBLE, fragment3.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        Fragment replacement1 = fm.findFragmentByTag("1");
+        Fragment replacement2 = fm.findFragmentByTag("2");
+        assertNotNull(replacement1);
+        assertNotNull(replacement2);
+        FragmentTestUtil.assertChildren(container, replacement1, replacement2);
+        assertFalse(replacement1.isHidden());
+        assertTrue(replacement1.isAdded());
+        assertFalse(replacement1.isDetached());
+        assertEquals(View.VISIBLE, replacement1.getView().getVisibility());
+
+        // fragment2 was hidden, so it should be returned hidden
+        assertTrue(replacement2.isHidden());
+        assertTrue(replacement2.isAdded());
+        assertFalse(replacement2.isDetached());
+        assertEquals(View.GONE, replacement2.getView().getVisibility());
+    }
+
+    // Replace of empty container. Should act as add and popping should just remove the fragment
+    @Test
+    public void replaceZero() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment = new StrictViewFragment();
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment);
+        assertEquals(View.VISIBLE, fragment.getView().getVisibility());
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container);
+    }
+
+    // Replace a fragment that exists with itself
+    @Test
+    public void replaceExisting() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .add(R.id.fragmentContainer, fragment2, "2")
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment1, fragment2);
+
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        FragmentTestUtil.assertChildren(container, fragment1);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        final Fragment replacement1 = fm.findFragmentByTag("1");
+        final Fragment replacement2 = fm.findFragmentByTag("2");
+
+        assertSame(fragment1, replacement1);
+        FragmentTestUtil.assertChildren(container, replacement1, replacement2);
+    }
+
+    // Have two replace operations in the same transaction to ensure that they
+    // don't interfere with each other
+    @Test
+    public void replaceReplace() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+        ViewGroup container1 = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer1);
+        ViewGroup container2 = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer2);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment1 = new StrictViewFragment();
+        final StrictViewFragment fragment2 = new StrictViewFragment();
+        final StrictViewFragment fragment3 = new StrictViewFragment();
+        final StrictViewFragment fragment4 = new StrictViewFragment();
+        final StrictViewFragment fragment5 = new StrictViewFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment1)
+                .add(R.id.fragmentContainer2, fragment2)
+                .replace(R.id.fragmentContainer1, fragment3)
+                .replace(R.id.fragmentContainer2, fragment4)
+                .replace(R.id.fragmentContainer1, fragment5)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        assertChildren(container1, fragment5);
+        assertChildren(container2, fragment4);
+
+        fm.popBackStack();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        assertChildren(container1);
+        assertChildren(container2);
+    }
+
+    // Test to prevent regressions in FragmentManager fragment replace method. See b/24693644
+    @Test
+    public void testReplaceFragment() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        StrictViewFragment fragmentA = new StrictViewFragment();
+        fragmentA.setLayoutId(R.layout.text_a);
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragmentA)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+        assertNotNull(findViewById(R.id.textA));
+        assertNull(findViewById(R.id.textB));
+        assertNull(findViewById(R.id.textC));
+
+        StrictViewFragment fragmentB = new StrictViewFragment();
+        fragmentB.setLayoutId(R.layout.text_b);
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragmentB)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertNotNull(findViewById(R.id.textA));
+        assertNotNull(findViewById(R.id.textB));
+        assertNull(findViewById(R.id.textC));
+
+        StrictViewFragment fragmentC = new StrictViewFragment();
+        fragmentC.setLayoutId(R.layout.text_c);
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragmentC)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        assertNull(findViewById(R.id.textA));
+        assertNull(findViewById(R.id.textB));
+        assertNotNull(findViewById(R.id.textC));
+    }
+
+    private View findViewById(int viewId) {
+        return mActivityRule.getActivity().findViewById(viewId);
+    }
+
+    private void assertChildren(ViewGroup container, Fragment... fragments) {
+        final int numFragments = fragments == null ? 0 : fragments.length;
+        assertEquals("There aren't the correct number of fragment Views in its container",
+                numFragments, container.getChildCount());
+        for (int i = 0; i < numFragments; i++) {
+            assertEquals("Wrong Fragment View order for [" + i + "]", container.getChildAt(i),
+                    fragments[i].getView());
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/HostCallbacks.java b/fragment/tests/java/android/support/v4/app/HostCallbacks.java
new file mode 100644
index 0000000..9a0ef1c
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/HostCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.os.Handler;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+class HostCallbacks extends FragmentHostCallback<FragmentTestActivity> {
+    private final FragmentTestActivity mActivity;
+
+    HostCallbacks(FragmentTestActivity activity, Handler handler, int windowAnimations) {
+        super(activity, handler, windowAnimations);
+        mActivity = activity;
+    }
+
+    @Override
+    public FragmentTestActivity onGetHost() {
+        return mActivity;
+    }
+
+    @Override
+    public View onFindViewById(int id) {
+        return mActivity.findViewById(id);
+    }
+
+    @Override
+    public LayoutInflater onGetLayoutInflater() {
+        return mActivity.getLayoutInflater().cloneInContext(mActivity);
+    }
+
+}
diff --git a/fragment/tests/java/android/support/v4/app/NestedFragmentRestoreTest.java b/fragment/tests/java/android/support/v4/app/NestedFragmentRestoreTest.java
index 2ec1a6c..380f204 100644
--- a/fragment/tests/java/android/support/v4/app/NestedFragmentRestoreTest.java
+++ b/fragment/tests/java/android/support/v4/app/NestedFragmentRestoreTest.java
@@ -17,15 +17,19 @@
 
 package android.support.v4.app;
 
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNotSame;
+import static junit.framework.TestCase.assertTrue;
+
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
 import android.support.v4.app.test.FragmentTestActivity.ChildFragment;
 import android.support.v4.app.test.FragmentTestActivity.ChildFragment.OnAttachListener;
 import android.support.v4.app.test.FragmentTestActivity.ParentFragment;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,10 +37,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static junit.framework.TestCase.assertNotNull;
-import static junit.framework.TestCase.assertNotSame;
-import static junit.framework.TestCase.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 public class NestedFragmentRestoreTest {
 
@@ -51,7 +51,7 @@
     @SmallTest
     public void recreateActivity() throws Throwable {
         final FragmentTestActivity activity = mActivityRule.getActivity();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 FragmentTestActivity.ParentFragment parent = new ParentFragment();
@@ -77,7 +77,7 @@
             }
         });
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 activity.recreate();
diff --git a/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java b/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
index 84700ff..39e19be 100644
--- a/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
+++ b/fragment/tests/java/android/support/v4/app/NestedFragmentTest.java
@@ -16,39 +16,50 @@
 
 package android.support.v4.app;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
 import android.support.v4.app.test.FragmentTestActivity.ChildFragment;
 import android.support.v4.app.test.FragmentTestActivity.ParentFragment;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+@RunWith(AndroidJUnit4.class)
 @LargeTest
-public class NestedFragmentTest extends ActivityInstrumentationTestCase2<FragmentTestActivity> {
+public class NestedFragmentTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
 
-    ParentFragment mParentFragment;
+    private Instrumentation mInstrumentation;
+    private ParentFragment mParentFragment;
 
-    public NestedFragmentTest() {
-        super(FragmentTestActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        final FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
+    @Before
+    public void setup() throws Throwable {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        final FragmentManager fragmentManager =
+                mActivityRule.getActivity().getSupportFragmentManager();
         mParentFragment = new ParentFragment();
         fragmentManager.beginTransaction().add(mParentFragment, "parent").commit();
         final CountDownLatch latch = new CountDownLatch(1);
-        getActivity().runOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             public void run() {
                 fragmentManager.executePendingTransactions();
                 latch.countDown();
@@ -58,27 +69,26 @@
     }
 
     @UiThreadTest
+    @Test(expected = IllegalArgumentException.class)
     public void testThrowsWhenUsingReservedRequestCode() {
-        try {
-            mParentFragment.getChildFragment().startActivityForResult(
+        mParentFragment.getChildFragment().startActivityForResult(
                 new Intent(Intent.ACTION_CALL), 16777216 /* requestCode */);
-            fail("Expected IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
     }
 
-    public void testNestedFragmentStartActivityForResult() throws Exception {
+    @Test
+    public void testNestedFragmentStartActivityForResult() throws Throwable {
         Instrumentation.ActivityResult activityResult = new Instrumentation.ActivityResult(
                 Activity.RESULT_OK, new Intent());
 
         Instrumentation.ActivityMonitor activityMonitor =
-                getInstrumentation().addMonitor(
+                mInstrumentation.addMonitor(
                         new IntentFilter(Intent.ACTION_CALL), activityResult, true /* block */);
 
         // Sanity check that onActivityResult hasn't been called yet.
         assertFalse(mParentFragment.getChildFragment().onActivityResultCalled);
 
         final CountDownLatch latch = new CountDownLatch(1);
-        getActivity().runOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             public void run() {
                 mParentFragment.getChildFragment().startActivityForResult(
                         new Intent(Intent.ACTION_CALL),
@@ -88,7 +98,7 @@
         });
         assertTrue(latch.await(1, TimeUnit.SECONDS));
 
-        assertTrue(getInstrumentation().checkMonitorHit(activityMonitor, 1));
+        assertTrue(mInstrumentation.checkMonitorHit(activityMonitor, 1));
 
         final ChildFragment childFragment = mParentFragment.getChildFragment();
         assertTrue(childFragment.onActivityResultCalled);
diff --git a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
new file mode 100644
index 0000000..854fd11
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.fragment.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.FragmentTestActivity;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class PostponedTransitionTest {
+    @Rule
+    public ActivityTestRule<FragmentTestActivity> mActivityRule =
+            new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private PostponedFragment1 mBeginningFragment;
+
+    @Before
+    public void setupContainer() throws Throwable {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        mBeginningFragment = new PostponedFragment1();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, mBeginningFragment)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        mBeginningFragment.startPostponedEnterTransition();
+        mBeginningFragment.waitForTransition();
+        clearTargets(mBeginningFragment);
+    }
+
+    // Ensure that replacing with a fragment that has a postponed transition
+    // will properly postpone it, both adding and popping.
+    @Test
+    public void replaceTransition() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final PostponedFragment2 fragment = new PostponedFragment2();
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        // should be postponed now
+        assertPostponedTransition(mBeginningFragment, fragment, null);
+
+        // start the postponed transition
+        fragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(mBeginningFragment, fragment);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        // should be postponed going back, too
+        assertPostponedTransition(fragment, mBeginningFragment, null);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment, mBeginningFragment);
+    }
+
+    // Ensure that postponed transition is forced after another has been committed.
+    // This tests when the transactions are executed together
+    @Test
+    public void forcedTransition1() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final PostponedFragment2 fragment2 = new PostponedFragment2();
+        final PostponedFragment1 fragment3 = new PostponedFragment1();
+
+        final int[] commit = new int[1];
+        // Need to run this on the UI thread so that the transaction doesn't start
+        // between the two
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                commit[0] = fm.beginTransaction()
+                        .addSharedElement(startBlue, "blueSquare")
+                        .replace(R.id.fragmentContainer, fragment2)
+                        .addToBackStack(null)
+                        .commit();
+
+                fm.beginTransaction()
+                        .addSharedElement(startBlue, "blueSquare")
+                        .replace(R.id.fragmentContainer, fragment3)
+                        .addToBackStack(null)
+                        .commit();
+            }
+        });
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        // transition to fragment2 should be started
+        assertForwardTransition(mBeginningFragment, fragment2);
+
+        // fragment3 should be postponed, but fragment2 should be executed with no transition.
+        assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(fragment2, fragment3);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule, commit[0],
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+
+        assertBackTransition(fragment3, fragment2);
+
+        assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment2, mBeginningFragment);
+    }
+
+    // Ensure that postponed transition is forced after another has been committed.
+    // This tests when the transactions are processed separately.
+    @Test
+    public void forcedTransition2() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+        final PostponedFragment1 fragment3 = new PostponedFragment1();
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment3)
+                .addToBackStack(null)
+                .commit();
+
+        // This should cancel the mBeginningFragment -> fragment2 transition
+        // and start fragment2 -> fragment3 transition postponed
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        // fragment3 should be postponed, but fragment2 should be executed with no transition.
+        assertPostponedTransition(fragment2, fragment3, mBeginningFragment);
+
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(fragment2, fragment3);
+
+        // Pop back to fragment2, but it should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment3, fragment2, null);
+
+        // Pop to mBeginningFragment -- should cancel the fragment2 transition and
+        // start the mBeginningFragment transaction postponed
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment2, mBeginningFragment, fragment3);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment2, mBeginningFragment);
+    }
+
+    // Do a bunch of things to one fragment in a transaction and see if it can screw things up.
+    @Test
+    public void crazyTransition() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue = mActivityRule.getActivity().findViewById(R.id.blueSquare);
+
+        final PostponedFragment2 fragment2 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .hide(mBeginningFragment)
+                .replace(R.id.fragmentContainer, fragment2)
+                .hide(fragment2)
+                .detach(fragment2)
+                .attach(fragment2)
+                .show(fragment2)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(mBeginningFragment, fragment2, null);
+
+        // start the postponed transition
+        fragment2.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(mBeginningFragment, fragment2);
+
+        // Pop back to fragment2, but it should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment2, mBeginningFragment, null);
+
+        // start the postponed transition
+        mBeginningFragment.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment2, mBeginningFragment);
+    }
+
+    // Execute transactions on different containers and ensure that they don't conflict
+    @Test
+    public void differentContainers() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.beginTransaction().remove(mBeginningFragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+        TransitionFragment fragment1 = new PostponedFragment1();
+        TransitionFragment fragment2 = new PostponedFragment1();
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment1)
+                .add(R.id.fragmentContainer2, fragment2)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.startPostponedEnterTransition();
+        fragment2.startPostponedEnterTransition();
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+        clearTargets(fragment1);
+        clearTargets(fragment2);
+
+        final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+        final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment3 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue1, "blueSquare")
+                .replace(R.id.fragmentContainer1, fragment3)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+
+        final TransitionFragment fragment4 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue2, "blueSquare")
+                .replace(R.id.fragmentContainer2, fragment4)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+        assertPostponedTransition(fragment2, fragment4, null);
+
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition();
+
+        // make sure only one ran
+        assertForwardTransition(fragment1, fragment3);
+        assertPostponedTransition(fragment2, fragment4, null);
+
+        // start the postponed transition
+        fragment4.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(fragment2, fragment4);
+
+        // Pop back to fragment2 -- should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment4, fragment2, null);
+
+        // Pop back to fragment1 -- also should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment4, fragment2, null);
+        assertPostponedTransition(fragment3, fragment1, null);
+
+        // start the postponed transition
+        fragment2.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment4, fragment2);
+
+        // but not the postponed one
+        assertPostponedTransition(fragment3, fragment1, null);
+
+        // start the postponed transition
+        fragment1.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment3, fragment1);
+    }
+
+    // Execute transactions on different containers and ensure that they don't conflict.
+    // The postponement can be started out-of-order
+    @Test
+    public void outOfOrderContainers() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.beginTransaction().remove(mBeginningFragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+        TransitionFragment fragment1 = new PostponedFragment1();
+        TransitionFragment fragment2 = new PostponedFragment1();
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment1)
+                .add(R.id.fragmentContainer2, fragment2)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.startPostponedEnterTransition();
+        fragment2.startPostponedEnterTransition();
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+        clearTargets(fragment1);
+        clearTargets(fragment2);
+
+        final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+        final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment3 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue1, "blueSquare")
+                .replace(R.id.fragmentContainer1, fragment3)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+
+        final TransitionFragment fragment4 = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue2, "blueSquare")
+                .replace(R.id.fragmentContainer2, fragment4)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+        assertPostponedTransition(fragment2, fragment4, null);
+
+        // start the postponed transition
+        fragment4.startPostponedEnterTransition();
+
+        // make sure only one ran
+        assertForwardTransition(fragment2, fragment4);
+        assertPostponedTransition(fragment1, fragment3, null);
+
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(fragment1, fragment3);
+
+        // Pop back to fragment2 -- should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment4, fragment2, null);
+
+        // Pop back to fragment1 -- also should be postponed
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertPostponedTransition(fragment4, fragment2, null);
+        assertPostponedTransition(fragment3, fragment1, null);
+
+        // start the postponed transition
+        fragment1.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment3, fragment1);
+
+        // but not the postponed one
+        assertPostponedTransition(fragment4, fragment2, null);
+
+        // start the postponed transition
+        fragment2.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertBackTransition(fragment4, fragment2);
+    }
+
+    // Make sure that commitNow for a transaction on a different fragment container doesn't
+    // affect the postponed transaction
+    @Test
+    public void commitNowNoEffect() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.beginTransaction().remove(mBeginningFragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+        final TransitionFragment fragment1 = new PostponedFragment1();
+        final TransitionFragment fragment2 = new PostponedFragment1();
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment1)
+                .add(R.id.fragmentContainer2, fragment2)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.startPostponedEnterTransition();
+        fragment2.startPostponedEnterTransition();
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+        clearTargets(fragment1);
+        clearTargets(fragment2);
+
+        final View startBlue1 = fragment1.getView().findViewById(R.id.blueSquare);
+        final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment3 = new PostponedFragment2();
+        final StrictFragment strictFragment1 = new StrictFragment();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue1, "blueSquare")
+                .replace(R.id.fragmentContainer1, fragment3)
+                .add(strictFragment1, "1")
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+
+        final TransitionFragment fragment4 = new PostponedFragment2();
+        final StrictFragment strictFragment2 = new StrictFragment();
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fm.beginTransaction()
+                        .addSharedElement(startBlue2, "blueSquare")
+                        .replace(R.id.fragmentContainer2, fragment4)
+                        .remove(strictFragment1)
+                        .add(strictFragment2, "2")
+                        .commitNow();
+            }
+        });
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment3, null);
+        assertPostponedTransition(fragment2, fragment4, null);
+
+        // start the postponed transition
+        fragment4.startPostponedEnterTransition();
+
+        // make sure only one ran
+        assertForwardTransition(fragment2, fragment4);
+        assertPostponedTransition(fragment1, fragment3, null);
+
+        // start the postponed transition
+        fragment3.startPostponedEnterTransition();
+
+        // make sure it ran
+        assertForwardTransition(fragment1, fragment3);
+    }
+
+    // Make sure that commitNow for a transaction affecting a postponed fragment in the same
+    // container forces the postponed transition to start.
+    @Test
+    public void commitNowStartsPostponed() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue1 = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment2 = new PostponedFragment2();
+        final TransitionFragment fragment1 = new PostponedFragment1();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue1, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final View startBlue2 = fragment2.getView().findViewById(R.id.blueSquare);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fm.beginTransaction()
+                        .addSharedElement(startBlue2, "blueSquare")
+                        .replace(R.id.fragmentContainer, fragment1)
+                        .commitNow();
+            }
+        });
+
+        assertPostponedTransition(fragment2, fragment1, mBeginningFragment);
+
+        // start the postponed transition
+        fragment1.startPostponedEnterTransition();
+
+        assertForwardTransition(fragment2, fragment1);
+    }
+
+    // Make sure that when a transaction that removes a view is postponed that
+    // another transaction doesn't accidentally remove the view early.
+    @Test
+    public void noAccidentalRemoval() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        fm.beginTransaction().remove(mBeginningFragment).commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
+
+        TransitionFragment fragment1 = new PostponedFragment1();
+
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer1, fragment1)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.startPostponedEnterTransition();
+        fragment1.waitForTransition();
+        clearTargets(fragment1);
+
+        TransitionFragment fragment2 = new PostponedFragment2();
+        // Create a postponed transaction that removes a view
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer1, fragment2)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        assertPostponedTransition(fragment1, fragment2, null);
+
+        TransitionFragment fragment3 = new PostponedFragment1();
+        // Create a transaction that doesn't interfere with the previously postponed one
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer2, fragment3)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(fragment1, fragment2, null);
+
+        fragment3.startPostponedEnterTransition();
+        fragment3.waitForTransition();
+        clearTargets(fragment3);
+
+        assertPostponedTransition(fragment1, fragment2, null);
+    }
+
+    // Ensure that a postponed transaction that is popped runs immediately and that
+    // the transaction results in the original state with no transition.
+    @Test
+    public void popPostponedTransaction() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+        final View startBlue = mBeginningFragment.getView().findViewById(R.id.blueSquare);
+
+        final TransitionFragment fragment = new PostponedFragment2();
+
+        fm.beginTransaction()
+                .addSharedElement(startBlue, "blueSquare")
+                .replace(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertPostponedTransition(mBeginningFragment, fragment, null);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        fragment.waitForNoTransition();
+        mBeginningFragment.waitForNoTransition();
+
+        assureNoTransition(fragment);
+        assureNoTransition(mBeginningFragment);
+
+        assertFalse(fragment.isAdded());
+        assertNull(fragment.getView());
+        assertNotNull(mBeginningFragment.getView());
+        assertEquals(View.VISIBLE, mBeginningFragment.getView().getVisibility());
+        assertTrue(mBeginningFragment.getView().isAttachedToWindow());
+    }
+
+    // Make sure that when saving the state during a postponed transaction that it saves
+    // the state as if it wasn't postponed.
+    @Test
+    public void saveWhilePostponed() throws Throwable {
+        final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(fc1, null);
+
+        final FragmentManager fm1 = fc1.getSupportFragmentManager();
+
+        PostponedFragment1 fragment1 = new PostponedFragment1();
+        fm1.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1, "1")
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        Pair<Parcelable, FragmentManagerNonConfig> state =
+                FragmentTestUtil.destroy(fc1);
+
+        final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(fc2, state);
+
+        final FragmentManager fm2 = fc2.getSupportFragmentManager();
+        Fragment fragment2 = fm2.findFragmentByTag("1");
+        assertNotNull(fragment2);
+        assertNotNull(fragment2.getView());
+        assertEquals(View.VISIBLE, fragment2.getView().getVisibility());
+        assertTrue(fragment2.isResumed());
+        assertTrue(fragment2.isAdded());
+        assertTrue(fragment2.getView().isAttachedToWindow());
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(fm2.popBackStackImmediate());
+
+            }
+        });
+
+        assertFalse(fragment2.isResumed());
+        assertFalse(fragment2.isAdded());
+        assertNull(fragment2.getView());
+    }
+
+    private void assertPostponedTransition(TransitionFragment fromFragment,
+            TransitionFragment toFragment, TransitionFragment removedFragment)
+            throws InterruptedException {
+        if (removedFragment != null) {
+            assertNull(removedFragment.getView());
+            assureNoTransition(removedFragment);
+        }
+
+        toFragment.waitForNoTransition();
+        assertNotNull(fromFragment.getView());
+        assertNotNull(toFragment.getView());
+        assertTrue(fromFragment.getView().isAttachedToWindow());
+        assertTrue(toFragment.getView().isAttachedToWindow());
+        assertEquals(View.VISIBLE, fromFragment.getView().getVisibility());
+        assertEquals(View.INVISIBLE, toFragment.getView().getVisibility());
+        assureNoTransition(fromFragment);
+        assureNoTransition(toFragment);
+        assertTrue(fromFragment.isResumed());
+        assertFalse(toFragment.isResumed());
+    }
+
+    private void clearTargets(TransitionFragment fragment) {
+        fragment.enterTransition.targets.clear();
+        fragment.reenterTransition.targets.clear();
+        fragment.exitTransition.targets.clear();
+        fragment.returnTransition.targets.clear();
+        fragment.sharedElementEnter.targets.clear();
+        fragment.sharedElementReturn.targets.clear();
+    }
+
+    private void assureNoTransition(TransitionFragment fragment) {
+        assertEquals(0, fragment.enterTransition.targets.size());
+        assertEquals(0, fragment.reenterTransition.targets.size());
+        assertEquals(0, fragment.enterTransition.targets.size());
+        assertEquals(0, fragment.returnTransition.targets.size());
+        assertEquals(0, fragment.sharedElementEnter.targets.size());
+        assertEquals(0, fragment.sharedElementReturn.targets.size());
+    }
+
+    private void assertForwardTransition(TransitionFragment start, TransitionFragment end)
+            throws InterruptedException {
+        start.waitForTransition();
+        end.waitForTransition();
+        assertEquals(0, start.enterTransition.targets.size());
+        assertEquals(1, end.enterTransition.targets.size());
+
+        assertEquals(0, start.reenterTransition.targets.size());
+        assertEquals(0, end.reenterTransition.targets.size());
+
+        assertEquals(0, start.returnTransition.targets.size());
+        assertEquals(0, end.returnTransition.targets.size());
+
+        assertEquals(1, start.exitTransition.targets.size());
+        assertEquals(0, end.exitTransition.targets.size());
+
+        assertEquals(0, start.sharedElementEnter.targets.size());
+        assertEquals(2, end.sharedElementEnter.targets.size());
+
+        assertEquals(0, start.sharedElementReturn.targets.size());
+        assertEquals(0, end.sharedElementReturn.targets.size());
+
+        final View blue = end.getView().findViewById(R.id.blueSquare);
+        assertTrue(end.sharedElementEnter.targets.contains(blue));
+        assertEquals("blueSquare", end.sharedElementEnter.targets.get(0).getTransitionName());
+        assertEquals("blueSquare", end.sharedElementEnter.targets.get(1).getTransitionName());
+
+        assertNoTargets(start);
+        assertNoTargets(end);
+
+        clearTargets(start);
+        clearTargets(end);
+    }
+
+    private void assertBackTransition(TransitionFragment start, TransitionFragment end)
+            throws InterruptedException {
+        start.waitForTransition();
+        end.waitForTransition();
+        assertEquals(1, end.reenterTransition.targets.size());
+        assertEquals(0, start.reenterTransition.targets.size());
+
+        assertEquals(0, end.returnTransition.targets.size());
+        assertEquals(1, start.returnTransition.targets.size());
+
+        assertEquals(0, start.enterTransition.targets.size());
+        assertEquals(0, end.enterTransition.targets.size());
+
+        assertEquals(0, start.exitTransition.targets.size());
+        assertEquals(0, end.exitTransition.targets.size());
+
+        assertEquals(0, start.sharedElementEnter.targets.size());
+        assertEquals(0, end.sharedElementEnter.targets.size());
+
+        assertEquals(2, start.sharedElementReturn.targets.size());
+        assertEquals(0, end.sharedElementReturn.targets.size());
+
+        final View blue = end.getView().findViewById(R.id.blueSquare);
+        assertTrue(start.sharedElementReturn.targets.contains(blue));
+        assertEquals("blueSquare", start.sharedElementReturn.targets.get(0).getTransitionName());
+        assertEquals("blueSquare", start.sharedElementReturn.targets.get(1).getTransitionName());
+
+        assertNoTargets(end);
+        assertNoTargets(start);
+
+        clearTargets(start);
+        clearTargets(end);
+    }
+
+    private static void assertNoTargets(TransitionFragment fragment) {
+        assertTrue(fragment.enterTransition.getTargets().isEmpty());
+        assertTrue(fragment.reenterTransition.getTargets().isEmpty());
+        assertTrue(fragment.exitTransition.getTargets().isEmpty());
+        assertTrue(fragment.returnTransition.getTargets().isEmpty());
+        assertTrue(fragment.sharedElementEnter.getTargets().isEmpty());
+        assertTrue(fragment.sharedElementReturn.getTargets().isEmpty());
+    }
+
+    public static class PostponedFragment1 extends TransitionFragment {
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            postponeEnterTransition();
+            return inflater.inflate(R.layout.scene1, container, false);
+        }
+    }
+
+    public static class PostponedFragment2 extends TransitionFragment {
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            postponeEnterTransition();
+            return inflater.inflate(R.layout.scene2, container, false);
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
index e0bdf33..3332ec6 100644
--- a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
@@ -17,21 +17,26 @@
 
 package android.support.v4.app;
 
-import android.support.fragment.test.R;
 import android.os.Bundle;
+import android.support.fragment.test.R;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 public class StrictViewFragment extends StrictFragment {
     boolean mOnCreateViewCalled, mOnViewCreatedCalled, mOnDestroyViewCalled;
+    int mLayoutId = R.layout.strict_view_fragment;
+
+    public void setLayoutId(int layoutId) {
+        mLayoutId = layoutId;
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         checkGetActivity();
         checkState("onCreateView", CREATED);
-        final View result = inflater.inflate(R.layout.strict_view_fragment, container, false);
+        final View result = inflater.inflate(mLayoutId, container, false);
         mOnCreateViewCalled = true;
         return result;
     }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RSDriverException.java b/fragment/tests/java/android/support/v4/app/TargetTracking.java
similarity index 64%
rename from v8/renderscript/java/src/android/support/v8/renderscript/RSDriverException.java
rename to fragment/tests/java/android/support/v4/app/TargetTracking.java
index d4ae341..2639ff8 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RSDriverException.java
+++ b/fragment/tests/java/android/support/v4/app/TargetTracking.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,19 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.support.v4.app;
 
-package android.support.v8.renderscript;
+import android.graphics.Rect;
+import android.view.View;
 
+import java.util.ArrayList;
 
-/**
- * Base class for all exceptions thrown by the Android
- * RenderScript
- */
-public class RSDriverException extends RSRuntimeException {
-    public RSDriverException(String string) {
-        super(string);
-    }
+public interface TargetTracking {
+    ArrayList<View> getTrackedTargets();
+    void clearTargets();
+    Rect getCapturedEpicenter();
 }
-
-
-
diff --git a/fragment/tests/java/android/support/v4/app/TrackingTransition.java b/fragment/tests/java/android/support/v4/app/TrackingTransition.java
new file mode 100644
index 0000000..87f5245
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TrackingTransition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * A transition that tracks which targets are applied to it.
+ * It will assume any target that it applies to will have differences
+ * between the start and end state, regardless of the differences
+ * that actually exist. In other words, it doesn't actually check
+ * any size or position differences or any other property of the view.
+ * It just records the difference.
+ * <p>
+ * Both start and end value Views are recorded, but no actual animation
+ * is created.
+ */
+class TrackingTransition extends Transition implements TargetTracking {
+    public final ArrayList<View> targets = new ArrayList<>();
+    private final Rect[] mEpicenter = new Rect[1];
+    private static final String PROP = "tracking:prop";
+    private static final String[] PROPS = { PROP };
+
+    @Override
+    public String[] getTransitionProperties() {
+        return PROPS;
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        transitionValues.values.put(PROP, 0);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        transitionValues.values.put(PROP, 1);
+    }
+
+    @Override
+    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        if (startValues != null) {
+            targets.add(startValues.view);
+        }
+        if (endValues != null) {
+            targets.add(endValues.view);
+        }
+        Rect epicenter = getEpicenter();
+        if (epicenter != null) {
+            mEpicenter[0] = new Rect(epicenter);
+        } else {
+            mEpicenter[0] = null;
+        }
+        return null;
+    }
+
+    @Override
+    public ArrayList<View> getTrackedTargets() {
+        return targets;
+    }
+
+    @Override
+    public void clearTargets() {
+        targets.clear();
+    }
+
+    @Override
+    public Rect getCapturedEpicenter() {
+        return mEpicenter[0];
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/TrackingVisibility.java b/fragment/tests/java/android/support/v4/app/TrackingVisibility.java
new file mode 100644
index 0000000..b302d7e
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TrackingVisibility.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import android.animation.Animator;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Visibility transition that tracks which targets are applied to it.
+ * This transition does no animation.
+ */
+class TrackingVisibility extends Visibility implements TargetTracking {
+    public final ArrayList<View> targets = new ArrayList<>();
+    private final Rect[] mEpicenter = new Rect[1];
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+            TransitionValues endValues) {
+        targets.add(endValues.view);
+        Rect epicenter = getEpicenter();
+        if (epicenter != null) {
+            mEpicenter[0] = new Rect(epicenter);
+        } else {
+            mEpicenter[0] = null;
+        }
+        return null;
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+            TransitionValues endValues) {
+        targets.add(startValues.view);
+        Rect epicenter = getEpicenter();
+        if (epicenter != null) {
+            mEpicenter[0] = new Rect(epicenter);
+        } else {
+            mEpicenter[0] = null;
+        }
+        return null;
+    }
+
+    @Override
+    public ArrayList<View> getTrackedTargets() {
+        return targets;
+    }
+
+    @Override
+    public void clearTargets() {
+        targets.clear();
+    }
+
+    @Override
+    public Rect getCapturedEpicenter() {
+        return mEpicenter[0];
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/TransitionFragment.java b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
new file mode 100644
index 0000000..fda2784
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+import static android.support.v4.app.CtsMockitoUtils.within;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.transition.Transition;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A fragment that has transitions that can be tracked.
+ */
+public class TransitionFragment extends StrictViewFragment {
+    public final TrackingVisibility enterTransition = new TrackingVisibility();
+    public final TrackingVisibility reenterTransition = new TrackingVisibility();
+    public final TrackingVisibility exitTransition = new TrackingVisibility();
+    public final TrackingVisibility returnTransition = new TrackingVisibility();
+    public final TrackingTransition sharedElementEnter = new TrackingTransition();
+    public final TrackingTransition sharedElementReturn = new TrackingTransition();
+
+    private Transition.TransitionListener mListener = mock(Transition.TransitionListener.class);
+
+    public TransitionFragment() {
+        setEnterTransition(enterTransition);
+        setReenterTransition(reenterTransition);
+        setExitTransition(exitTransition);
+        setReturnTransition(returnTransition);
+        setSharedElementEnterTransition(sharedElementEnter);
+        setSharedElementReturnTransition(sharedElementReturn);
+        enterTransition.addListener(mListener);
+        reenterTransition.addListener(mListener);
+        exitTransition.addListener(mListener);
+        returnTransition.addListener(mListener);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        checkGetActivity();
+        checkState("onCreateView", CREATED);
+        mOnCreateViewCalled = true;
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    void waitForTransition() throws InterruptedException {
+        verify(mListener, within(300)).onTransitionEnd((Transition) any());
+        reset(mListener);
+    }
+
+    void waitForNoTransition() throws InterruptedException {
+        SystemClock.sleep(250);
+        verify(mListener, never()).onTransitionStart((Transition) any());
+    }
+}
diff --git a/fragment/tests/res/layout/double_container.xml b/fragment/tests/res/layout/double_container.xml
new file mode 100644
index 0000000..c24d5ce
--- /dev/null
+++ b/fragment/tests/res/layout/double_container.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:id="@+id/fragmentContainer1"
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0px"/>
+    <FrameLayout
+        android:id="@+id/fragmentContainer2"
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0px"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene1.xml b/fragment/tests/res/layout/scene1.xml
new file mode 100644
index 0000000..d0509c3
--- /dev/null
+++ b/fragment/tests/res/layout/scene1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/squareContainer"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <View android:id="@+id/greenSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:transitionName="greenSquare"
+          android:background="#080"/>
+    <View android:id="@+id/blueSquare"
+          android:transitionName="blueSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#008"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene2.xml b/fragment/tests/res/layout/scene2.xml
new file mode 100644
index 0000000..ef809a4
--- /dev/null
+++ b/fragment/tests/res/layout/scene2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/squareContainer"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <View android:id="@+id/blueSquare"
+          android:transitionName="blueSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#008"/>
+    <View android:id="@+id/greenSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:transitionName="greenSquare"
+          android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/fragment/tests/res/layout/scene3.xml b/fragment/tests/res/layout/scene3.xml
new file mode 100644
index 0000000..15519fd
--- /dev/null
+++ b/fragment/tests/res/layout/scene3.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/squareContainer"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <View android:id="@+id/redSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#800"/>
+    <View android:id="@+id/blueSquare"
+          android:transitionName="shared"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:background="#008"/>
+    <View android:id="@+id/greenSquare"
+          android:layout_width="10dp"
+          android:layout_height="10dp"
+          android:transitionName="greenSquare"
+          android:background="#080"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/simple_container.xml
similarity index 61%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/simple_container.xml
index 2cd0f7a..f231c1e 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/simple_container.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+         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,
@@ -13,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/fragmentContainer"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+</FrameLayout>
\ No newline at end of file
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_a.xml
similarity index 64%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/text_a.xml
index 2cd0f7a..b164469 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_a.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+         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,
@@ -13,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:id="@+id/textA"/>
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_b.xml
similarity index 64%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/text_b.xml
index 2cd0f7a..3d307fc 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_b.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+         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,
@@ -13,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:id="@+id/textB"/>
diff --git a/documents-archive/AndroidManifest.xml b/fragment/tests/res/layout/text_c.xml
similarity index 64%
copy from documents-archive/AndroidManifest.xml
copy to fragment/tests/res/layout/text_c.xml
index 2cd0f7a..e40b5ab 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/fragment/tests/res/layout/text_c.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+         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,
@@ -13,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:id="@+id/textC"/>
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index 61039fb..f58493b 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -20,13 +20,17 @@
 #
 # ---------------------------------------------
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-vectordrawable
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, static/src)
-
-LOCAL_JAVA_LIBRARIES := android-support-compat
-
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/static/res
+LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-compat \
+    android-support-annotations
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # ---------------------------------------------
@@ -35,12 +39,16 @@
 #
 # ---------------------------------------------
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-animatedvectordrawable
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, animated/src)
-
-LOCAL_JAVA_LIBRARIES := android-support-compat android-support-vectordrawable
-
-LOCAL_AAPT_FLAGS := --no-version-vectors
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/animated/res
+LOCAL_MANIFEST_FILE := animated/AndroidManifest.xml
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-compat \
+    android-support-vectordrawable \
+    android-support-annotations
+LOCAL_AAPT_FLAGS := --no-version-vectors --add-javadoc-annotation doconly
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/drawable/animated/api/current.txt b/graphics/drawable/animated/api/current.txt
deleted file mode 100644
index 1461956..0000000
--- a/graphics/drawable/animated/api/current.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-package android.support.graphics.drawable {
-
-  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
-    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
-    method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas);
-    method public int getOpacity();
-    method public boolean isRunning();
-    method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-    method public void start();
-    method public void stop();
-  }
-
-   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
-  }
-
-}
-
diff --git a/graphics/drawable/animated/api/removed.txt b/graphics/drawable/animated/api/removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/graphics/drawable/animated/api/removed.txt
+++ /dev/null
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index 0b0522e..b0b5fa1 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -62,7 +61,6 @@
  * API. In order to refer to AnimatedVectorDrawableCompat inside a XML file, you can use
  * app:srcCompat attribute in AppCompat library's ImageButton or ImageView.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable {
     private static final String LOGTAG = "AnimatedVDCompat";
 
@@ -339,18 +337,6 @@
         mAnimatedVectorState.mVectorDrawable.setAutoMirrored(mirrored);
     }
 
-    /**
-     * Obtains styled attributes from the theme, if available, or unstyled
-     * resources if the theme is null.
-     */
-    static TypedArray obtainAttributes(
-            Resources res, Theme theme, AttributeSet set, int[] attrs) {
-        if (theme == null) {
-            return res.obtainAttributes(set, attrs);
-        }
-        return theme.obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
     @Override
     public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
@@ -371,7 +357,7 @@
                 }
                 if (ANIMATED_VECTOR.equals(tagName)) {
                     final TypedArray a =
-                            obtainAttributes(res, theme, attrs,
+                            VectorDrawableCommon.obtainAttributes(res, theme, attrs,
                                     AndroidResources.styleable_AnimatedVectorDrawable);
 
                     int drawableRes = a.getResourceId(
diff --git a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
index 2e50823..0d6e367 100644
--- a/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
+++ b/graphics/drawable/animated/tests/src/android/support/graphics/drawable/tests/AnimatedVectorDrawableTest.java
@@ -16,6 +16,10 @@
 
 package android.support.graphics.drawable.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -24,16 +28,16 @@
 import android.support.annotation.DrawableRes;
 import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
 import android.support.graphics.drawable.animated.test.R;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 import android.view.View;
 import android.widget.ImageButton;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -47,8 +51,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static org.junit.Assert.*;
-
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class AnimatedVectorDrawableTest {
@@ -197,7 +199,7 @@
         final Canvas c = new Canvas(bitmap);
         CountDownLatch latch = new CountDownLatch(numTests);
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext,
@@ -223,7 +225,7 @@
      */
     private void verifyRedOnly(final int pixelX, final int pixelY, final View button,
             final Bitmap bitmap, final Canvas canvas, final CountDownLatch latch) throws Throwable {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 button.draw(canvas);
diff --git a/graphics/drawable/static/api/current.txt b/graphics/drawable/static/api/current.txt
deleted file mode 100644
index db07bf2..0000000
--- a/graphics/drawable/static/api/current.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-package android.support.graphics.drawable {
-
-   abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
-  }
-
-  public class VectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
-    method public static android.support.graphics.drawable.VectorDrawableCompat create(android.content.res.Resources, int, android.content.res.Resources.Theme);
-    method public static android.support.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas);
-    method public int getOpacity();
-    method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter);
-  }
-
-}
-
diff --git a/graphics/drawable/static/api/removed.txt b/graphics/drawable/static/api/removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/graphics/drawable/static/api/removed.txt
+++ /dev/null
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 2564a27..24d0d50 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -1,8 +1,8 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'support-vector-drawable'
 
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-compat')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -14,11 +14,12 @@
 }
 
 android {
-    compileSdkVersion 23
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
     }
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
index bcabd99..12b0383 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -14,7 +14,6 @@
 
 package android.support.graphics.drawable;
 
-import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.ColorFilter;
@@ -30,13 +29,14 @@
 /**
  * Internal common delegation shared by VectorDrawableCompat and AnimatedVectorDrawableCompat
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 abstract class VectorDrawableCommon extends Drawable implements TintAwareDrawable {
     /**
      * Obtains styled attributes from the theme, if available, or unstyled
      * resources if the theme is null.
+     *
+     * @hide
      */
-    static TypedArray obtainAttributes(
+    protected static TypedArray obtainAttributes(
             Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
         if (theme == null) {
             return res.obtainAttributes(set, attrs);
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index f4d1198..8c93919 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -16,7 +16,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
-import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -52,9 +51,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Stack;
@@ -221,8 +217,6 @@
  * </dl></dd>
  * </dl>
  */
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class VectorDrawableCompat extends VectorDrawableCommon {
     static final String LOGTAG = "VectorDrawableCompat";
 
@@ -678,7 +672,11 @@
             case 15:
                 return Mode.SCREEN;
             case 16:
-                return Mode.ADD;
+                if (Build.VERSION.SDK_INT >= 11) {
+                    return Mode.ADD;
+                } else {
+                    return defaultMode;
+                }
             default:
                 return defaultMode;
         }
diff --git a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
index c941bbd..97b19e7 100644
--- a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
+++ b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
@@ -16,6 +16,11 @@
 
 package android.support.graphics.drawable.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -28,9 +33,10 @@
 import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.graphics.drawable.test.R;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,8 +46,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import static org.junit.Assert.*;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class VectorDrawableTest {
diff --git a/media-compat/Android.mk b/media-compat/Android.mk
index e8f9c8b..da9686c 100644
--- a/media-compat/Android.mk
+++ b/media-compat/Android.mk
@@ -14,98 +14,35 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-compat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of Lollipop APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-kitkat
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V22 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api22
-LOCAL_SDK_VERSION := 22
-LOCAL_SRC_FILES := $(call all-java-files-under, api22)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api21
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V23 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api22
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api24
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api23
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-media-compat \
+#       android-support-compat \
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-media-compat
-LOCAL_SDK_VERSION := 9
-LOCAL_AIDL_INCLUDES := frameworks/support/media-compat/java
-LOCAL_SRC_FILES := $(call all-java-files-under, java) \
-    $(call all-Iaidl-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/java
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,ics) \
+    $(call all-java-files-under,jellybean-mr2) \
+    $(call all-java-files-under,kitkat) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,api22) \
+    $(call all-java-files-under,api23) \
+    $(call all-java-files-under,api24) \
+    $(call all-java-files-under,api25) \
+    $(call all-java-files-under,java) \
+    $(call all-Iaidl-files-under,java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-media-compat-api24
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index 1a17dae..9a0b0f3 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -16,15 +16,18 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 
-import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaBrowserCompatApi21 {
     static final String NULL_MEDIA_ITEM_ID =
             "android.support.v4.media.MediaBrowserCompat.NULL_MEDIA_ITEM";
diff --git a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
index 2a7eaf3..affb130 100644
--- a/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.media.browse.MediaBrowser;
@@ -24,10 +25,13 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.service.media.MediaBrowserService;
+import android.support.annotation.RequiresApi;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaBrowserServiceCompatApi21 {
 
     public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 234a77a..556d092 100644
--- a/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -15,12 +15,16 @@
  */
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.media.MediaDescription;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaDescriptionCompatApi21 {
 
     public static String getMediaId(Object descriptionObj) {
diff --git a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
index fd51f78..ed30c29 100644
--- a/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -16,13 +16,17 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.os.Parcel;
+import android.support.annotation.RequiresApi;
 
 import java.util.Set;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaMetadataCompatApi21 {
     public static Set<String> keySet(Object metadataObj) {
         return ((MediaMetadata)metadataObj).keySet();
diff --git a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
index 9292b93..ab5e4ef 100644
--- a/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/media-compat/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -16,7 +16,9 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.browse.MediaBrowser;
+import android.support.annotation.RequiresApi;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -25,6 +27,8 @@
 /**
  * An adapter class for accessing the hidden framework classes, ParceledListSlice using reflection.
  */
+@RequiresApi(21)
+@TargetApi(21)
 class ParceledListSliceAdapterApi21 {
     private static Constructor sConstructor;
     static {
diff --git a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
index c0f21c5..66f5144 100644
--- a/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/VolumeProviderCompatApi21.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.VolumeProvider;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(21)
+@TargetApi(21)
 class VolumeProviderCompatApi21 {
     public static Object createVolumeProvider(int volumeControl, int maxVolume, int currentVolume,
             final Delegate delegate) {
diff --git a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
index 8de40dc..9b00e8c 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.media.AudioAttributes;
@@ -27,13 +28,15 @@
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Parcel;
 import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaControllerCompatApi21 {
     public static Object fromToken(Context context, Object sessionToken) {
         return new MediaController(context, (MediaSession.Token) sessionToken);
diff --git a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index 1b3f8fd..38c42cb 100644
--- a/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -30,10 +31,13 @@
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(21)
+@TargetApi(21)
 class MediaSessionCompatApi21 {
     public static Object createSession(Context context, String tag) {
         return new MediaSession(context, tag);
diff --git a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
index 09d3e32..df6a203 100644
--- a/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/media-compat/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -16,13 +16,15 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
 
-import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(21)
+@TargetApi(21)
 class PlaybackStateCompatApi21 {
     public static int getState(Object stateObj) {
         return ((PlaybackState)stateObj).getState();
diff --git a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
index b847778..687e965 100644
--- a/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -15,8 +15,12 @@
  */
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.MediaSession;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(22)
+@TargetApi(22)
 class MediaSessionCompatApi22 {
 
     public static void setRatingType(Object sessionObj, int type) {
diff --git a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
index 55d4b83..ff398b9 100644
--- a/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/media-compat/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -16,12 +16,15 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
-import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(22)
+@TargetApi(22)
 class PlaybackStateCompatApi22 {
     public static Bundle getExtras(Object stateObj) {
         return ((PlaybackState)stateObj).getExtras();
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
index 1e9df1a..308c490 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -16,10 +16,14 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.browse.MediaBrowser;
 import android.os.Parcel;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class MediaBrowserCompatApi23 {
 
     public static Object createItemCallback(ItemCallback callback) {
diff --git a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
index 4eab9c2..1091dec 100644
--- a/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
@@ -16,11 +16,15 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Parcel;
+import android.support.annotation.RequiresApi;
 import android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper;
 
+@RequiresApi(23)
+@TargetApi(23)
 class MediaBrowserServiceCompatApi23 {
 
     public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
diff --git a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
index 2c1bda3..862fbbf 100644
--- a/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -15,9 +15,13 @@
  */
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.MediaDescription;
 import android.net.Uri;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
     public static Uri getMediaUri(Object descriptionObj) {
         return ((MediaDescription) descriptionObj).getMediaUri();
diff --git a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
index d7e6669..92e49fc 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -16,10 +16,14 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class MediaControllerCompatApi23 {
 
     public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
diff --git a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
index cc8bba3..ddb25fc 100644
--- a/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/media-compat/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -16,9 +16,13 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class MediaSessionCompatApi23 {
 
     public static Object createCallback(Callback callback) {
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
index 1e364a6..45a428c 100644
--- a/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserCompatApi24.java
@@ -16,12 +16,16 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 
 import java.util.List;
 
+@RequiresApi(24)
+@TargetApi(24)
 class MediaBrowserCompatApi24 {
     public static Object createSubscriptionCallback(SubscriptionCallback callback) {
         return new SubscriptionCallbackProxy<>(callback);
diff --git a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
index a1a4f48..2440864 100644
--- a/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/MediaBrowserServiceCompatApi24.java
@@ -16,17 +16,21 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.service.media.MediaBrowserService;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(24)
+@TargetApi(24)
 class MediaBrowserServiceCompatApi24 {
     private static final String TAG = "MBSCompatApi24";
 
diff --git a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
index 04bf843..3c8b650 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -16,10 +16,14 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(24)
+@TargetApi(24)
 class MediaControllerCompatApi24 {
 
     public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
diff --git a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
index e03a3ee..506b04f 100644
--- a/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/media-compat/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -16,14 +16,18 @@
 
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+@RequiresApi(24)
+@TargetApi(24)
 class MediaSessionCompatApi24 {
     private static final String TAG = "MediaSessionCompatApi24";
 
diff --git a/media-compat/api25/android/support/v4/media/session/MediaControllerCompatApi25.java b/media-compat/api25/android/support/v4/media/session/MediaControllerCompatApi25.java
new file mode 100644
index 0000000..307dfe2
--- /dev/null
+++ b/media-compat/api25/android/support/v4/media/session/MediaControllerCompatApi25.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media.session;
+
+import android.media.session.MediaController;
+
+class MediaControllerCompatApi25 {
+    public static Object createCallback(Callback callback) {
+        return new CallbackProxy<Callback>(callback);
+    }
+
+    public static int getRepeatMode(Object controllerObj) {
+        return ((MediaController) controllerObj).getRepeatMode();
+    }
+
+    public static boolean isShuffleModeEnabled(Object controllerObj) {
+        return ((MediaController) controllerObj).isShuffleModeEnabled();
+    }
+
+    public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
+        public static void setRepeatMode(Object controlsObj, int repeatMode) {
+            ((MediaController.TransportControls) controlsObj).setRepeatMode(repeatMode);
+        }
+
+        public static void setShuffleModeEnabled(Object controlsObj, boolean enabled) {
+            ((MediaController.TransportControls) controlsObj).setShuffleModeEnabled(enabled);
+        }
+    }
+
+    public interface Callback extends MediaControllerCompatApi21.Callback {
+        void onRepeatModeChanged(int repeatMode);
+        void onShuffleModeChanged(boolean enabled);
+    }
+
+    static class CallbackProxy<T extends Callback> extends MediaControllerCompatApi21
+            .CallbackProxy<T> {
+        CallbackProxy(T callback) {
+            super(callback);
+        }
+
+        @Override
+        public void onRepeatModeChanged(int repeatMode) {
+            mCallback.onRepeatModeChanged(repeatMode);
+        }
+
+        @Override
+        public void onShuffleModeChanged(boolean enabled) {
+            mCallback.onShuffleModeChanged(enabled);
+        }
+    }
+}
diff --git a/media-compat/api25/android/support/v4/media/session/MediaSessionCompatApi25.java b/media-compat/api25/android/support/v4/media/session/MediaSessionCompatApi25.java
new file mode 100644
index 0000000..cba1020
--- /dev/null
+++ b/media-compat/api25/android/support/v4/media/session/MediaSessionCompatApi25.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media.session;
+
+import android.media.session.MediaSession;
+
+class MediaSessionCompatApi25 {
+
+    public static Object createCallback(Callback callback) {
+        return new CallbackProxy<Callback>(callback);
+    }
+
+    public static void setRepeatMode(Object sessionObj, int repeatMode) {
+        ((MediaSession) sessionObj).setRepeatMode(repeatMode);
+    }
+
+    public static void setShuffleModeEnabled(Object sessionObj, boolean enabled) {
+        ((MediaSession) sessionObj).setShuffleModeEnabled(enabled);
+    }
+
+    public interface Callback extends MediaSessionCompatApi24.Callback {
+        void onSetRepeatMode(int repeatMode);
+        void onSetShuffleModeEnabled(boolean enabled);
+    }
+
+    static class CallbackProxy<T extends Callback>
+            extends MediaSessionCompatApi24.CallbackProxy<T> {
+        CallbackProxy(T callback) {
+            super(callback);
+        }
+
+        @Override
+        public void onSetRepeatMode(int repeatMode) {
+            mCallback.onSetRepeatMode(repeatMode);
+        }
+
+        @Override
+        public void onSetShuffleModeEnabled(boolean enabled) {
+            mCallback.onSetShuffleModeEnabled(enabled);
+        }
+    }
+}
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index 3e547b3..4b569a4 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -1,9 +1,8 @@
 apply plugin: 'com.android.library'
 archivesBaseName = 'support-media-compat'
 
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.mediacompat.apiTargets)
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-compat')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -18,20 +17,27 @@
 
 sourceCompatibility = JavaVersion.VERSION_1_7
 targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.mediacompat.dependencies)
 
 android {
-    compileSdkVersion 9
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
+        main.java.srcDirs = [
+                'ics',
+                'jellybean-mr2',
+                'kitkat',
+                'api21',
+                'api22',
+                'api23',
+                'api24',
+                'api25',
+                'java'
+        ]
         main.aidl.srcDirs = ['java']
     }
 
@@ -77,11 +83,6 @@
         exclude('android/service/media/**')
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java b/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
index a0e0e7d..b6f7a38 100644
--- a/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
+++ b/media-compat/ics/android/support/v4/media/session/MediaSessionCompatApi14.java
@@ -15,16 +15,18 @@
  */
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.media.MediaMetadataRetriever;
 import android.media.RemoteControlClient;
 import android.os.Bundle;
-import android.os.ResultReceiver;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(14)
+@TargetApi(14)
 class MediaSessionCompatApi14 {
     /***** RemoteControlClient States, we only need none as the others were public *******/
     final static int RCC_PLAYSTATE_NONE = 0;
diff --git a/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl b/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
index d905350..d1d143d 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaControllerCallback.aidl
@@ -37,4 +37,6 @@
     void onQueueTitleChanged(CharSequence title);
     void onExtrasChanged(in Bundle extras);
     void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+    void onRepeatModeChanged(int repeatMode);
+    void onShuffleModeChanged(boolean enabled);
 }
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 718b870..4f2e38a 100644
--- a/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
+++ b/media-compat/java/android/support/v4/media/session/IMediaSession.aidl
@@ -33,42 +33,47 @@
  * @hide
  */
 interface IMediaSession {
-    void sendCommand(String command, in Bundle args, in MediaSessionCompat.ResultReceiverWrapper cb);
-    boolean sendMediaButton(in KeyEvent mediaButton);
-    void registerCallbackListener(in IMediaControllerCallback cb);
-    void unregisterCallbackListener(in IMediaControllerCallback cb);
-    boolean isTransportControlEnabled();
-    String getPackageName();
-    String getTag();
-    PendingIntent getLaunchPendingIntent();
-    long getFlags();
-    ParcelableVolumeInfo getVolumeAttributes();
-    void adjustVolume(int direction, int flags, String packageName);
-    void setVolumeTo(int value, int flags, String packageName);
+    // Next ID: 40
+    void sendCommand(String command, in Bundle args, in MediaSessionCompat.ResultReceiverWrapper cb) = 0;
+    boolean sendMediaButton(in KeyEvent mediaButton) = 1;
+    void registerCallbackListener(in IMediaControllerCallback cb) = 2;
+    void unregisterCallbackListener(in IMediaControllerCallback cb) = 3;
+    boolean isTransportControlEnabled() = 4;
+    String getPackageName() = 5;
+    String getTag() = 6;
+    PendingIntent getLaunchPendingIntent() = 7;
+    long getFlags() = 8;
+    ParcelableVolumeInfo getVolumeAttributes() = 9;
+    void adjustVolume(int direction, int flags, String packageName) = 10;
+    void setVolumeTo(int value, int flags, String packageName) = 11;
+    MediaMetadataCompat getMetadata() = 26;
+    PlaybackStateCompat getPlaybackState() = 27;
+    List<MediaSessionCompat.QueueItem> getQueue() = 28;
+    CharSequence getQueueTitle() = 29;
+    Bundle getExtras() = 30;
+    int getRatingType() = 31;
+    int getRepeatMode() = 36;
+    boolean isShuffleModeEnabled() = 37;
 
     // These commands are for the TransportControls
-    void play();
-    void playFromMediaId(String uri, in Bundle extras);
-    void playFromSearch(String string, in Bundle extras);
-    void playFromUri(in Uri uri, in Bundle extras);
-    void skipToQueueItem(long id);
-    void pause();
-    void stop();
-    void next();
-    void previous();
-    void fastForward();
-    void rewind();
-    void seekTo(long pos);
-    void rate(in RatingCompat rating);
-    void sendCustomAction(String action, in Bundle args);
-    MediaMetadataCompat getMetadata();
-    PlaybackStateCompat getPlaybackState();
-    List<MediaSessionCompat.QueueItem> getQueue();
-    CharSequence getQueueTitle();
-    Bundle getExtras();
-    int getRatingType();
-    void prepare();
-    void prepareFromMediaId(String uri, in Bundle extras);
-    void prepareFromSearch(String string, in Bundle extras);
-    void prepareFromUri(in Uri uri, in Bundle extras);
+    void prepare() = 32;
+    void prepareFromMediaId(String uri, in Bundle extras) = 33;
+    void prepareFromSearch(String string, in Bundle extras) = 34;
+    void prepareFromUri(in Uri uri, in Bundle extras) = 35;
+    void play() = 12;
+    void playFromMediaId(String uri, in Bundle extras) = 13;
+    void playFromSearch(String string, in Bundle extras) = 14;
+    void playFromUri(in Uri uri, in Bundle extras) = 15;
+    void skipToQueueItem(long id) = 16;
+    void pause() = 17;
+    void stop() = 18;
+    void next() = 19;
+    void previous() = 20;
+    void fastForward() = 21;
+    void rewind() = 22;
+    void seekTo(long pos) = 23;
+    void rate(in RatingCompat rating) = 24;
+    void setRepeatMode(int repeatMode) = 38;
+    void setShuffleModeEnabled(boolean shuffleMode) = 39;
+    void sendCustomAction(String action, in Bundle args) = 25;
 }
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 266a4d1..8f73abf 100644
--- a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -27,6 +27,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.support.v4.app.BundleCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
 import android.support.v4.media.VolumeProviderCompat;
@@ -36,6 +37,8 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -54,6 +57,9 @@
 public final class MediaControllerCompat {
     static final String TAG = "MediaControllerCompat";
 
+    static final String COMMAND_GET_EXTRA_BINDER =
+            "android.support.v4.media.session.command.GET_EXTRA_BINDER";
+
     private final MediaControllerImpl mImpl;
     private final MediaSessionCompat.Token mToken;
 
@@ -68,7 +74,9 @@
         }
         mToken = session.getSessionToken();
 
-        if (android.os.Build.VERSION.SDK_INT >= 24) {
+        if (android.os.Build.VERSION.SDK_INT >= 25) {
+            mImpl = new MediaControllerImplApi25(context, session);
+        } else if (android.os.Build.VERSION.SDK_INT >= 24) {
             mImpl = new MediaControllerImplApi24(context, session);
         } else if (android.os.Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaControllerImplApi23(context, session);
@@ -93,7 +101,9 @@
         }
         mToken = sessionToken;
 
-        if (android.os.Build.VERSION.SDK_INT >= 24) {
+        if (android.os.Build.VERSION.SDK_INT >= 25) {
+            mImpl = new MediaControllerImplApi25(context, sessionToken);
+        } else if (android.os.Build.VERSION.SDK_INT >= 24) {
             mImpl = new MediaControllerImplApi24(context, sessionToken);
         } else if (android.os.Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaControllerImplApi23(context, sessionToken);
@@ -188,6 +198,25 @@
     }
 
     /**
+     * Get the repeat mode for this session.
+     *
+     * @return The latest repeat mode set to the session, or
+     *         {@link PlaybackStateCompat#REPEAT_MODE_NONE} if not set.
+     */
+    public int getRepeatMode() {
+        return mImpl.getRepeatMode();
+    }
+
+    /**
+     * Return whether the shuffle mode is enabled for this session.
+     *
+     * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set.
+     */
+    public boolean isShuffleModeEnabled() {
+        return mImpl.isShuffleModeEnabled();
+    }
+
+    /**
      * Get the flags for this session. Flags are defined in
      * {@link MediaSessionCompat}.
      *
@@ -344,11 +373,14 @@
     public static abstract class Callback implements IBinder.DeathRecipient {
         private final Object mCallbackObj;
         MessageHandler mHandler;
+        boolean mHasExtraCallback;
 
         boolean mRegistered = false;
 
         public Callback() {
-            if (android.os.Build.VERSION.SDK_INT >= 21) {
+            if (android.os.Build.VERSION.SDK_INT >= 25) {
+                mCallbackObj = MediaControllerCompatApi25.createCallback(new StubApi25());
+            } else if (android.os.Build.VERSION.SDK_INT >= 21) {
                 mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21());
             } else {
                 mCallbackObj = new StubCompat();
@@ -428,6 +460,25 @@
         public void onAudioInfoChanged(PlaybackInfo info) {
         }
 
+        /**
+         * Override to handle changes to the repeat mode.
+         *
+         * @param repeatMode The repeat mode. It should be one of followings:
+         *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         */
+        public void onRepeatModeChanged(@PlaybackStateCompat.RepeatMode int repeatMode) {
+        }
+
+        /**
+         * Override to handle changes to the shuffle mode.
+         *
+         * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise.
+         */
+        public void onShuffleModeChanged(boolean enabled) {
+        }
+
         @Override
         public void binderDied() {
             onSessionDestroyed();
@@ -451,13 +502,21 @@
 
             @Override
             public void onSessionEvent(String event, Bundle extras) {
-                Callback.this.onSessionEvent(event, extras);
+                if (mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 23) {
+                    // Ignore. ExtraCallback will handle this.
+                } else {
+                    Callback.this.onSessionEvent(event, extras);
+                }
             }
 
             @Override
             public void onPlaybackStateChanged(Object stateObj) {
-                Callback.this.onPlaybackStateChanged(
-                        PlaybackStateCompat.fromPlaybackState(stateObj));
+                if (mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 22) {
+                    // Ignore. ExtraCallback will handle this.
+                } else {
+                    Callback.this.onPlaybackStateChanged(
+                            PlaybackStateCompat.fromPlaybackState(stateObj));
+                }
             }
 
             @Override
@@ -488,6 +547,18 @@
             }
         }
 
+        private class StubApi25 extends StubApi21 implements MediaControllerCompatApi25.Callback {
+            @Override
+            public void onRepeatModeChanged(@PlaybackStateCompat.RepeatMode int repeatMode) {
+                Callback.this.onRepeatModeChanged(repeatMode);
+            }
+
+            @Override
+            public void onShuffleModeChanged(boolean enabled) {
+                Callback.this.onShuffleModeChanged(enabled);
+            }
+        }
+
         private class StubCompat extends IMediaControllerCallback.Stub {
 
             StubCompat() {
@@ -524,6 +595,16 @@
             }
 
             @Override
+            public void onRepeatModeChanged(int repeatMode) throws RemoteException {
+                mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE, repeatMode, null);
+            }
+
+            @Override
+            public void onShuffleModeChanged(boolean enabled) throws RemoteException {
+                mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE, enabled, null);
+            }
+
+            @Override
             public void onExtrasChanged(Bundle extras) throws RemoteException {
                 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS, extras, null);
             }
@@ -548,6 +629,8 @@
             private static final int MSG_UPDATE_QUEUE_TITLE = 6;
             private static final int MSG_UPDATE_EXTRAS = 7;
             private static final int MSG_DESTROYED = 8;
+            private static final int MSG_UPDATE_REPEAT_MODE = 9;
+            private static final int MSG_UPDATE_SHUFFLE_MODE = 10;
 
             public MessageHandler(Looper looper) {
                 super(looper);
@@ -574,6 +657,12 @@
                     case MSG_UPDATE_QUEUE_TITLE:
                         onQueueTitleChanged((CharSequence) msg.obj);
                         break;
+                    case MSG_UPDATE_REPEAT_MODE:
+                        onRepeatModeChanged((int) msg.obj);
+                        break;
+                    case MSG_UPDATE_SHUFFLE_MODE:
+                        onShuffleModeChanged((boolean) msg.obj);
+                        break;
                     case MSG_UPDATE_EXTRAS:
                         onExtrasChanged((Bundle) msg.obj);
                         break;
@@ -749,6 +838,23 @@
         public abstract void setRating(RatingCompat rating);
 
         /**
+         * Set the repeat mode for this session.
+         *
+         * @param repeatMode The repeat mode. Must be one of the followings:
+         *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         */
+        public abstract void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode);
+
+        /**
+         * Set the shuffle mode for this session.
+         *
+         * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
+         */
+        public abstract void setShuffleModeEnabled(boolean enabled);
+
+        /**
          * Send a custom action for the {@link MediaSessionCompat} to perform.
          *
          * @param customAction The action to perform.
@@ -873,6 +979,8 @@
         CharSequence getQueueTitle();
         Bundle getExtras();
         int getRatingType();
+        int getRepeatMode();
+        boolean isShuffleModeEnabled();
         long getFlags();
         PlaybackInfo getPlaybackInfo();
         PendingIntent getSessionActivity();
@@ -1009,6 +1117,26 @@
         }
 
         @Override
+        public int getRepeatMode() {
+            try {
+                return mBinder.getRepeatMode();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in getRepeatMode. " + e);
+            }
+            return 0;
+        }
+
+        @Override
+        public boolean isShuffleModeEnabled() {
+            try {
+                return mBinder.isShuffleModeEnabled();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in isShuffleModeEnabled. " + e);
+            }
+            return false;
+        }
+
+        @Override
         public long getFlags() {
             try {
                 return mBinder.getFlags();
@@ -1246,6 +1374,24 @@
         }
 
         @Override
+        public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+            try {
+                mBinder.setRepeatMode(repeatMode);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setRepeatMode. " + e);
+            }
+        }
+
+        @Override
+        public void setShuffleModeEnabled(boolean enabled) {
+            try {
+                mBinder.setShuffleModeEnabled(enabled);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setShuffleModeEnabled. " + e);
+            }
+        }
+
+        @Override
         public void sendCustomAction(CustomAction customAction, Bundle args) {
             sendCustomAction(customAction.getAction(), args);
         }
@@ -1263,9 +1409,18 @@
     static class MediaControllerImplApi21 implements MediaControllerImpl {
         protected final Object mControllerObj;
 
+        // Extra binder is used for applying the framework change of new APIs and bug fixes
+        // after API 21.
+        private IMediaSession mExtraBinder;
+        private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>();
+        private List<Callback> mPendingCallbacks;
+
         public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
             mControllerObj = MediaControllerCompatApi21.fromToken(context,
                     session.getSessionToken().getToken());
+            if (android.os.Build.VERSION.SDK_INT < 23) {
+                requestExtraBinder();
+            }
         }
 
         public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken)
@@ -1273,16 +1428,52 @@
             mControllerObj = MediaControllerCompatApi21.fromToken(context,
                     sessionToken.getToken());
             if (mControllerObj == null) throw new RemoteException();
+            if (android.os.Build.VERSION.SDK_INT < 23) {
+                requestExtraBinder();
+            }
         }
 
         @Override
-        public void registerCallback(Callback callback, Handler handler) {
-            MediaControllerCompatApi21.registerCallback(mControllerObj, callback.mCallbackObj, handler);
+        public final void registerCallback(Callback callback, Handler handler) {
+            MediaControllerCompatApi21.registerCallback(
+                    mControllerObj, callback.mCallbackObj, handler);
+            if (mExtraBinder != null) {
+                callback.setHandler(handler);
+                ExtraCallback extraCallback = new ExtraCallback(callback);
+                mCallbackMap.put(callback, extraCallback);
+                callback.mHasExtraCallback = true;
+                try {
+                    mExtraBinder.registerCallbackListener(extraCallback);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Dead object in registerCallback. " + e);
+                }
+            } else if (android.os.Build.VERSION.SDK_INT < 23) {
+                if (mPendingCallbacks == null) {
+                    mPendingCallbacks = new ArrayList<>();
+                }
+                callback.setHandler(handler);
+                mPendingCallbacks.add(callback);
+            }
         }
 
         @Override
-        public void unregisterCallback(Callback callback) {
+        public final void unregisterCallback(Callback callback) {
             MediaControllerCompatApi21.unregisterCallback(mControllerObj, callback.mCallbackObj);
+            if (mExtraBinder != null) {
+                try {
+                    ExtraCallback extraCallback = mCallbackMap.remove(callback);
+                    if (extraCallback != null) {
+                        mExtraBinder.unregisterCallbackListener(extraCallback);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Dead object in unregisterCallback. " + e);
+                }
+            } else if (android.os.Build.VERSION.SDK_INT < 23) {
+                if (mPendingCallbacks == null) {
+                    mPendingCallbacks = new ArrayList<>();
+                }
+                mPendingCallbacks.remove(callback);
+            }
         }
 
         @Override
@@ -1298,6 +1489,13 @@
 
         @Override
         public PlaybackStateCompat getPlaybackState() {
+            if (android.os.Build.VERSION.SDK_INT < 22 && mExtraBinder != null) {
+                try {
+                    return mExtraBinder.getPlaybackState();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Dead object in getPlaybackState. " + e);
+                }
+            }
             Object stateObj = MediaControllerCompatApi21.getPlaybackState(mControllerObj);
             return stateObj != null ? PlaybackStateCompat.fromPlaybackState(stateObj) : null;
         }
@@ -1327,10 +1525,29 @@
 
         @Override
         public int getRatingType() {
+            if (android.os.Build.VERSION.SDK_INT < 22 && mExtraBinder != null) {
+                try {
+                    return mExtraBinder.getRatingType();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Dead object in getRatingType. " + e);
+                }
+            }
             return MediaControllerCompatApi21.getRatingType(mControllerObj);
         }
 
         @Override
+        public int getRepeatMode() {
+            // TODO: implement this
+            return 0;
+        }
+
+        @Override
+        public boolean isShuffleModeEnabled() {
+            // TODO: implement this
+            return false;
+        }
+
+        @Override
         public long getFlags() {
             return MediaControllerCompatApi21.getFlags(mControllerObj);
         }
@@ -1375,6 +1592,111 @@
         public Object getMediaController() {
             return mControllerObj;
         }
+
+        private void requestExtraBinder() {
+            ResultReceiver cb = new ResultReceiver(new Handler()) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle resultData) {
+                    if (resultData != null) {
+                        mExtraBinder = IMediaSession.Stub.asInterface(
+                                BundleCompat.getBinder(
+                                        resultData, MediaSessionCompat.EXTRA_BINDER));
+                        if (mPendingCallbacks != null) {
+                            for (Callback callback : mPendingCallbacks) {
+                                ExtraCallback extraCallback = new ExtraCallback(callback);
+                                mCallbackMap.put(callback, extraCallback);
+                                callback.mHasExtraCallback = true;
+                                try {
+                                    mExtraBinder.registerCallbackListener(extraCallback);
+                                } catch (RemoteException e) {
+                                    Log.e(TAG, "Dead object in registerCallback. " + e);
+                                    break;
+                                }
+                            }
+                            mPendingCallbacks = null;
+                        }
+                    }
+                }
+            };
+            sendCommand(COMMAND_GET_EXTRA_BINDER, null, cb);
+        }
+
+        private class ExtraCallback extends IMediaControllerCallback.Stub {
+            private Callback mCallback;
+
+            ExtraCallback(Callback callback) {
+                mCallback = callback;
+            }
+
+            @Override
+            public void onEvent(final String event, final Bundle extras) throws RemoteException {
+                mCallback.mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCallback.onSessionEvent(event, extras);
+                    }
+                });
+            }
+
+            @Override
+            public void onSessionDestroyed() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onPlaybackStateChanged(final PlaybackStateCompat state)
+                    throws RemoteException {
+                mCallback.mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCallback.onPlaybackStateChanged(state);
+                    }
+                });
+            }
+
+            @Override
+            public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onQueueChanged(List<QueueItem> queue) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onQueueTitleChanged(CharSequence title) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onRepeatModeChanged(int repeatMode) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onShuffleModeChanged(boolean enabled) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onExtrasChanged(Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+        }
     }
 
     static class TransportControlsApi21 extends TransportControls {
@@ -1460,6 +1782,16 @@
         }
 
         @Override
+        public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+            // TODO: implement this
+        }
+
+        @Override
+        public void setShuffleModeEnabled(boolean enabled) {
+            // TODO: implement this
+        }
+
+        @Override
         public void playFromMediaId(String mediaId, Bundle extras) {
             MediaControllerCompatApi21.TransportControls.playFromMediaId(mControlsObj, mediaId,
                     extras);
@@ -1579,4 +1911,50 @@
         }
     }
 
+    static class MediaControllerImplApi25 extends MediaControllerImplApi24 {
+
+        MediaControllerImplApi25(Context context, MediaSessionCompat session) {
+            super(context, session);
+        }
+
+        MediaControllerImplApi25(Context context, MediaSessionCompat.Token sessionToken)
+                throws RemoteException {
+            super(context, sessionToken);
+        }
+
+        @Override
+        public TransportControls getTransportControls() {
+            Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj);
+            return controlsObj != null ? new TransportControlsApi25(controlsObj) : null;
+        }
+
+        @Override
+        public int getRepeatMode() {
+            return MediaControllerCompatApi25.getRepeatMode(mControllerObj);
+        }
+
+        @Override
+        public boolean isShuffleModeEnabled() {
+            return MediaControllerCompatApi25.isShuffleModeEnabled(mControllerObj);
+        }
+    }
+
+    static class TransportControlsApi25 extends TransportControlsApi24 {
+
+        TransportControlsApi25(Object controlsObj) {
+            super(controlsObj);
+        }
+
+        @Override
+        public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+            MediaControllerCompatApi25.TransportControls.setRepeatMode(mControlsObj, repeatMode);
+        }
+
+        @Override
+        public void setShuffleModeEnabled(boolean enabled) {
+            MediaControllerCompatApi25.TransportControls.setShuffleModeEnabled(mControlsObj,
+                    enabled);
+        }
+    }
+
 }
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 e73666c..b175143 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -17,13 +17,14 @@
 
 package android.support.v4.media.session;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Build;
@@ -40,6 +41,7 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.RestrictTo;
+import android.support.v4.app.BundleCompat;
 import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
@@ -51,11 +53,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Allows interaction with media controllers, volume keys, media buttons, and
  * transport controls.
@@ -163,6 +164,8 @@
     static final String ACTION_ARGUMENT_EXTRAS =
             "android.support.v4.media.session.action.ARGUMENT_EXTRAS";
 
+    static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER";
+
     // Maximum size of the bitmap in dp.
     private static final int MAX_BITMAP_SIZE_IN_DP = 320;
 
@@ -221,6 +224,10 @@
 
         if (android.os.Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaSessionImplApi21(context, tag);
+            if (android.os.Build.VERSION.SDK_INT < 23) {
+                // Set default callback to respond to controllers' extra binder requests.
+                setCallback(new Callback() {});
+            }
         } else {
             mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent);
         }
@@ -234,6 +241,10 @@
 
     private MediaSessionCompat(Context context, MediaSessionImpl impl) {
         mImpl = impl;
+        if (android.os.Build.VERSION.SDK_INT >= 21 && android.os.Build.VERSION.SDK_INT < 23) {
+            // Set default callback to respond to controllers' extra binder requests.
+            setCallback(new Callback() {});
+        }
         mController = new MediaControllerCompat(context, this);
     }
 
@@ -480,6 +491,33 @@
     }
 
     /**
+     * Set the repeat mode for this session.
+     * <p>
+     * Note that if this method is not called before, {@link MediaControllerCompat#getRepeatMode}
+     * will return {@link PlaybackStateCompat#REPEAT_MODE_NONE}.
+     *
+     * @param repeatMode The repeat mode. Must be one of the followings:
+     *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
+     *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
+     *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+     */
+    public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+        mImpl.setRepeatMode(repeatMode);
+    }
+
+    /**
+     * Set the shuffle mode for this session.
+     * <p>
+     * Note that if this method is not called before,
+     * {@link MediaControllerCompat#isShuffleModeEnabled} will return {@code false}.
+     *
+     * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
+     */
+    public void setShuffleModeEnabled(boolean enabled) {
+        mImpl.setShuffleModeEnabled(enabled);
+    }
+
+    /**
      * Set some extras that can be associated with the
      * {@link MediaSessionCompat}. No assumptions should be made as to how a
      * {@link MediaControllerCompat} will handle these extras. Keys should be
@@ -597,9 +635,12 @@
      */
     public abstract static class Callback {
         final Object mCallbackObj;
+        WeakReference<MediaSessionImpl> mSessionImpl;
 
         public Callback() {
-            if (android.os.Build.VERSION.SDK_INT >= 24) {
+            if (android.os.Build.VERSION.SDK_INT >= 25) {
+                mCallbackObj = MediaSessionCompatApi25.createCallback(new StubApi25());
+            } else if (android.os.Build.VERSION.SDK_INT >= 24) {
                 mCallbackObj = MediaSessionCompatApi24.createCallback(new StubApi24());
             } else if (android.os.Build.VERSION.SDK_INT >= 23) {
                 mCallbackObj = MediaSessionCompatApi23.createCallback(new StubApi23());
@@ -765,6 +806,33 @@
         }
 
         /**
+         * Override to handle the setting of the repeat mode.
+         * <p>
+         * You should call {@link #setRepeatMode} before end of this method in order to notify
+         * the change to the {@link MediaControllerCompat}, or
+         * {@link MediaControllerCompat#getRepeatMode} could return an invalid value.
+         *
+         * @param repeatMode The repeat mode which is one of followings:
+         *            {@link PlaybackStateCompat#REPEAT_MODE_NONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ONE},
+         *            {@link PlaybackStateCompat#REPEAT_MODE_ALL}
+         */
+        public void onSetRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+        }
+
+        /**
+         * Override to handle the setting of the shuffle mode.
+         * <p>
+         * You should call {@link #setShuffleModeEnabled} before the end of this method in order to
+         * notify the change to the {@link MediaControllerCompat}, or
+         * {@link MediaControllerCompat#isShuffleModeEnabled} could return an invalid value.
+         *
+         * @param enabled true when the shuffle mode is enabled, false otherwise.
+         */
+        public void onSetShuffleModeEnabled(boolean enabled) {
+        }
+
+        /**
          * Called when a {@link MediaControllerCompat} wants a
          * {@link PlaybackStateCompat.CustomAction} to be performed.
          *
@@ -783,7 +851,16 @@
 
             @Override
             public void onCommand(String command, Bundle extras, ResultReceiver cb) {
-                Callback.this.onCommand(command, extras, cb);
+                if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) {
+                    MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
+                    if (impl != null) {
+                        Bundle result = new Bundle();
+                        BundleCompat.putBinder(result, EXTRA_BINDER, impl.getExtraSessionBinder());
+                        cb.send(0, result);
+                    }
+                } else {
+                    Callback.this.onCommand(command, extras, cb);
+                }
             }
 
             @Override
@@ -913,6 +990,18 @@
                 Callback.this.onPrepareFromUri(uri, extras);
             }
         }
+
+        private class StubApi25 extends StubApi24 implements MediaSessionCompatApi25.Callback {
+            @Override
+            public void onSetRepeatMode(int repeatMode) {
+                Callback.this.onSetRepeatMode(repeatMode);
+            }
+
+            @Override
+            public void onSetShuffleModeEnabled(boolean enabled) {
+                Callback.this.onSetShuffleModeEnabled(enabled);
+            }
+        }
     }
 
     /**
@@ -1250,6 +1339,8 @@
         void setQueueTitle(CharSequence title);
 
         void setRatingType(@RatingCompat.Style int type);
+        void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode);
+        void setShuffleModeEnabled(boolean enabled);
         void setExtras(Bundle extras);
 
         Object getMediaSession();
@@ -1289,6 +1380,8 @@
         List<QueueItem> mQueue;
         CharSequence mQueueTitle;
         @RatingCompat.Style int mRatingType;
+        @PlaybackStateCompat.RepeatMode int mRepeatMode;
+        boolean mShuffleModeEnabled;
         Bundle mExtras;
 
         int mVolumeType;
@@ -1591,6 +1684,22 @@
         }
 
         @Override
+        public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+            if (mRepeatMode != repeatMode) {
+                mRepeatMode = repeatMode;
+                sendRepeatMode(repeatMode);
+            }
+        }
+
+        @Override
+        public void setShuffleModeEnabled(boolean enabled) {
+            if (mShuffleModeEnabled != enabled) {
+                mShuffleModeEnabled = enabled;
+                sendShuffleModeEnabled(enabled);
+            }
+        }
+
+        @Override
         public void setExtras(Bundle extras) {
             mExtras = extras;
             sendExtras(extras);
@@ -1809,6 +1918,30 @@
             mControllerCallbacks.finishBroadcast();
         }
 
+        private void sendRepeatMode(int repeatMode) {
+            int size = mControllerCallbacks.beginBroadcast();
+            for (int i = size - 1; i >= 0; i--) {
+                IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
+                try {
+                    cb.onRepeatModeChanged(repeatMode);
+                } catch (RemoteException e) {
+                }
+            }
+            mControllerCallbacks.finishBroadcast();
+        }
+
+        private void sendShuffleModeEnabled(boolean enabled) {
+            int size = mControllerCallbacks.beginBroadcast();
+            for (int i = size - 1; i >= 0; i--) {
+                IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i);
+                try {
+                    cb.onShuffleModeChanged(enabled);
+                } catch (RemoteException e) {
+                }
+            }
+            mControllerCallbacks.finishBroadcast();
+        }
+
         private void sendExtras(Bundle extras) {
             int size = mControllerCallbacks.beginBroadcast();
             for (int i = size - 1; i >= 0; i--) {
@@ -2005,6 +2138,16 @@
             }
 
             @Override
+            public void setRepeatMode(int repeatMode) throws RemoteException {
+                postToHandler(MessageHandler.MSG_SET_REPEAT_MODE, repeatMode);
+            }
+
+            @Override
+            public void setShuffleModeEnabled(boolean enabled) throws RemoteException {
+                postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE_ENABLED, enabled);
+            }
+
+            @Override
             public void sendCustomAction(String action, Bundle args)
                     throws RemoteException {
                 postToHandler(MessageHandler.MSG_CUSTOM_ACTION, action, args);
@@ -2046,6 +2189,17 @@
             }
 
             @Override
+            @PlaybackStateCompat.RepeatMode
+            public int getRepeatMode() {
+                return mRepeatMode;
+            }
+
+            @Override
+            public boolean isShuffleModeEnabled() {
+                return mShuffleModeEnabled;
+            }
+
+            @Override
             public boolean isTransportControlEnabled() {
                 return (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0;
             }
@@ -2087,6 +2241,8 @@
             private static final int MSG_CUSTOM_ACTION = 20;
             private static final int MSG_MEDIA_BUTTON = 21;
             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;
 
             // KeyEvent constants only available on API 11+
             private static final int KEYCODE_MEDIA_PAUSE = 127;
@@ -2194,6 +2350,12 @@
                     case MSG_SET_VOLUME:
                         setVolumeTo((int) msg.obj, 0);
                         break;
+                    case MSG_SET_REPEAT_MODE:
+                        cb.onSetRepeatMode((int) msg.obj);
+                        break;
+                    case MSG_SET_SHUFFLE_MODE_ENABLED:
+                        cb.onSetShuffleModeEnabled((boolean) msg.obj);
+                        break;
                 }
             }
 
@@ -2263,7 +2425,13 @@
         private final Object mSessionObj;
         private final Token mToken;
 
-        private PendingIntent mMediaButtonIntent;
+        private boolean mDestroyed = false;
+        private ExtraSession mExtraSessionBinder;
+        private final RemoteCallbackList<IMediaControllerCallback> mExtraControllerCallbacks =
+                new RemoteCallbackList<>();
+
+        private PlaybackStateCompat mPlaybackState;
+        @RatingCompat.Style int mRatingType;
 
         public MediaSessionImplApi21(Context context, String tag) {
             mSessionObj = MediaSessionCompatApi21.createSession(context, tag);
@@ -2279,6 +2447,9 @@
         public void setCallback(Callback callback, Handler handler) {
             MediaSessionCompatApi21.setCallback(mSessionObj,
                     callback == null ? null : callback.mCallbackObj, handler);
+            if (android.os.Build.VERSION.SDK_INT < 23) {
+                callback.mSessionImpl = new WeakReference<MediaSessionImpl>(this);
+            }
         }
 
         @Override
@@ -2309,11 +2480,23 @@
 
         @Override
         public void sendSessionEvent(String event, Bundle extras) {
+            if (android.os.Build.VERSION.SDK_INT < 23) {
+                int size = mExtraControllerCallbacks.beginBroadcast();
+                for (int i = size - 1; i >= 0; i--) {
+                    IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
+                    try {
+                        cb.onEvent(event, extras);
+                    } catch (RemoteException e) {
+                    }
+                }
+                mExtraControllerCallbacks.finishBroadcast();
+            }
             MediaSessionCompatApi21.sendSessionEvent(mSessionObj, event, extras);
         }
 
         @Override
         public void release() {
+            mDestroyed = true;
             MediaSessionCompatApi21.release(mSessionObj);
         }
 
@@ -2324,6 +2507,18 @@
 
         @Override
         public void setPlaybackState(PlaybackStateCompat state) {
+            if (android.os.Build.VERSION.SDK_INT < 22) {
+                mPlaybackState = state;
+                int size = mExtraControllerCallbacks.beginBroadcast();
+                for (int i = size - 1; i >= 0; i--) {
+                    IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i);
+                    try {
+                        cb.onPlaybackStateChanged(state);
+                    } catch (RemoteException e) {
+                    }
+                }
+                mExtraControllerCallbacks.finishBroadcast();
+            }
             MediaSessionCompatApi21.setPlaybackState(mSessionObj,
                     state == null ? null : state.getPlaybackState());
         }
@@ -2341,7 +2536,6 @@
 
         @Override
         public void setMediaButtonReceiver(PendingIntent mbr) {
-            mMediaButtonIntent = mbr;
             MediaSessionCompatApi21.setMediaButtonReceiver(mSessionObj, mbr);
         }
 
@@ -2365,13 +2559,31 @@
         @Override
         public void setRatingType(@RatingCompat.Style int type) {
             if (android.os.Build.VERSION.SDK_INT < 22) {
-                // TODO figure out 21 implementation
+                mRatingType = type;
             } else {
                 MediaSessionCompatApi22.setRatingType(mSessionObj, type);
             }
         }
 
         @Override
+        public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) {
+            if (android.os.Build.VERSION.SDK_INT < 25) {
+                // TODO: implement this
+            } else {
+                MediaSessionCompatApi25.setRepeatMode(mSessionObj, repeatMode);
+            }
+        }
+
+        @Override
+        public void setShuffleModeEnabled(boolean enabled) {
+            if (android.os.Build.VERSION.SDK_INT < 25) {
+                // TODO: implement this
+            } else {
+                MediaSessionCompatApi25.setShuffleModeEnabled(mSessionObj, enabled);
+            }
+        }
+
+        @Override
         public void setExtras(Bundle extras) {
             MediaSessionCompatApi21.setExtras(mSessionObj, extras);
         }
@@ -2394,5 +2606,255 @@
                 return MediaSessionCompatApi24.getCallingPackage(mSessionObj);
             }
         }
+
+        ExtraSession getExtraSessionBinder() {
+            if (mExtraSessionBinder == null) {
+                mExtraSessionBinder = new ExtraSession();
+            }
+            return mExtraSessionBinder;
+        }
+
+        class ExtraSession extends IMediaSession.Stub {
+            @Override
+            public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public boolean sendMediaButton(KeyEvent mediaButton) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void registerCallbackListener(IMediaControllerCallback cb) {
+                if (!mDestroyed) {
+                    mExtraControllerCallbacks.register(cb);
+                }
+            }
+
+            @Override
+            public void unregisterCallbackListener(IMediaControllerCallback cb) {
+                mExtraControllerCallbacks.unregister(cb);
+            }
+
+            @Override
+            public String getPackageName() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public String getTag() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public PendingIntent getLaunchPendingIntent() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            @SessionFlags
+            public long getFlags() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public ParcelableVolumeInfo getVolumeAttributes() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void adjustVolume(int direction, int flags, String packageName) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void setVolumeTo(int value, int flags, String packageName) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void prepare() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void prepareFromSearch(String query, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void play() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void playFromSearch(String query, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void skipToQueueItem(long id) {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void pause() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void stop() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void next() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void previous() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void fastForward() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void rewind() throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void seekTo(long pos) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void rate(RatingCompat rating) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void setRepeatMode(int repeatMode) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void setShuffleModeEnabled(boolean enabled) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public void sendCustomAction(String action, Bundle args) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public MediaMetadataCompat getMetadata() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public PlaybackStateCompat getPlaybackState() {
+                return mPlaybackState;
+            }
+
+            @Override
+            public List<QueueItem> getQueue() {
+                // Will not be called.
+                return null;
+            }
+
+            @Override
+            public CharSequence getQueueTitle() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public Bundle getExtras() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            @RatingCompat.Style
+            public int getRatingType() {
+                return mRatingType;
+            }
+
+            @Override
+            @PlaybackStateCompat.RepeatMode
+            public int getRepeatMode() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public boolean isShuffleModeEnabled() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
+            public boolean isTransportControlEnabled() {
+                // Will not be called.
+                throw new AssertionError();
+            }
+        }
     }
 }
diff --git a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
index f23d0fa..569e2c9 100644
--- a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -16,6 +16,8 @@
 package android.support.v4.media.session;
 
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -32,8 +34,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Playback state for a {@link MediaSessionCompat}. This includes a state like
  * {@link PlaybackStateCompat#STATE_PLAYING}, the current playback position,
@@ -49,7 +49,8 @@
             ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
             ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
             ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
-            ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
+            ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
+            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE_ENABLED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Actions {}
 
@@ -189,6 +190,20 @@
     public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
 
     /**
+     * Indicates this session supports the set repeat mode command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SET_REPEAT_MODE = 1 << 18;
+
+    /**
+     * Indicates this session supports the set shuffle mode enabled command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19;
+
+    /**
      * @hide
      */
     @RestrictTo(GROUP_ID)
@@ -300,6 +315,31 @@
      */
     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
 
+    /**
+     * @hide
+     */
+    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RepeatMode {}
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback will be stopped at the end of the playing media list.
+     */
+    public static final int REPEAT_MODE_NONE = 0;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback of the current playing media item will be repeated.
+     */
+    public static final int REPEAT_MODE_ONE = 1;
+
+    /**
+     * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode}
+     * to indicate that the playback of the playing media list will be repeated.
+     */
+    public static final int REPEAT_MODE_ALL = 2;
+
     // KeyEvent constants only available on API 11+
     private static final int KEYCODE_MEDIA_PAUSE = 127;
     private static final int KEYCODE_MEDIA_PLAY = 126;
@@ -490,6 +530,8 @@
      * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li>
      * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li>
      * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li>
+     * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li>
+     * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
      * </ul>
      */
     @Actions
@@ -1000,6 +1042,8 @@
          * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li>
          * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li>
          * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li>
+         * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li>
+         * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
          * </ul>
          *
          * @return this
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
index 7dae7dc..4abf568 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorCallback.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.KeyEvent;
 
+@RequiresApi(18)
+@TargetApi(18)
 interface TransportMediatorCallback {
     public void handleKey(KeyEvent key);
     public void handleAudioFocusChange(int focusChange);
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
index be3555e..aebf7ca 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/TransportMediatorJellybeanMR2.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -23,11 +24,14 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.RemoteControlClient;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewTreeObserver;
 
+@RequiresApi(18)
+@TargetApi(18)
 class TransportMediatorJellybeanMR2 {
     final Context mContext;
     final AudioManager mAudioManager;
diff --git a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java b/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
index 693eb39..3f323a1 100644
--- a/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
+++ b/media-compat/jellybean-mr2/android/support/v4/media/session/MediaSessionCompatApi18.java
@@ -15,14 +15,18 @@
  */
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.RemoteControlClient;
-import android.util.Log;
 import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
 
+@RequiresApi(18)
+@TargetApi(18)
 class MediaSessionCompatApi18 {
     private static final String TAG = "MediaSessionCompatApi18";
 
diff --git a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
index b15c15e..5efdc58 100644
--- a/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
+++ b/media-compat/kitkat/android/support/v4/media/RatingCompatKitkat.java
@@ -16,8 +16,12 @@
 
 package android.support.v4.media;
 
+import android.annotation.TargetApi;
 import android.media.Rating;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(19)
+@TargetApi(19)
 class RatingCompatKitkat {
     public static Object newUnratedRating(int ratingStyle) {
         return Rating.newUnratedRating(ratingStyle);
diff --git a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java b/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
index 43c22af..94b446b 100644
--- a/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
+++ b/media-compat/kitkat/android/support/v4/media/session/MediaSessionCompatApi19.java
@@ -15,12 +15,16 @@
  */
 package android.support.v4.media.session;
 
+import android.annotation.TargetApi;
 import android.media.MediaMetadataEditor;
 import android.media.MediaMetadataRetriever;
 import android.media.Rating;
 import android.media.RemoteControlClient;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(19)
+@TargetApi(19)
 class MediaSessionCompatApi19 {
     /***** PlaybackState actions *****/
     private static final long ACTION_SET_RATING = 1 << 7;
diff --git a/percent/Android.mk b/percent/Android.mk
index ecec417..b569224 100644
--- a/percent/Android.mk
+++ b/percent/Android.mk
@@ -24,8 +24,7 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-percent
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := android-support-v4
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
index 3763d2f..679ffbe 100644
--- a/percent/src/android/support/percent/PercentFrameLayout.java
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -16,8 +16,10 @@
 
 package android.support.percent;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -144,7 +146,10 @@
             gravity = source.gravity;
         }
 
+        @RequiresApi(19)
+        @TargetApi(19)
         public LayoutParams(LayoutParams source) {
+            // The copy constructor used here is only supported on API 19+.
             this((FrameLayout.LayoutParams) source);
             mPercentLayoutInfo = source.mPercentLayoutInfo;
         }
diff --git a/percent/tests/java/android/support/percent/PercentDynamicLayoutTest.java b/percent/tests/java/android/support/percent/PercentDynamicLayoutTest.java
index 08a96bc..ed988a8 100755
--- a/percent/tests/java/android/support/percent/PercentDynamicLayoutTest.java
+++ b/percent/tests/java/android/support/percent/PercentDynamicLayoutTest.java
@@ -15,26 +15,28 @@
  */
 package android.support.percent;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.hamcrest.CoreMatchers.allOf;
+
 import android.support.annotation.LayoutRes;
 import android.support.percent.test.R;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 import android.view.View;
 import android.view.ViewStub;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Test;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.core.AllOf.allOf;
-
 /**
  * Test cases to verify that percent layouts properly account for their own paddings.
  */
@@ -45,17 +47,13 @@
         super(PercentDynamicLayoutActivity.class);
     }
 
+    @UiThreadTest
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         // Now that the test is done, replace the activity content view with ViewStub so
         // that it's ready to be replaced for the next test.
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final PercentDynamicLayoutActivity activity = mActivityTestRule.getActivity();
-                activity.setContentView(R.layout.percent_dynamic_layout);
-            }
-        });
+        final PercentDynamicLayoutActivity activity = mActivityTestRule.getActivity();
+        activity.setContentView(R.layout.percent_dynamic_layout);
     }
 
     /**
diff --git a/percent/tests/java/android/support/percent/PercentFrameTest.java b/percent/tests/java/android/support/percent/PercentFrameTest.java
index 8b2fe8b..7268326 100644
--- a/percent/tests/java/android/support/percent/PercentFrameTest.java
+++ b/percent/tests/java/android/support/percent/PercentFrameTest.java
@@ -15,22 +15,19 @@
  */
 package android.support.percent;
 
-import android.os.Build;
-import android.support.percent.test.R;
-import android.support.v4.view.ViewCompat;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import android.view.View;
-import junit.framework.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
 import static android.support.percent.LayoutDirectionActions.setLayoutDirection;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
+import android.os.Build;
+import android.support.percent.test.R;
+import android.support.test.filters.SmallTest;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+
 @SmallTest
 public class PercentFrameTest extends BaseInstrumentationTestCase<TestFrameActivity> {
     private PercentFrameLayout mPercentFrameLayout;
diff --git a/percent/tests/java/android/support/percent/PercentRelativeRtlTest.java b/percent/tests/java/android/support/percent/PercentRelativeRtlTest.java
index e688612..81ff253 100644
--- a/percent/tests/java/android/support/percent/PercentRelativeRtlTest.java
+++ b/percent/tests/java/android/support/percent/PercentRelativeRtlTest.java
@@ -15,19 +15,21 @@
  */
 package android.support.percent;
 
-import android.os.Build;
-import android.support.percent.test.R;
-import android.support.v4.view.ViewCompat;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import org.junit.Before;
-import org.junit.Test;
-
 import static android.support.percent.LayoutDirectionActions.setLayoutDirection;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assume.*;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.os.Build;
+import android.support.percent.test.R;
+import android.support.test.filters.SmallTest;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * The arrangement of child views in the layout class in the default LTR (left-to-right) direction
  * is as follows:
diff --git a/percent/tests/java/android/support/percent/PercentRelativeTest.java b/percent/tests/java/android/support/percent/PercentRelativeTest.java
index eaa38f6..15ac278 100644
--- a/percent/tests/java/android/support/percent/PercentRelativeTest.java
+++ b/percent/tests/java/android/support/percent/PercentRelativeTest.java
@@ -16,9 +16,9 @@
 package android.support.percent;
 
 import android.support.percent.test.R;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 import android.view.View;
+
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
index 85b0817..0e0a9d7 100644
--- a/recommendation/Android.mk
+++ b/recommendation/Android.mk
@@ -1,18 +1,37 @@
-LOCAL_PATH:= $(call my-dir)
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must include it with
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-recommendation \
+#       android-support-v4
+#
 include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_SDK_VERSION := 21
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-
-LOCAL_JAVA_LIBRARIES := \
-    android-support-v4
-
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-recommendation
-
+LOCAL_SDK_VERSION := 21
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-v4 \
+    android-support-annotations
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # ===========================================================
@@ -38,7 +57,7 @@
 LOCAL_IS_HOST_MODULE := false
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
 
-LOCAL_JAVA_LIBRARIES := $(recommendation.docs.java_libraries)
+LOCAL_SHARED_ANDROID_LIBRARIES := $(recommendation.docs.java_libraries)
 
 LOCAL_DROIDDOC_OPTIONS := \
     -offlinemode \
diff --git a/samples/Support13Demos/Android.mk b/samples/Support13Demos/Android.mk
index 52646b3..a227e10 100644
--- a/samples/Support13Demos/Android.mk
+++ b/samples/Support13Demos/Android.mk
@@ -1,12 +1,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_USE_AAPT2 := true
+
 LOCAL_MODULE_TAGS := samples tests
 
 # Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
+LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v13
 
 LOCAL_PACKAGE_NAME := Support13Demos
 
diff --git a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentNestingStatePagerSupport.java b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentNestingStatePagerSupport.java
index 5863852..36a8144 100644
--- a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentNestingStatePagerSupport.java
+++ b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentNestingStatePagerSupport.java
@@ -15,10 +15,6 @@
  */
 package com.example.android.supportv13.app;
 
-import java.util.ArrayList;
-
-import com.example.android.supportv13.R;
-
 import android.app.ActionBar;
 import android.app.ActionBar.Tab;
 import android.app.Activity;
@@ -29,6 +25,10 @@
 import android.support.v13.app.FragmentStatePagerAdapter;
 import android.support.v4.view.ViewPager;
 
+import com.example.android.supportv13.R;
+
+import java.util.ArrayList;
+
 //BEGIN_INCLUDE(complete)
 public class FragmentNestingStatePagerSupport extends Activity {
     ViewPager mViewPager;
@@ -101,7 +101,7 @@
             mActionBar = activity.getActionBar();
             mViewPager = pager;
             mViewPager.setAdapter(this);
-            mViewPager.setOnPageChangeListener(this);
+            mViewPager.addOnPageChangeListener(this);
         }
 
         public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
diff --git a/samples/Support4Demos/Android.mk b/samples/Support4Demos/Android.mk
index eefbdab..0ba19b0 100644
--- a/samples/Support4Demos/Android.mk
+++ b/samples/Support4Demos/Android.mk
@@ -1,12 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := samples tests
 
 # Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v4
 
 LOCAL_PACKAGE_NAME := Support4Demos
 
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
index 5893521..478cb7d 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentCustomAnimationSupport.java
@@ -16,20 +16,21 @@
 
 package com.example.android.supportv4.app;
 
-import com.example.android.supportv4.R;
-
+import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentTransaction;
-
-import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
+import com.example.android.supportv4.R;
+
 public class FragmentCustomAnimationSupport extends FragmentActivity {
     int mStackLevel = 1;
 
@@ -39,7 +40,7 @@
         setContentView(R.layout.fragment_stack);
 
         // Watch for button clicks.
-        Button button = (Button)findViewById(R.id.new_fragment);
+        Button button = (Button) findViewById(R.id.new_fragment);
         button.setOnClickListener(new OnClickListener() {
             public void onClick(View v) {
                 addFragmentToStack();
@@ -119,8 +120,9 @@
                 Bundle savedInstanceState) {
             View v = inflater.inflate(R.layout.hello_world, container, false);
             View tv = v.findViewById(R.id.text);
-            ((TextView)tv).setText("Fragment #" + mNum);
-            tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
+            ((TextView) tv).setText("Fragment #" + mNum);
+            ViewCompat.setBackground(tv,
+                    ContextCompat.getDrawable(getContext(), android.R.drawable.gallery_thumb));
             return v;
         }
     }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
index 337f2c1..4115d5e 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackSupport.java
@@ -16,21 +16,22 @@
 
 package com.example.android.supportv4.app;
 
-import com.example.android.supportv4.R;
-
+import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
-
-import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.TextView;
 
+import com.example.android.supportv4.R;
+
 public class FragmentStackSupport extends FragmentActivity {
     int mStackLevel = 1;
 
@@ -40,13 +41,13 @@
         setContentView(R.layout.fragment_stack);
 
         // Watch for button clicks.
-        Button button = (Button)findViewById(R.id.new_fragment);
+        Button button = (Button) findViewById(R.id.new_fragment);
         button.setOnClickListener(new OnClickListener() {
             public void onClick(View v) {
                 addFragmentToStack();
             }
         });
-        button = (Button)findViewById(R.id.home);
+        button = (Button) findViewById(R.id.home);
         button.setOnClickListener(new OnClickListener() {
             public void onClick(View v) {
                 // If there is a back stack, pop it all.
@@ -128,8 +129,9 @@
                 Bundle savedInstanceState) {
             View v = inflater.inflate(R.layout.hello_world, container, false);
             View tv = v.findViewById(R.id.text);
-            ((TextView)tv).setText("Fragment #" + mNum);
-            tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
+            ((TextView) tv).setText("Fragment #" + mNum);
+            ViewCompat.setBackground(tv,
+                    ContextCompat.getDrawable(getContext(), android.R.drawable.gallery_thumb));
             return v;
         }
     }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
index d858fae..d9689d2 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/app/LoaderCustomSupport.java
@@ -32,6 +32,7 @@
 import android.support.v4.app.ListFragment;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.content.IntentCompat;
 import android.support.v4.content.Loader;
 import android.support.v4.content.pm.ActivityInfoCompat;
@@ -116,8 +117,8 @@
                 return mIcon;
             }
 
-            return mLoader.getContext().getResources().getDrawable(
-                    android.R.drawable.sym_def_app_icon);
+            return ContextCompat.getDrawable(
+                    mLoader.getContext(), android.R.drawable.sym_def_app_icon);
         }
 
         @Override public String toString() {
@@ -230,8 +231,8 @@
         @Override public List<AppEntry> loadInBackground() {
             // Retrieve all known applications.
             List<ApplicationInfo> apps = mPm.getInstalledApplications(
-                    PackageManager.GET_UNINSTALLED_PACKAGES |
-                    PackageManager.GET_DISABLED_COMPONENTS);
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
             if (apps == null) {
                 apps = new ArrayList<ApplicationInfo>();
             }
@@ -240,7 +241,7 @@
 
             // Create corresponding array of entries and load their labels.
             List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
-            for (int i=0; i<apps.size(); i++) {
+            for (int i = 0; i < apps.size(); i++) {
                 AppEntry entry = new AppEntry(this, apps.get(i));
                 entry.loadLabel(context);
                 entries.add(entry);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
index 765dc88..9fac4ab 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.session.MediaControllerCompat;
 import android.util.Log;
@@ -277,8 +278,9 @@
             holder.mTitleView.setText(item.getDescription().getTitle());
             holder.mDescriptionView.setText(item.getDescription().getDescription());
             if (item.isPlayable()) {
-                holder.mImageView.setImageDrawable(getContext().getResources()
-                        .getDrawable(R.drawable.ic_play_arrow_white_24dp));
+
+                holder.mImageView.setImageDrawable(ContextCompat.getDrawable(
+                        getContext(), R.drawable.ic_play_arrow_white_24dp));
                 holder.mImageView.setVisibility(View.VISIBLE);
             } else {
                 holder.mImageView.setVisibility(View.GONE);
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
index 0702e2b..3d327ff 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueAdapter.java
@@ -16,8 +16,8 @@
 
 package com.example.android.supportv4.media;
 
-import com.example.android.supportv4.R;
 import android.app.Activity;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -26,6 +26,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.example.android.supportv4.R;
+
 import java.util.ArrayList;
 
 /**
@@ -74,10 +76,10 @@
         // If the itemId matches the active Id then use a different icon
         if (mActiveQueueItemId == item.getQueueId()) {
             holder.mImageView.setImageDrawable(
-                    getContext().getResources().getDrawable(R.drawable.ic_equalizer_white_24dp));
+                    ContextCompat.getDrawable(getContext(), R.drawable.ic_equalizer_white_24dp));
         } else {
             holder.mImageView.setImageDrawable(
-                    getContext().getResources().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+                    ContextCompat.getDrawable(getContext(), R.drawable.ic_play_arrow_white_24dp));
         }
         return convertView;
     }
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
index f66447d..6ec5477 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.media.MediaBrowserCompat;
 import android.support.v4.media.session.MediaControllerCompat;
 import android.support.v4.media.session.MediaSessionCompat;
@@ -236,10 +237,10 @@
 
         if (enablePlay) {
             mPlayPause.setImageDrawable(
-                    getActivity().getResources().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+                    ContextCompat.getDrawable(getActivity(), R.drawable.ic_play_arrow_white_24dp));
         } else {
             mPlayPause.setImageDrawable(
-                    getActivity().getResources().getDrawable(R.drawable.ic_pause_white_24dp));
+                    ContextCompat.getDrawable(getActivity(), R.drawable.ic_pause_white_24dp));
         }
 
         mSkipPrevious.setEnabled((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java b/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
index c559cf6..1ca8840 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/view/CheckableFrameLayout.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.ColorDrawable;
+import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.widget.Checkable;
 import android.widget.FrameLayout;
@@ -35,7 +36,7 @@
 
     public void setChecked(boolean checked) {
         mChecked = checked;
-        setBackgroundDrawable(checked ? new ColorDrawable(0xff0000a0) : null);
+        ViewCompat.setBackground(this, checked ? new ColorDrawable(0xff0000a0) : null);
     }
 
     public boolean isChecked() {
diff --git a/samples/Support7Demos/AndroidManifest.xml b/samples/Support7Demos/AndroidManifest.xml
index aac5a6d..0e9005a 100644
--- a/samples/Support7Demos/AndroidManifest.xml
+++ b/samples/Support7Demos/AndroidManifest.xml
@@ -370,6 +370,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".widget.PagerRecyclerViewActivity"
+                  android:label="@string/pager_recycler_view"
+                  android:theme="@style/Theme.AppCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".widget.AnimatedRecyclerView"
                   android:label="@string/animated_recycler_view"
                   android:theme="@style/Theme.AppCompat">
diff --git a/samples/Support7Demos/res/menu/sample_media_router_menu.xml b/samples/Support7Demos/res/menu/sample_media_router_menu.xml
index c93111f..8057fa8 100644
--- a/samples/Support7Demos/res/menu/sample_media_router_menu.xml
+++ b/samples/Support7Demos/res/menu/sample_media_router_menu.xml
@@ -19,6 +19,5 @@
     <item android:id="@+id/media_route_menu_item"
         android:title="@string/media_route_menu_title"
         app:showAsAction="always"
-        app:actionProviderClass=
-        "com.example.android.supportv7.media.SampleMediaRouterActivity$MyMediaRouteActionProvider"/>
+        app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/>
 </menu>
diff --git a/samples/Support7Demos/res/values/strings.xml b/samples/Support7Demos/res/values/strings.xml
index afb37b0..10f6761 100644
--- a/samples/Support7Demos/res/values/strings.xml
+++ b/samples/Support7Demos/res/values/strings.xml
@@ -132,6 +132,7 @@
     <string name="sample_media_route_activity_presentation">Local Playback on Presentation Display</string>
 
     <string name="recycler_view">RecyclerView/RecyclerViewActivity</string>
+    <string name="pager_recycler_view">RecyclerView/PagerRecyclerViewActivity</string>
     <string name="animated_recycler_view">RecyclerView/Animated RecyclerView</string>
     <string name="nested_recycler_view">RecyclerView/Nested RecyclerView</string>
     <string name="linear_layout_manager">RecyclerView/Linear Layout Manager</string>
@@ -143,6 +144,7 @@
     <string name="checkbox_reverse">Rev.</string>
     <string name="checkbox_layout_dir">Layout Dir</string>
     <string name="checkbox_stack_from_end">Stack From End</string>
+    <string name="checkbox_snap">Snap</string>
     <string name="enableAnimations">Animate</string>
     <string name="enablePredictiveAnimations">Predictive</string>
     <string name="enableInPlaceChange">In Place Change</string>
diff --git a/samples/Support7Demos/res/values/styles.xml b/samples/Support7Demos/res/values/styles.xml
index 250b4bf..a1ec841 100644
--- a/samples/Support7Demos/res/values/styles.xml
+++ b/samples/Support7Demos/res/values/styles.xml
@@ -32,34 +32,16 @@
     <style name="Theme.SampleMediaRouter" parent="Theme.AppCompat">
         <item name="colorPrimary">#fff44336</item>
         <item name="colorPrimaryDark">#d32f2f</item>
-        <item name="alertDialogTheme">@style/Theme.SampleMediaRouter.Dialog.Alert</item>
     </style>
 
     <style name="Theme.SampleMediaRouter.Light" parent="Theme.AppCompat.Light">
         <item name="colorPrimary">#ffff9800</item>
         <item name="colorPrimaryDark">#f57c00</item>
-        <item name="alertDialogTheme">@style/Theme.SampleMediaRouter.Light.Dialog.Alert</item>
     </style>
 
     <style name="Theme.SampleMediaRouter.Light.DarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
         <item name="colorPrimary">#ff2196f3</item>
         <item name="colorPrimaryDark">#1976d2</item>
-        <item name="alertDialogTheme">@style/Theme.SampleMediaRouter.Light.DarkActionBar.Dialog.Alert</item>
-    </style>
-
-    <style name="Theme.SampleMediaRouter.Dialog.Alert" parent="Theme.AppCompat.Dialog.Alert">
-        <item name="colorPrimary">#fff44336</item>
-        <item name="colorPrimaryDark">#d32f2f</item>
-    </style>
-
-    <style name="Theme.SampleMediaRouter.Light.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert">
-        <item name="colorPrimary">#ffff9800</item>
-        <item name="colorPrimaryDark">#f57c00</item>
-    </style>
-
-    <style name="Theme.SampleMediaRouter.Light.DarkActionBar.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog.Alert">
-        <item name="colorPrimary">#ff2196f3</item>
-        <item name="colorPrimaryDark">#1976d2</item>
     </style>
 
     <style name="SortedListItem" parent="@android:style/TextAppearance.Medium">
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
index 375d4a0..bae5362 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java
@@ -28,6 +28,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
@@ -35,10 +36,12 @@
 import android.support.v7.app.MediaRouteControllerDialog;
 import android.support.v7.app.MediaRouteControllerDialogFragment;
 import android.support.v7.app.MediaRouteDialogFactory;
+import android.support.v7.app.MediaRouteDiscoveryFragment;
 import android.support.v7.media.MediaControlIntent;
 import android.support.v7.media.MediaItemStatus;
 import android.support.v7.media.MediaRouteSelector;
 import android.support.v7.media.MediaRouter;
+import android.support.v7.media.MediaRouter.Callback;
 import android.support.v7.media.MediaRouter.ProviderInfo;
 import android.support.v7.media.MediaRouter.RouteInfo;
 import android.util.Log;
@@ -212,7 +215,23 @@
                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
                 .addControlCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE)
                 .build();
-        mMediaRouter.addCallback(mSelector, mMediaRouterCB);
+
+        // Add a fragment to take care of media route discovery.
+        // This fragment automatically adds or removes a callback whenever the activity
+        // is started or stopped.
+        FragmentManager fm = getSupportFragmentManager();
+        DiscoveryFragment fragment = (DiscoveryFragment) fm.findFragmentByTag(
+                DISCOVERY_FRAGMENT_TAG);
+        if (fragment == null) {
+            fragment = new DiscoveryFragment(mMediaRouterCB);
+            fragment.setRouteSelector(mSelector);
+            fm.beginTransaction()
+                    .add(fragment, DISCOVERY_FRAGMENT_TAG)
+                    .commit();
+        } else {
+            fragment.setCallback(mMediaRouterCB);
+            fragment.setRouteSelector(mSelector);
+        }
 
         // Populate an array adapter with streaming media items.
         String[] mediaNames = getResources().getStringArray(R.array.media_names);
@@ -500,8 +519,8 @@
         getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
 
         MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
-        MyMediaRouteActionProvider mediaRouteActionProvider =
-                (MyMediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
+        MediaRouteActionProvider mediaRouteActionProvider =
+                (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
         mediaRouteActionProvider.setRouteSelector(mSelector);
         mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() {
             @Override
@@ -602,6 +621,41 @@
         return null;
     }
 
+    /**
+     * Media route discovery fragment.
+     */
+    public static final class DiscoveryFragment extends MediaRouteDiscoveryFragment {
+        private static final String TAG = "DiscoveryFragment";
+        private Callback mCallback;
+
+        public DiscoveryFragment() {
+            mCallback = null;
+        }
+
+        public DiscoveryFragment(Callback cb) {
+            mCallback = cb;
+        }
+
+        public void setCallback(Callback cb) {
+            mCallback = cb;
+        }
+
+        @Override
+        public Callback onCreateCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public int onPrepareCallbackFlags() {
+            // Add the CALLBACK_FLAG_UNFILTERED_EVENTS flag to ensure that we will
+            // observe and log all route events including those that are for routes
+            // that do not match our selector.  This is only for demonstration purposes
+            // and should not be needed by most applications.
+            return super.onPrepareCallbackFlags()
+                    | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS;
+        }
+    }
+
     private static final class MediaItem {
         public final String mName;
         public final Uri mUri;
@@ -703,17 +757,6 @@
     public static class LightWithDarkActionBar extends SampleMediaRouterActivity {
     }
 
-    public static class MyMediaRouteActionProvider extends MediaRouteActionProvider {
-        public MyMediaRouteActionProvider(Context context) {
-            super(context);
-        }
-
-        @Override
-        public boolean isVisible() {
-            return true;
-        }
-    }
-
     public static class ControllerDialogFragment extends MediaRouteControllerDialogFragment {
         private MediaRouteControllerDialog mControllerDialog;
         private Player mPlayer;
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
index 8f3871d..b2e7094 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java
@@ -15,9 +15,8 @@
  */
 package com.example.android.supportv7.util;
 
-import com.example.android.supportv7.R;
 import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.support.v7.util.SortedList;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -32,10 +31,12 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
+import com.example.android.supportv7.R;
+
 /**
  * A sample activity that uses {@link SortedList} in combination with RecyclerView.
  */
-public class SortedListActivity extends ActionBarActivity {
+public class SortedListActivity extends AppCompatActivity {
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLinearLayoutManager;
     private SortedListAdapter mAdapter;
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java
index 31d53cc..184beaa 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/LinearLayoutManagerActivity.java
@@ -19,6 +19,7 @@
 import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.DividerItemDecoration;
 import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.LinearSnapHelper;
 import android.support.v7.widget.RecyclerView;
 
 import com.example.android.supportv7.R;
@@ -29,6 +30,8 @@
  */
 public class LinearLayoutManagerActivity extends BaseLayoutManagerActivity<LinearLayoutManager> {
     private DividerItemDecoration mDividerItemDecoration;
+    private LinearSnapHelper mLinearSnapHelper;
+    private boolean mSnapHelperAttached;
 
     @Override
     protected LinearLayoutManager createLayoutManager() {
@@ -39,6 +42,7 @@
     protected void onRecyclerViewInit(RecyclerView recyclerView) {
         mDividerItemDecoration = new DividerItemDecoration(this, mLayoutManager.getOrientation());
         recyclerView.addItemDecoration(mDividerItemDecoration);
+        mLinearSnapHelper = new LinearSnapHelper();
     }
 
     @Override
@@ -94,6 +98,18 @@
                     public void onChange(boolean newValue) {
                         mLayoutManager.setStackFromEnd(newValue);
                     }
+                },
+                new ConfigToggle(this, R.string.checkbox_snap) {
+                    @Override
+                    public boolean isChecked() {
+                        return mSnapHelperAttached;
+                    }
+
+                    @Override
+                    public void onChange(boolean newValue) {
+                        mLinearSnapHelper.attachToRecyclerView(newValue ? mRecyclerView : null);
+                        mSnapHelperAttached = newValue;
+                    }
                 }
         };
     }
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/PagerRecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/PagerRecyclerViewActivity.java
new file mode 100644
index 0000000..2116aae
--- /dev/null
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/PagerRecyclerViewActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.supportv7.widget;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.PagerSnapHelper;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+
+import com.example.android.supportv7.Cheeses;
+import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
+
+/**
+ * Example activity that uses LinearLayoutManager, RecyclerView, and PagerSnapHelper.
+ */
+public class PagerRecyclerViewActivity extends AppCompatActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final RecyclerView rv = new RecyclerView(this);
+        final LinearLayoutManager manager =
+                new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
+        rv.setLayoutManager(manager);
+        rv.setHasFixedSize(true);
+        rv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        rv.setAdapter(new SimpleStringAdapter(this, Cheeses.sCheeseStrings) {
+            @Override
+            public RecyclerView.LayoutParams getLayoutParams() {
+                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT);
+            }
+        });
+        PagerSnapHelper snapHelper = new PagerSnapHelper();
+        snapHelper.attachToRecyclerView(rv);
+        setContentView(rv);
+    }
+}
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java
index 04161ea..f9c4ae4 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/adapter/SimpleStringAdapter.java
@@ -27,6 +27,9 @@
 import java.util.Collections;
 import java.util.List;
 
+/**
+ * A simple RecyclerView adapter that displays every string passed in a constructor as an item.
+ */
 public class SimpleStringAdapter extends RecyclerView.Adapter<SimpleStringAdapter.ViewHolder> {
 
     private int mBackground;
@@ -78,13 +81,7 @@
         h.mTextView.setPadding(20, 0, 20, 0);
         h.mTextView.setFocusable(true);
         h.mTextView.setBackgroundResource(mBackground);
-        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-        lp.leftMargin = 10;
-        lp.rightMargin = 5;
-        lp.topMargin = 20;
-        lp.bottomMargin = 15;
+        RecyclerView.LayoutParams lp = getLayoutParams();
         h.mTextView.setLayoutParams(lp);
         return h;
     }
@@ -97,6 +94,23 @@
         holder.mTextView.setBackgroundColor(getBackgroundColor(position));
     }
 
+
+    /**
+     * Returns LayoutParams to be used for each item in this adapter. It can be overridden
+     * to provide different LayoutParams.
+     * @return LayoutParams to be used for each item in this adapter.
+     */
+    public RecyclerView.LayoutParams getLayoutParams() {
+        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT);
+        lp.leftMargin = 10;
+        lp.rightMargin = 5;
+        lp.topMargin = 20;
+        lp.bottomMargin = 15;
+        return lp;
+    }
+
     private int getBackgroundColor(int position) {
         switch (position % 4) {
             case 0: return Color.BLACK;
diff --git a/samples/SupportAppNavigation/Android.mk b/samples/SupportAppNavigation/Android.mk
index 8e6ec46..ea9322f 100644
--- a/samples/SupportAppNavigation/Android.mk
+++ b/samples/SupportAppNavigation/Android.mk
@@ -1,13 +1,15 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_USE_AAPT2 := true
+
 LOCAL_MODULE_TAGS := samples tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := SupportAppNavigation
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_ANDROID_LIBRARIES += android-support-v4
 
 LOCAL_SDK_VERSION := current
 
diff --git a/samples/SupportDesignDemos/Android.mk b/samples/SupportDesignDemos/Android.mk
index 8ae120e..4c74ad2 100644
--- a/samples/SupportDesignDemos/Android.mk
+++ b/samples/SupportDesignDemos/Android.mk
@@ -18,30 +18,19 @@
 # We need to add some special AAPT flags to generate R classes
 # for resources that are included from the libraries.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_PACKAGE_NAME := SupportDesignDemos
 LOCAL_MODULE_TAGS := samples
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 7
 LOCAL_DEX_PREOPT := false
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
         android-support-v4 \
         android-support-v7-appcompat \
         android-support-v7-recyclerview \
         android-support-transition \
         android-support-design
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/v7/appcompat/res \
-        frameworks/support/v7/recyclerview/res \
-        frameworks/support/transition/res \
-        frameworks/support/design/res
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v7.appcompat \
-        --extra-packages android.support.v7.recyclerview \
-        --extra-packages android.support.transition \
-        --extra-packages android.support.design \
-        --no-version-vectors
+LOCAL_AAPT_FLAGS := --no-version-vectors
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 include $(BUILD_PACKAGE)
diff --git a/samples/SupportDesignDemos/AndroidManifest.xml b/samples/SupportDesignDemos/AndroidManifest.xml
index 13071ea..29b042c 100644
--- a/samples/SupportDesignDemos/AndroidManifest.xml
+++ b/samples/SupportDesignDemos/AndroidManifest.xml
@@ -128,6 +128,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".widget.CustomSnackbarUsage"
+                  android:label="@string/design_snackbar_custom">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.design.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".widget.AppBarLayoutToolbarScrollTabsScroll"
                   android:label="@string/design_appbar_toolbar_scroll_tabs_scroll">
             <intent-filter>
diff --git a/samples/SupportDesignDemos/res/drawable-hdpi/ic_account_circle_white_48dp.png b/samples/SupportDesignDemos/res/drawable-hdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..bfd4632
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-hdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png b/samples/SupportDesignDemos/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..c9dd4c1
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-hdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-mdpi/ic_account_circle_white_48dp.png b/samples/SupportDesignDemos/res/drawable-mdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..246e0c8
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-mdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png b/samples/SupportDesignDemos/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..91702b1
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-mdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xhdpi/ic_account_circle_white_48dp.png b/samples/SupportDesignDemos/res/drawable-xhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..07643f9
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png b/samples/SupportDesignDemos/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..afc7e53
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_account_circle_white_48dp.png b/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..1ac34a7
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png b/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..1c16761
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xxhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png b/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png
new file mode 100644
index 0000000..baa6045
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_account_circle_white_48dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png b/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png
new file mode 100644
index 0000000..983a253
--- /dev/null
+++ b/samples/SupportDesignDemos/res/drawable-xxxhdpi/ic_lightbulb_outline_white_24dp.png
Binary files differ
diff --git a/samples/SupportDesignDemos/res/layout/custom_snackbar_include.xml b/samples/SupportDesignDemos/res/layout/custom_snackbar_include.xml
new file mode 100644
index 0000000..89029e0
--- /dev/null
+++ b/samples/SupportDesignDemos/res/layout/custom_snackbar_include.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+
+<com.example.android.support.design.widget.CustomSnackbarMainContent
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="8dp">
+
+    <ImageView
+        android:id="@+id/custom_snackbar_image_leading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentLeft="true"
+        android:layout_marginRight="8dp"
+        android:src="@drawable/ic_account_circle_white_48dp" />
+
+    <ImageView
+        android:id="@+id/custom_snackbar_image_trailing"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_marginTop="8dp"
+        android:src="@drawable/ic_lightbulb_outline_white_24dp" />
+
+    <TextView
+        android:id="@+id/custom_snackbar_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/custom_snackbar_image_leading"
+        android:layout_toLeftOf="@id/custom_snackbar_image_trailing"
+        android:layout_marginTop="4dp"
+        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+        android:textStyle="bold"
+        android:singleLine="true" />
+
+    <TextView
+        android:id="@+id/custom_snackbar_subtitle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/custom_snackbar_image_leading"
+        android:layout_toLeftOf="@id/custom_snackbar_image_trailing"
+        android:layout_below="@id/custom_snackbar_title"
+        android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
+        android:singleLine="true" />
+
+</com.example.android.support.design.widget.CustomSnackbarMainContent>
diff --git a/samples/SupportDesignDemos/res/layout/custom_snackbar_with_fab.xml b/samples/SupportDesignDemos/res/layout/custom_snackbar_with_fab.xml
new file mode 100644
index 0000000..1b92d85
--- /dev/null
+++ b/samples/SupportDesignDemos/res/layout/custom_snackbar_with_fab.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<android.support.design.widget.CoordinatorLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/content_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <LinearLayout android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:padding="16dp"
+                  android:orientation="vertical">
+
+        <Button
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/custom_snackbar_show"
+                android:onClick="show"/>
+
+    </LinearLayout>
+
+    <android.support.design.widget.FloatingActionButton
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="bottom|end"
+            android:layout_margin="16dp"
+            android:src="@drawable/ic_add"
+            android:clickable="true"/>
+
+</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/samples/SupportDesignDemos/res/layout/design_navigation_header.xml b/samples/SupportDesignDemos/res/layout/design_navigation_header.xml
index 1ab584d..2f691c5 100644
--- a/samples/SupportDesignDemos/res/layout/design_navigation_header.xml
+++ b/samples/SupportDesignDemos/res/layout/design_navigation_header.xml
@@ -15,6 +15,7 @@
      limitations under the License.
 -->
 <FrameLayout
+    android:id="@+id/header"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
@@ -37,4 +38,11 @@
         android:text="@string/navigation_header"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"/>
 
+    <android.support.v7.widget.SwitchCompat
+        android:id="@+id/value"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end|bottom"
+        android:layout_margin="16dp"/>
+
 </FrameLayout>
diff --git a/samples/SupportDesignDemos/res/layout/include_bottom_sheet.xml b/samples/SupportDesignDemos/res/layout/include_bottom_sheet.xml
index ab00ece..c4dfcab 100644
--- a/samples/SupportDesignDemos/res/layout/include_bottom_sheet.xml
+++ b/samples/SupportDesignDemos/res/layout/include_bottom_sheet.xml
@@ -15,8 +15,7 @@
      limitations under the License.
 -->
 <merge xmlns:tools="http://schemas.android.com/tools"
-       xmlns:android="http://schemas.android.com/apk/res/android"
-       tools:showIn="@layout/design_bottom_sheet">
+       xmlns:android="http://schemas.android.com/apk/res/android">
 
     <TextView
             android:layout_width="match_parent"
diff --git a/documents-archive/AndroidManifest.xml b/samples/SupportDesignDemos/res/values-sw600dp/dimens.xml
similarity index 70%
copy from documents-archive/AndroidManifest.xml
copy to samples/SupportDesignDemos/res/values-sw600dp/dimens.xml
index 2cd0f7a..67765c5 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/samples/SupportDesignDemos/res/values-sw600dp/dimens.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<resources>
+    <dimen name="custom_snackbar_max_width">540dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportDesignDemos/res/values/dimens.xml b/samples/SupportDesignDemos/res/values/dimens.xml
index dc3a5f6..c8a5ea9 100644
--- a/samples/SupportDesignDemos/res/values/dimens.xml
+++ b/samples/SupportDesignDemos/res/values/dimens.xml
@@ -18,4 +18,6 @@
     <dimen name="bottom_sheet_horizontal_margin">0dp</dimen>
     <dimen name="bottom_sheet_elevation">16dp</dimen>
     <dimen name="bottom_sheet_peek_height">128dp</dimen>
+
+    <dimen name="custom_snackbar_max_width">-1px</dimen>
 </resources>
diff --git a/samples/SupportDesignDemos/res/values/strings.xml b/samples/SupportDesignDemos/res/values/strings.xml
index 95900ca..10ffd86 100644
--- a/samples/SupportDesignDemos/res/values/strings.xml
+++ b/samples/SupportDesignDemos/res/values/strings.xml
@@ -84,6 +84,8 @@
     <string name="snackbar_show_short_action">Show (short message + action)</string>
     <string name="snackbar_show_long_action">Show (long message + action)</string>
     <string name="snackbar_show_long_long_action">Show (long message + long action)</string>
+    <string name="design_snackbar_custom">Snackbar/Custom</string>
+    <string name="custom_snackbar_show">Custom snackbar</string>
 
     <string name="design_appbar_toolbar_scroll_tabs_scroll">AppBar/Toolbar Scroll + Tabs Scroll</string>
     <string name="design_appbar_toolbar_scroll_tabs_scroll_snap">AppBar/Toolbar Scroll + Tabs Scroll + Snap</string>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbar.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbar.java
new file mode 100644
index 0000000..adee5a6
--- /dev/null
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbar.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.support.design.widget;
+
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.design.widget.CoordinatorLayout;
+import android.view.View;
+import android.widget.TextView;
+
+import com.example.android.support.design.R;
+
+/**
+ * Sample code for a custom snackbar that shows two separate text views and two images
+ * in the main content area.
+ */
+public class CustomSnackbar extends BaseTransientBottomBar<CustomSnackbar> {
+    public CustomSnackbar(CoordinatorLayout parent, View content,
+            BaseTransientBottomBar.ContentViewCallback contentViewCallback) {
+        super(parent, content, contentViewCallback);
+    }
+
+    /** Sets the title of this custom snackbar. */
+    public CustomSnackbar setTitle(String title) {
+        TextView titleView = (TextView) getView().findViewById(R.id.custom_snackbar_title);
+        titleView.setText(title);
+        return this;
+    }
+
+    /** Sets the subtitle of this custom snackbar. */
+    public CustomSnackbar setSubtitle(String subtitle) {
+        TextView subtitleView = (TextView) getView().findViewById(R.id.custom_snackbar_subtitle);
+        subtitleView.setText(subtitle);
+        return this;
+    }
+}
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarMainContent.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarMainContent.java
new file mode 100644
index 0000000..c819fa6
--- /dev/null
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarMainContent.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.support.design.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+import com.example.android.support.design.R;
+
+/**
+ * Layout for the custom snackbar that shows two separate text views and two images
+ * in the main content area.
+ */
+public class CustomSnackbarMainContent extends RelativeLayout {
+    private final int mMaxWidth;
+
+    public CustomSnackbarMainContent(Context context) {
+        this(context, null);
+    }
+
+    public CustomSnackbarMainContent(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CustomSnackbarMainContent(Context context, AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mMaxWidth = context.getResources().getDimensionPixelSize(R.dimen.custom_snackbar_max_width);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if ((mMaxWidth > 0) && (getMeasuredWidth() > mMaxWidth)) {
+            super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY),
+                    heightMeasureSpec);
+        }
+    }
+}
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java
new file mode 100644
index 0000000..163cce2
--- /dev/null
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/CustomSnackbarUsage.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.design.widget;
+
+import android.os.Bundle;
+import android.support.design.widget.BaseTransientBottomBar;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.example.android.support.design.R;
+
+/**
+ * This demonstrates custom usage of the snackbar
+ */
+public class CustomSnackbarUsage extends AppCompatActivity {
+
+    private CoordinatorLayout mContentView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.custom_snackbar_with_fab);
+
+        mContentView = (CoordinatorLayout) findViewById(R.id.content_view);
+    }
+
+    /** Shows a custom snackbar with no action. */
+    public void show(View view) {
+        final LayoutInflater inflater = LayoutInflater.from(mContentView.getContext());
+        final CustomSnackbarMainContent content =
+                (CustomSnackbarMainContent) inflater.inflate(
+                        R.layout.custom_snackbar_include, mContentView, false);
+        final BaseTransientBottomBar.ContentViewCallback contentViewCallback =
+                new BaseTransientBottomBar.ContentViewCallback() {
+                    @Override
+                    public void animateContentIn(int delay, int duration) {
+                        ViewCompat.setAlpha(content, 0f);
+                        ViewCompat.animate(content).alpha(1f).setDuration(duration)
+                                .setStartDelay(delay).start();
+                    }
+
+                    @Override
+                    public void animateContentOut(int delay, int duration) {
+                        ViewCompat.setAlpha(content, 1f);
+                        ViewCompat.animate(content).alpha(0f).setDuration(duration)
+                                .setStartDelay(delay).start();
+                    }
+                };
+        new CustomSnackbar(mContentView, content, contentViewCallback).setTitle("Custom title")
+                .setSubtitle("Custom subtitle").show();
+    }
+}
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
index c2ff6c9..85f2152 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarUsage.java
@@ -16,8 +16,6 @@
 
 package com.example.android.support.design.widget;
 
-import com.example.android.support.design.R;
-
 import android.os.Bundle;
 import android.support.design.widget.Snackbar;
 import android.support.v7.app.AppCompatActivity;
@@ -25,8 +23,10 @@
 import android.view.ViewGroup;
 import android.widget.Toast;
 
+import com.example.android.support.design.R;
+
 /**
- * This demonstrates idiomatic usage of the Floating Action Button
+ * This demonstrates idiomatic usage of the snackbar
  */
 public class SnackbarUsage extends AppCompatActivity {
 
diff --git a/samples/SupportLeanbackDemos/Android.mk b/samples/SupportLeanbackDemos/Android.mk
index 53bb778..5a8d110 100644
--- a/samples/SupportLeanbackDemos/Android.mk
+++ b/samples/SupportLeanbackDemos/Android.mk
@@ -18,22 +18,22 @@
 # We need to add some special AAPT flags to generate R classes
 # for resources that are included from the libraries.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_PACKAGE_NAME := SupportLeanbackDemos
 LOCAL_MODULE_TAGS := samples tests
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 17
 LOCAL_DEX_PREOPT := false
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-support-v4 \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+        android-support-compat \
+        android-support-core-ui \
+        android-support-media-compat \
+        android-support-fragment \
         android-support-v7-recyclerview \
-        android-support-v17-leanback
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/v17/leanback/res \
-        frameworks/support/v7/recyclerview/res
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v17.leanback \
-        --extra-packages android.support.v7.recyclerview
+        android-support-v17-leanback \
+        android-support-v17-preference-leanback \
+        android-support-v7-preference \
+        android-support-v14-preference
+
 include $(BUILD_PACKAGE)
diff --git a/samples/SupportLeanbackDemos/AndroidManifest.xml b/samples/SupportLeanbackDemos/AndroidManifest.xml
index 48d638d..a600520 100644
--- a/samples/SupportLeanbackDemos/AndroidManifest.xml
+++ b/samples/SupportLeanbackDemos/AndroidManifest.xml
@@ -6,6 +6,7 @@
 
     <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="23" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <application
         android:label="@string/app_name"
@@ -123,6 +124,10 @@
             android:theme="@style/Theme.Example.Leanback.GuidedStep"
             android:exported="true" />
 
+        <activity android:name=".SettingsActivity"
+                  android:theme="@style/Theme.Example.Leanback.Preferences"
+                  android:exported="true" />
+
         <activity android:name=".OnboardingActivity"
                   android:theme="@style/Theme.Example.Leanback.Onboarding"
                   android:exported="true" />
diff --git a/samples/SupportLeanbackDemos/build.gradle b/samples/SupportLeanbackDemos/build.gradle
index c94d41a..67cfe4d 100644
--- a/samples/SupportLeanbackDemos/build.gradle
+++ b/samples/SupportLeanbackDemos/build.gradle
@@ -2,6 +2,7 @@
 
 dependencies {
     compile project(':support-leanback-v17')
+    compile project(':support-preference-leanback-v17')
 }
 
 android {
diff --git a/samples/SupportLeanbackDemos/generatev4.py b/samples/SupportLeanbackDemos/generatev4.py
index 7c9f0fe..145d022 100755
--- a/samples/SupportLeanbackDemos/generatev4.py
+++ b/samples/SupportLeanbackDemos/generatev4.py
@@ -19,6 +19,7 @@
 import getopt
 
 def write_java_head(tofile, name):
+    tofile.write("// CHECKSTYLE:OFF Generated code\n")
     tofile.write("/* This file is auto-generated from {}.java.  DO NOT MODIFY. */\n\n".format(name))
 
 def replace_xml_head(line, name):
@@ -114,9 +115,13 @@
     line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
     line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
     line = line.replace('DetailsFragment', 'DetailsSupportFragment')
+    line = line.replace('VideoFragment', 'VideoSupportFragment')
+    line = line.replace('PlaybackFragmentGlueHost', 'PlaybackSupportFragmentGlueHost')
     line = line.replace('DetailsActivity', 'DetailsSupportActivity')
     line = line.replace('PlaybackOverlayActivity', 'PlaybackOverlaySupportActivity')
+    line = line.replace('DetailsSupportFragmentVideoHelper', 'DetailsFragmentVideoHelper')
     line = line.replace('SearchActivity', 'SearchSupportActivity')
+    line = line.replace('getRowsFragment', 'getRowsSupportFragment')
     outfile.write(line)
 file.close()
 outfile.close()
@@ -202,7 +207,7 @@
 
 file = open('src/com/example/android/leanback/VerticalGridFragment.java', 'r')
 outfile = open('src/com/example/android/leanback/VerticalGridSupportFragment.java', 'w')
-outfile.write("/* This file is auto-generated from VerticalGridFragment.  DO NOT MODIFY. */\n\n")
+write_java_head(outfile, "VerticalGridFragment")
 for line in file:
     line = line.replace('VerticalGridFragment', 'VerticalGridSupportFragment')
     line = line.replace('DetailsActivity', 'DetailsSupportActivity')
@@ -300,19 +305,10 @@
 write_java_head(outfile, "PlaybackOverlayFragment")
 for line in file:
     line = line.replace('PlaybackOverlayFragment', 'PlaybackOverlaySupportFragment')
-    line = line.replace('PlaybackControlHelper', 'PlaybackControlSupportHelper')
     line = line.replace('PlaybackOverlayActivity', 'PlaybackOverlaySupportActivity')
-    outfile.write(line)
-file.close()
-outfile.close()
-
-file = open('src/com/example/android/leanback/PlaybackControlHelper.java', 'r')
-outfile = open('src/com/example/android/leanback/PlaybackControlSupportHelper.java', 'w')
-write_java_head(outfile, "PlaybackControlHelper")
-for line in file:
-    line = line.replace('PlaybackControlHelper', 'PlaybackControlSupportHelper')
-    line = line.replace('PlaybackControlGlue', 'PlaybackControlSupportGlue')
-    line = line.replace('PlaybackOverlayFragment', 'PlaybackOverlaySupportFragment')
+    line = line.replace('PlaybackFragmentGlueHost', 'PlaybackSupportFragmentGlueHost')
+    line = line.replace('VideoFragment', 'VideoSupportFragment')
+    line = line.replace('PlaybackFragment', 'PlaybackSupportFragment')
     outfile.write(line)
 file.close()
 outfile.close()
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_apps.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_apps.png
new file mode 100755
index 0000000..ed92603
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_apps.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_parental_control.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_parental_control.png
new file mode 100755
index 0000000..d541c6a
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_parental_control.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_time.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_time.png
new file mode 100755
index 0000000..72899ae
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_time.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_3_bar.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_3_bar.png
new file mode 100755
index 0000000..f9abb6c
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_3_bar.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_4_bar.png b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_4_bar.png
new file mode 100755
index 0000000..dcd2088
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable-xhdpi/ic_settings_wifi_4_bar.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/drawable/spiderman.jpg b/samples/SupportLeanbackDemos/res/drawable/spiderman.jpg
new file mode 100644
index 0000000..b68384c
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/drawable/spiderman.jpg
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/layout/settings.xml b/samples/SupportLeanbackDemos/res/layout/settings.xml
new file mode 100644
index 0000000..f4d6bd6
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/layout/settings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+    <fragment
+            android:id="@+id/settingsFragment"
+            android:name="com.example.android.leanback.SettingsFragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"></fragment>
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/SupportLeanbackDemos/res/mipmap-xhdpi/app_banner_sample_app.png b/samples/SupportLeanbackDemos/res/mipmap-xhdpi/app_banner_sample_app.png
new file mode 100644
index 0000000..222c1e5
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/mipmap-xhdpi/app_banner_sample_app.png
Binary files differ
diff --git a/samples/SupportLeanbackDemos/res/values/arrays.xml b/samples/SupportLeanbackDemos/res/values/arrays.xml
index 307f49d..a8179fe 100644
--- a/samples/SupportLeanbackDemos/res/values/arrays.xml
+++ b/samples/SupportLeanbackDemos/res/values/arrays.xml
@@ -30,4 +30,31 @@
         <item>Just override OnboardingFragment and provide the messages and images.</item>
         <item>Customize your own logo image, splash animation and the contents animations.</item>
     </string-array>
+
+    <array name="pref_parent_control_entries">
+        <item>Everyone</item>
+        <item>Low maturity</item>
+        <item>Medium maturity</item>
+        <item>High maturity</item>
+    </array>
+    <array name="pref_parent_control_entries_values">
+        <item>everyone</item>
+        <item>low</item>
+        <item>medium</item>
+        <item>high</item>
+    </array>
+    <array name="pref_parent_control_entries_summaries">
+        <item>This description becomes visible only on focus.</item>
+        <item>This description becomes visible only on focus.</item>
+        <item>This description becomes visible only on focus.</item>
+        <item>This description becomes visible only on focus.</item>
+    </array>
+    <array name="pref_wifi_networks">
+        <item>Wi-Fi network 1</item>
+        <item>Wi-Fi network 2</item>
+        <item>Wi-Fi network 3</item>
+        <item>Wi-Fi network 4</item>
+        <item>Wi-Fi network 5</item>
+        <item>Wi-Fi network 6</item>
+    </array>
 </resources>
diff --git a/samples/SupportLeanbackDemos/res/values/colors.xml b/samples/SupportLeanbackDemos/res/values/colors.xml
index 1fdeb87..1f48473 100644
--- a/samples/SupportLeanbackDemos/res/values/colors.xml
+++ b/samples/SupportLeanbackDemos/res/values/colors.xml
@@ -17,4 +17,5 @@
     <color name="icon_background">#4A4F51</color>
     <color name="icon_alt_background">#2A2F51</color>
     <color name="page_indicator_dot">#80FFFFFF</color>
+    <color name="settings_background">#00695C</color>
 </resources>
diff --git a/samples/SupportLeanbackDemos/res/values/strings.xml b/samples/SupportLeanbackDemos/res/values/strings.xml
index 0a3ead4..ed98cd6 100644
--- a/samples/SupportLeanbackDemos/res/values/strings.xml
+++ b/samples/SupportLeanbackDemos/res/values/strings.xml
@@ -59,6 +59,8 @@
     <string name="detail_presenter_options_description">Choose Presenter for Details</string>
     <string name="legacydetails_off">Use New DetailsPresenter</string>
     <string name="legacydetails_on">Use Legacy DetailsPresenter</string>
+    <string name="settings">Settings</string>
+    <string name="settings_description">Settings and Preferences.</string>
     <string name="onboarding">Onboarding</string>
     <string name="onboarding_description">Show onboarding activity.</string>
     <string name="onboarding_support">Onboarding(support version)</string>
diff --git a/samples/SupportLeanbackDemos/res/values/themes.xml b/samples/SupportLeanbackDemos/res/values/themes.xml
index db9f4c4..458c5c0 100644
--- a/samples/SupportLeanbackDemos/res/values/themes.xml
+++ b/samples/SupportLeanbackDemos/res/values/themes.xml
@@ -37,6 +37,14 @@
     </style>
     <style name="Theme.Example.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep.Half">
     </style>
+    <style name="Theme.Example.Leanback.Preferences">
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Leanback</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <item name="android:colorPrimary">@color/settings_background</item>
+        <item name="android:colorAccent">?attr/defaultBrandColor</item>
+    </style>
     <style name="Theme.Example.Leanback.Onboarding" parent="Theme.Leanback.Onboarding">
         <item name="onboardingLogoStyle">@style/Widget.Example.Leanback.OnboardingLogoStyle</item>
         <item name="onboardingPageIndicatorStyle">@style/Widget.Example.Leanback.OnboardingPageIndicatorStyle</item>
diff --git a/samples/SupportLeanbackDemos/res/xml/prefs.xml b/samples/SupportLeanbackDemos/res/xml/prefs.xml
new file mode 100644
index 0000000..a25411a
--- /dev/null
+++ b/samples/SupportLeanbackDemos/res/xml/prefs.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:app="http://schemas.android.com/apk/res-auto"
+                  android:summary="This is a dummy activity only to show case how to build a settings in an application. Changing configurations in this example doesn't affect anything."
+                  android:title="Settings Example">
+    <PreferenceScreen
+            android:icon="@drawable/ic_settings_wifi_4_bar"
+            android:key="prefs_wifi_screen_key"
+            android:title="Wi-Fi">
+        <PreferenceCategory
+                android:key="prefs_wifi_networks_key"
+                android:title="Available Wi-Fi networks">
+            <ListPreference
+                    android:defaultValue="-1"
+                    android:entries="@array/pref_wifi_networks"
+                    android:entryValues="@array/pref_wifi_networks"
+                    android:title="Available Wi-Fi networks"
+                    android:key="prefs_wifi_key">
+            </ListPreference>
+        </PreferenceCategory>
+        <PreferenceCategory
+                android:key="prefs_wifi_others_key"
+                android:title="Other options">
+            <Preference
+                    android:title="Connect via WPS"
+                    android:key="prefs_wifi_connect_wps"><!-- You can use Intents here -->
+            </Preference>
+        </PreferenceCategory>
+    </PreferenceScreen>
+    <PreferenceScreen
+            android:icon="@drawable/ic_settings_time"
+            android:key="prefs_date_time_screen_key"
+            android:title="Date &amp; time">
+        <CheckBoxPreference
+                android:defaultValue="true"
+                android:disableDependentsState="true"
+                android:key="prefs_date_time_automatic"
+                android:summaryOff="On"
+                android:summaryOn="Off"
+                android:title="Automatic date  &amp; time"></CheckBoxPreference>
+        <Preference
+                android:dependency="prefs_date_time_automatic"
+                android:key="prefs_date"
+                android:summary="01/01/1970"
+                android:title="Date"></Preference>
+        <Preference
+                android:dependency="prefs_date_time_automatic"
+                android:key="prefs_time"
+                android:summary="00:43 PM"
+                android:title="Time"></Preference>
+        <CheckBoxPreference
+                android:defaultValue="true"
+                android:disableDependentsState="true"
+                android:key="prefs_date_time_use_timezone"
+                android:summary="Use network provided time zone"
+                android:title="Automatic time zone"></CheckBoxPreference>
+        <Preference
+                android:dependency="prefs_date_time_use_timezone"
+                android:summary="GMT 07:00 Pacific Daylight Time"
+                android:title="Time zone"></Preference>
+    </PreferenceScreen>
+    <ListPreference
+            android:defaultValue="everyone"
+            android:icon="@drawable/ic_settings_parental_control"
+            android:entries="@array/pref_parent_control_entries"
+            android:entryValues="@array/pref_parent_control_entries_values"
+            android:key="prefs_parental_control_level_key"
+            android:dialogMessage="Allow contents rated for"
+            android:title="Parental Control">
+    </ListPreference>
+    <PreferenceScreen
+            android:icon="@drawable/ic_settings_apps"
+            android:key="prefs_apps_screen_key"
+            android:title="Apps">
+        <PreferenceCategory
+                android:key="prefs_app_settings_category"
+                android:title="Apps settings">
+            <PreferenceScreen
+                    android:key="prefs_app_settings_screen_key"
+                    android:summary="Lorem ipsum dolor sit amet consectur adipiscing."
+                    android:title="App permissions">
+                <Preference
+                        android:icon="@mipmap/app_banner_sample_app"
+                        android:summary="45.5 MB"
+                        android:selectable="false"
+                        android:title="Application A"></Preference>
+                <Preference
+                        android:selectable="false"
+                        android:summary="Hier steht ein voelligst sinnfreier Text den ja sowieso niemandhier lesen kann. Deshalb macht es auch keinen Unterschied ob hier sinnvolles und nicht so sinnvolles Zeug steht. Hm... Sasha, du kannst das vielleicht lesen und denkst dir jetzt auch, dass ich voll haengen geblieben bin, oder?... ^_^"></Preference>
+                <Preference
+                        android:title="Force Stop"
+                        android:key="pref_force_stop"><!-- Start an Intent --></Preference>
+                <Preference
+                        android:title="Uninstall"
+                        android:key="pref_uninstall"><!-- Start an Intent --></Preference>
+                <Preference
+                        android:title="More Information"
+                        android:key="pref_more_info"></Preference>
+                <SeekBarPreference
+                        android:key="pref_sample_seekbar"
+                        android:title="Seekbar Title"
+                        android:summary="Seekbar Summary"
+                        android:defaultValue="-10"
+                        app:min="-50"
+                        android:max="140"
+                        app:adjustable="true"
+                        app:showSeekBarValue="true"
+                />
+                <SwitchPreference
+                        android:key="wifi_always_scan"
+                        android:persistent="false"
+                        android:title="SwitchPref Title"
+                        android:summary="SwitchPref Summary" />
+            </PreferenceScreen>
+        </PreferenceCategory>
+        <PreferenceCategory
+                android:key="prefs_app_downloaded_apps_category"
+                android:title="Downloaded Apps">
+            <ListPreference
+                    android:defaultValue="everyone"
+                    android:entries="@array/pref_parent_control_entries"
+                    android:entryValues="@array/pref_parent_control_entries_values"
+                    android:key="prefs_parental_control_level_key"
+                    android:title="Downloaded Apps">
+            </ListPreference>
+        </PreferenceCategory>
+    </PreferenceScreen>
+</PreferenceScreen>
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
index 99943df..54e851d 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BackgroundHelper.java
@@ -69,7 +69,7 @@
             mTask.execute(mRequest);
             mRunnable = null;
         }
-    };
+    }
 
     class LoadBitmapTask extends AsyncTask<Request, Object, Request> {
         @Override
@@ -136,6 +136,14 @@
         mHandler.postDelayed(mRunnable, SET_BACKGROUND_DELAY_MS);
     }
 
+    public void setDrawable(Activity activity, Drawable drawable) {
+        BackgroundManager backgroundManager = BackgroundManager.getInstance(activity);
+        if (!backgroundManager.isAttached()) {
+            backgroundManager.attachToView(activity.findViewById(R.id.details_background_view));
+        }
+        backgroundManager.setDrawable(drawable);
+    }
+
     static public void attach(Activity activity) {
         if (!ENABLED) {
             return;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java
index 6892c2c..9a95e33 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java
@@ -15,6 +15,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.HeaderItem;
 import android.support.v17.leanback.widget.ListRow;
@@ -23,9 +24,9 @@
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
-import android.os.Handler;
 
 import java.util.Random;
 
@@ -57,8 +58,8 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(
-                getActivity().getResources().getDrawable(R.drawable.ic_title));
+        setBadgeDrawable(ResourcesCompat.getDrawable(getActivity().getResources(),
+                R.drawable.ic_title, getActivity().getTheme()));
         setTitle("Leanback Sample App");
         setHeadersState(HEADERS_ENABLED);
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
index 43f726e..9b978f9 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java
@@ -26,9 +26,6 @@
 
 public class BrowseErrorActivity extends Activity
 {
-    private ErrorFragment mErrorFragment;
-    private SpinnerFragment mSpinnerFragment;
-
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState)
@@ -52,12 +49,6 @@
     }
 
     private void testError() {
-        mErrorFragment = new ErrorFragment();
-        getFragmentManager().beginTransaction().add(R.id.main_frame, mErrorFragment).commit();
-
-        mSpinnerFragment = new SpinnerFragment();
-        getFragmentManager().beginTransaction().add(R.id.main_frame, mSpinnerFragment).commit();
-
         Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
             @Override
@@ -65,8 +56,8 @@
                 if (getFragmentManager().isDestroyed()) {
                     return;
                 }
-                getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit();
-                mErrorFragment.setErrorContent(getResources());
+                getFragmentManager().beginTransaction().add(R.id.main_frame,
+                        new ErrorFragment()).commit();
             }
         }, 3000);
     }
@@ -77,7 +68,8 @@
                     Bundle savedInstanceState) {
             ProgressBar progressBar = new ProgressBar(container.getContext());
             if (container instanceof FrameLayout) {
-                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(100, 100, Gravity.CENTER);
+                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(100, 100,
+                        Gravity.CENTER);
                 progressBar.setLayoutParams(layoutParams);
             }
             return progressBar;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
index f1e7b1e..963325e 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseErrorActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -28,9 +29,6 @@
 
 public class BrowseErrorSupportActivity extends FragmentActivity
 {
-    private ErrorSupportFragment mErrorSupportFragment;
-    private SpinnerSupportFragment mSpinnerSupportFragment;
-
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState)
@@ -54,12 +52,6 @@
     }
 
     private void testError() {
-        mErrorSupportFragment = new ErrorSupportFragment();
-        getSupportFragmentManager().beginTransaction().add(R.id.main_frame, mErrorSupportFragment).commit();
-
-        mSpinnerSupportFragment = new SpinnerSupportFragment();
-        getSupportFragmentManager().beginTransaction().add(R.id.main_frame, mSpinnerSupportFragment).commit();
-
         Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
             @Override
@@ -67,8 +59,8 @@
                 if (getSupportFragmentManager().isDestroyed()) {
                     return;
                 }
-                getSupportFragmentManager().beginTransaction().remove(mSpinnerSupportFragment).commit();
-                mErrorSupportFragment.setErrorContent(getResources());
+                getSupportFragmentManager().beginTransaction().add(R.id.main_frame,
+                        new ErrorSupportFragment()).commit();
             }
         }, 3000);
     }
@@ -79,7 +71,8 @@
                     Bundle savedInstanceState) {
             ProgressBar progressBar = new ProgressBar(container.getContext());
             if (container instanceof FrameLayout) {
-                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(100, 100, Gravity.CENTER);
+                FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(100, 100,
+                        Gravity.CENTER);
                 progressBar.setLayoutParams(layoutParams);
             }
             return progressBar;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
index c601ca8..378b7b5 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
@@ -33,6 +33,7 @@
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SectionRow;
 import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -64,7 +65,8 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        setBadgeDrawable(ResourcesCompat.getDrawable(getActivity().getResources(),
+                R.drawable.ic_title, getActivity().getTheme()));
         setTitle("Leanback Sample App");
         setHeadersState(HEADERS_ENABLED);
         setOnSearchClickedListener(new View.OnClickListener() {
@@ -115,7 +117,7 @@
 
     private void setupRows() {
         ListRowPresenter listRowPresenter = new ListRowPresenter();
-        listRowPresenter.setNumRows(2);
+        listRowPresenter.setNumRows(1);
         mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
         setAdapter(mRowsAdapter);
     }
@@ -128,7 +130,9 @@
 
         mRowsAdapter.add(new SectionRow(new HeaderItem("section 0")));
         for (; i < NUM_ROWS; ++i) {
-            mRowsAdapter.add(new ListRow(new HeaderItem(i, "Row " + i), createListRowAdapter(i)));
+            HeaderItem headerItem = new HeaderItem(i, "Row " + i);
+            headerItem.setDescription("Description for Row "+i);
+            mRowsAdapter.add(new ListRow(headerItem, createListRowAdapter(i)));
         }
 
         mRowsAdapter.add(new DividerRow());
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
index 0bafcc2..e96c220 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseActivity.java.  DO NOT MODIFY. */
 
 /*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
index 7719b0e..0773d47 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -35,6 +36,7 @@
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SectionRow;
 import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -66,7 +68,8 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        setBadgeDrawable(ResourcesCompat.getDrawable(getActivity().getResources(),
+                R.drawable.ic_title, getActivity().getTheme()));
         setTitle("Leanback Sample App");
         setHeadersState(HEADERS_ENABLED);
         setOnSearchClickedListener(new View.OnClickListener() {
@@ -117,7 +120,7 @@
 
     private void setupRows() {
         ListRowPresenter listRowPresenter = new ListRowPresenter();
-        listRowPresenter.setNumRows(2);
+        listRowPresenter.setNumRows(1);
         mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
         setAdapter(mRowsAdapter);
     }
@@ -130,7 +133,9 @@
 
         mRowsAdapter.add(new SectionRow(new HeaderItem("section 0")));
         for (; i < NUM_ROWS; ++i) {
-            mRowsAdapter.add(new ListRow(new HeaderItem(i, "Row " + i), createListRowAdapter(i)));
+            HeaderItem headerItem = new HeaderItem(i, "Row " + i);
+            headerItem.setDescription("Description for Row "+i);
+            mRowsAdapter.add(new ListRow(headerItem, createListRowAdapter(i)));
         }
 
         mRowsAdapter.add(new DividerRow());
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java
index 924975d..066832b 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java
@@ -15,16 +15,15 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import com.example.android.leanback.R;
 import android.support.v17.leanback.widget.ImageCardView;
 import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.ViewGroup;
 import android.view.ContextThemeWrapper;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.widget.TextView;
 
 import java.util.Random;
 
@@ -104,8 +103,9 @@
     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
         Log.d(TAG, "onBindViewHolder for " + item.toString());
         PhotoItem photoItem = (PhotoItem) item;
-        Drawable drawable =  viewHolder.view.getContext().getResources()
-                .getDrawable(photoItem.getImageResourceId());
+        final Context context = viewHolder.view.getContext();
+        Drawable drawable =  ResourcesCompat.getDrawable(context.getResources(),
+                photoItem.getImageResourceId(), context.getTheme());
         ((ImageCardView) viewHolder.view).setMainImage(drawable);
         ((ImageCardView) viewHolder.view).setTitleText(photoItem.getTitle());
         if (!TextUtils.isEmpty(photoItem.getContent())) {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java
index 1ec1041..43c6910 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsActivity.java
@@ -31,6 +31,7 @@
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
+        getFragmentManager().enableDebugLogging(true);
         setContentView(useLegacyFragment() ? R.layout.legacy_details : R.layout.details);
         if (savedInstanceState == null) {
             // Only pass object to fragment when activity is first time created,
@@ -44,16 +45,4 @@
             }
         }
     }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        BackgroundHelper.attach(this);
-    }
-
-    @Override
-    public void onStop() {
-        BackgroundHelper.release(this);
-        super.onStop();
-    }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
index 9238562..cae115f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java
@@ -13,11 +13,10 @@
  */
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -34,6 +33,8 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
@@ -67,7 +68,9 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Sample App");
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -78,17 +81,19 @@
         });
 
         mActionPlay = new Action(ACTION_PLAY, "Play");
-        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99",
-                getResources().getDrawable(R.drawable.ic_action_a));
+        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99", ResourcesCompat.getDrawable(
+                context.getResources(), R.drawable.ic_action_a, context.getTheme()));
         mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
 
         ClassPresenterSelector ps = new ClassPresenterSelector();
+        @SuppressWarnings("deprecation")
         DetailsOverviewRowPresenter dorPresenter =
                 new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
         dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
             @Override
             public void onActionClicked(Action action) {
-                Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
+                final Context context = getActivity();
+                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
                 DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(0);
                 if (action.getId() == ACTION_BUY) {
                     // on the UI thread, we can modify actions adapter directly
@@ -98,7 +103,8 @@
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
                     dor.setItem(mPhotoItem.getTitle() + "(Owned)");
-                    dor.setImageDrawable(getResources().getDrawable(R.drawable.details_img_16x9));
+                    dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                            R.drawable.details_img_16x9, context.getTheme()));
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -107,7 +113,7 @@
                     actions.clear(ACTION_RENT);
                     dor.setItem(mPhotoItem.getTitle() + "(Rented)");
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
+                    Intent intent = new Intent(context, PlaybackOverlayActivity.class);
                     getActivity().startActivity(intent);
                 }
             }
@@ -161,6 +167,7 @@
         }
     }
 
+
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -173,9 +180,10 @@
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
             public void run() {
-                Resources res = getActivity().getResources();
+                final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
-                dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId()));
+                dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                        mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
                 adapter.set(ACTION_RENT, mActionRent);
                 adapter.set(ACTION_BUY, mActionBuy);
@@ -212,5 +220,4 @@
                     getActivity(), mPhotoItem.getImageResourceId());
         }
     }
-
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
index 9fe7fd3..01bd583 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsPresenterSelectionActivity.java
@@ -17,18 +17,15 @@
 package com.example.android.leanback;
 
 import android.app.Activity;
-import android.app.FragmentManager;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v17.leanback.app.GuidedStepFragment;
-import android.support.v17.leanback.widget.GuidedAction;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
 
 import java.util.List;
-import java.util.ArrayList;
 
 /**
  * Activity that showcases different aspects of GuidedStepFragments.
@@ -54,7 +51,7 @@
     }
 
     private static void addAction(List<GuidedAction> actions, long id, String title, String desc) {
-        actions.add(new GuidedAction.Builder()
+        actions.add(new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
@@ -63,7 +60,7 @@
 
     private static void addCheckedAction(List<GuidedAction> actions, Context context,
             long id, String title, String desc, boolean checked) {
-        actions.add(new GuidedAction.Builder()
+        actions.add(new GuidedAction.Builder(null)
                 .title(title)
                 .description(desc)
                 .id(id)
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportActivity.java
index 40c2109..3ebf101 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from DetailsActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -33,6 +34,7 @@
     public void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
+        getSupportFragmentManager().enableDebugLogging(true);
         setContentView(useLegacyFragment() ? R.layout.legacy_details_support : R.layout.details_support);
         if (savedInstanceState == null) {
             // Only pass object to fragment when activity is first time created,
@@ -46,16 +48,4 @@
             }
         }
     }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        BackgroundHelper.attach(this);
-    }
-
-    @Override
-    public void onStop() {
-        BackgroundHelper.release(this);
-        super.onStop();
-    }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
index 143a37d..eb1e201 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from DetailsFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,11 +16,10 @@
  */
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -36,6 +36,8 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
@@ -69,7 +71,9 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Sample App");
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -80,17 +84,19 @@
         });
 
         mActionPlay = new Action(ACTION_PLAY, "Play");
-        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99",
-                getResources().getDrawable(R.drawable.ic_action_a));
+        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99", ResourcesCompat.getDrawable(
+                context.getResources(), R.drawable.ic_action_a, context.getTheme()));
         mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
 
         ClassPresenterSelector ps = new ClassPresenterSelector();
+        @SuppressWarnings("deprecation")
         DetailsOverviewRowPresenter dorPresenter =
                 new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
         dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
             @Override
             public void onActionClicked(Action action) {
-                Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
+                final Context context = getActivity();
+                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
                 DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(0);
                 if (action.getId() == ACTION_BUY) {
                     // on the UI thread, we can modify actions adapter directly
@@ -100,7 +106,8 @@
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
                     dor.setItem(mPhotoItem.getTitle() + "(Owned)");
-                    dor.setImageDrawable(getResources().getDrawable(R.drawable.details_img_16x9));
+                    dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                            R.drawable.details_img_16x9, context.getTheme()));
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -109,7 +116,7 @@
                     actions.clear(ACTION_RENT);
                     dor.setItem(mPhotoItem.getTitle() + "(Rented)");
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(getActivity(), PlaybackOverlaySupportActivity.class);
+                    Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
                     getActivity().startActivity(intent);
                 }
             }
@@ -163,6 +170,7 @@
         }
     }
 
+
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -175,9 +183,10 @@
         mRowsAdapter.clear();
         new Handler().postDelayed(new Runnable() {
             public void run() {
-                Resources res = getActivity().getResources();
+                final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
-                dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId()));
+                dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                        mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
                 adapter.set(ACTION_RENT, mActionRent);
                 adapter.set(ACTION_BUY, mActionBuy);
@@ -214,5 +223,4 @@
                     getActivity(), mPhotoItem.getImageResourceId());
         }
     }
-
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorFragment.java
index 7a88c91..c05fbe0 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorFragment.java
@@ -13,15 +13,9 @@
  */
 package com.example.android.leanback;
 
-import android.content.Intent;
-import android.content.res.Resources;
+import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.HeaderItem;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ListRowPresenter;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.SearchOrbView;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 
@@ -35,10 +29,9 @@
         super.onCreate(savedInstanceState);
 
         setTitle("Leanback Sample App");
-    }
-
-    void setErrorContent(Resources resources) {
-        setImageDrawable(resources.getDrawable(R.drawable.lb_ic_sad_cloud));
+        final Context context = getActivity();
+        setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.lb_ic_sad_cloud, context.getTheme()));
         setMessage("An error occurred.");
         setDefaultBackground(TRANSLUCENT);
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorSupportFragment.java
index ea3e17c..dbc8945 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/ErrorSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from ErrorFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,15 +16,9 @@
  */
 package com.example.android.leanback;
 
-import android.content.Intent;
-import android.content.res.Resources;
+import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.HeaderItem;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ListRowPresenter;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.SearchOrbView;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 
@@ -37,10 +32,9 @@
         super.onCreate(savedInstanceState);
 
         setTitle("Leanback Sample App");
-    }
-
-    void setErrorContent(Resources resources) {
-        setImageDrawable(resources.getDrawable(R.drawable.lb_ic_sad_cloud));
+        final Context context = getActivity();
+        setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.lb_ic_sad_cloud, context.getTheme()));
         setMessage("An error occurred.");
         setDefaultBackground(TRANSLUCENT);
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
index 15af18f..94f7d6c 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepActivity.java
@@ -17,9 +17,9 @@
 package com.example.android.leanback;
 
 import android.app.Activity;
+import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -28,13 +28,14 @@
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
-import android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder;
 import android.support.v17.leanback.widget.GuidedDatePickerAction;
+import android.support.v4.content.res.ResourcesCompat;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.inputmethod.EditorInfo;
 
 import java.util.ArrayList;
@@ -94,49 +95,59 @@
         super.onRestoreInstanceState(savedInstanceState);
     }
 
-    private static void addAction(List<GuidedAction> actions, long id, String title, String desc) {
-        actions.add(new GuidedAction.Builder()
+    private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
+            String desc) {
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .build());
+        return action;
     }
 
-    private static void addAction(List<GuidedAction> actions, long id, String title, String desc,
-            List<GuidedAction> subActions) {
-        actions.add(new GuidedAction.Builder()
+    private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
+            String desc, List<GuidedAction> subActions) {
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .subActions(subActions)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(Context context, List<GuidedAction> actions,
+    private static GuidedAction addEditableAction(Context context, List<GuidedAction> actions,
             long id, String title, String desc) {
-        actions.add(new GuidedAction.Builder(context)
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(context)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .editable(true)
                 .icon(R.drawable.lb_ic_search_mic)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
+    private static GuidedAction addEditableAction(List<GuidedAction> actions, long id, String title,
             String editTitle, String desc) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .editTitle(editTitle)
                 .description(desc)
                 .editable(true)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
+    private static GuidedAction addEditableAction(List<GuidedAction> actions, long id, String title,
             String editTitle, int editInputType, String desc, String editDesc) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .editTitle(editTitle)
@@ -145,19 +156,24 @@
                 .editDescription(editDesc)
                 .editable(true)
                 .build());
+        return action;
     }
 
-    private static void addDatePickerAction(List<GuidedAction> actions, long id, String title) {
-        actions.add(new GuidedDatePickerAction.Builder(null)
+    private static GuidedDatePickerAction addDatePickerAction(List<GuidedAction> actions, long id,
+            String title) {
+        GuidedDatePickerAction action;
+        actions.add(action = new GuidedDatePickerAction.Builder(null)
                 .id(id)
                 .title(title)
                 .datePickerFormat("MY")
                 .build());
+        return action;
     }
 
-    private static void addEditableDescriptionAction(List<GuidedAction> actions, long id,
+    private static GuidedAction addEditableDescriptionAction(List<GuidedAction> actions, long id,
             String title, String desc, String editDescription, int descriptionEditInputType) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
@@ -165,16 +181,19 @@
                 .descriptionEditInputType(descriptionEditInputType)
                 .descriptionEditable(true)
                 .build());
+        return action;
     }
 
-    private static void addCheckedAction(List<GuidedAction> actions, long id, Context context,
+    private static GuidedAction addCheckedAction(List<GuidedAction> actions, long id,
             String title, String desc, int checkSetId) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .checkSetId(checkSetId)
                 .build());
+        return action;
     }
 
     public static class FirstStepFragment extends GuidedStepFragment {
@@ -189,7 +208,9 @@
             String title = getString(R.string.guidedstep_first_title);
             String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
             String description = getString(R.string.guidedstep_first_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -217,6 +238,11 @@
         }
     }
 
+    public interface NewPaymentFragmentTarget {
+        void onNewPaymentFragmentStarted();
+        void onNewPaymentAdded(int selection);
+    }
+
     static ArrayList<String> sCards = new ArrayList<String>();
     static int sSelectedCard = -1;
     static {
@@ -226,12 +252,26 @@
 
     public static class NewPaymentStepFragment extends GuidedStepFragment {
 
+        NewPaymentFragmentTarget mNewPaymentTarget;
+
+        @Override
+        public void onCreate(Bundle savedInstance) {
+            super.onCreate(savedInstance);
+            Fragment targetFragment = getTargetFragment();
+            if (targetFragment instanceof NewPaymentFragmentTarget) {
+                mNewPaymentTarget = ((NewPaymentFragmentTarget) targetFragment);
+                mNewPaymentTarget.onNewPaymentFragmentStarted();
+            }
+        }
+
         @Override
         public Guidance onCreateGuidance(Bundle savedInstanceState) {
             String title = getString(R.string.guidedstep_newpayment_title);
             String breadcrumb = getString(R.string.guidedstep_newpayment_breadcrumb);
             String description = getString(R.string.guidedstep_newpayment_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -262,8 +302,11 @@
                 } else {
                     card = "Master "+cardNumber;
                 }
-                sSelectedCard = sCards.size();
+                int selection = sCards.size();
                 sCards.add(card);
+                if (mNewPaymentTarget != null) {
+                    mNewPaymentTarget.onNewPaymentAdded(selection);
+                }
                 popBackStackToGuidedStepFragment(NewPaymentStepFragment.class,
                         FragmentManager.POP_BACK_STACK_INCLUSIVE);
             }
@@ -311,7 +354,27 @@
         }
     }
 
-    public static class SecondStepFragment extends GuidedStepFragment {
+    public static class SecondStepFragment extends GuidedStepFragment
+            implements NewPaymentFragmentTarget {
+
+
+        boolean mExpandPaymentListInOnCreateView;
+
+        @Override
+        public void onNewPaymentAdded(int selection) {
+            // if a new payment is added, we dont need expand the sub actions list.
+            mExpandPaymentListInOnCreateView = false;
+            sSelectedCard = selection;
+            updatePaymentAction(findActionById(PAYMENT));
+            findButtonActionById(GuidedAction.ACTION_ID_CONTINUE).setEnabled(sSelectedCard != -1);
+        }
+
+        @Override
+        public void onNewPaymentFragmentStarted() {
+            // if a new payment fragment is opened, when come back we should expand the payment
+            // sub actions list unless user created a new payment in onNewPaymentAdded
+            mExpandPaymentListInOnCreateView = true;
+        }
 
         public GuidedActionsStylist onCreateActionsStylist() {
             return new GuidedActionsStylist() {
@@ -332,7 +395,9 @@
             String title = getString(R.string.guidedstep_second_title);
             String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
             String description = getString(R.string.guidedstep_second_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -341,7 +406,7 @@
             addEditableAction(getContext(), actions, FIRST_NAME, "Pat", "Your first name");
             addEditableAction(getContext(), actions, LAST_NAME, "Smith", "Your last name");
             List<GuidedAction> subActions = new ArrayList<GuidedAction>();
-            addAction(actions, PAYMENT, "Select Payment", "", subActions);
+            updatePaymentAction(addAction(actions, PAYMENT, "Select Payment", "", subActions));
             addEditableDescriptionAction(actions, PASSWORD, "Password", "", "",
                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
         }
@@ -351,6 +416,7 @@
             actions.add(new GuidedAction.Builder(getActivity())
                     .clickAction(GuidedAction.ACTION_ID_CONTINUE)
                     .description("Continue")
+                    .enabled(isPasswordValid() && isPaymentValid())
                     .build());
         }
 
@@ -362,6 +428,20 @@
             }
         }
 
+        void updatePaymentAction(GuidedAction paymentAction) {
+            List<GuidedAction> subActions = paymentAction.getSubActions();
+            subActions.clear();
+            for (int i = 0; i < sCards.size(); i++) {
+                addCheckedAction(subActions, -1, sCards.get(i), "",
+                        GuidedAction.DEFAULT_CHECK_SET_ID);
+                if (i == sSelectedCard) {
+                    subActions.get(i).setChecked(true);
+                }
+            }
+            addAction(subActions, NEW_PAYMENT, "Add New Card", "");
+            paymentAction.setDescription(sSelectedCard == -1 ? "" : sCards.get(sSelectedCard));
+        }
+
         @Override
         public long onGuidedActionEditedAndProceed(GuidedAction action) {
             if (action.getId() == PASSWORD) {
@@ -398,30 +478,21 @@
                 return true;
             } else {
                 FragmentManager fm = getFragmentManager();
-                GuidedStepFragment.add(fm, new NewPaymentStepFragment(), R.id.lb_guidedstep_host);
+                NewPaymentStepFragment newPaymentFragment = new NewPaymentStepFragment();
+                newPaymentFragment.setTargetFragment(this, 0);
+                GuidedStepFragment.add(fm, newPaymentFragment, R.id.lb_guidedstep_host);
                 return false;
             }
         }
 
         @Override
-        public void onResume() {
-            super.onResume();
-            // when resumed, update sub actions list and selected index from data model.
-            GuidedAction payments = findActionById(PAYMENT);
-            payments.getSubActions().clear();
-            for (int i = 0; i < sCards.size(); i++) {
-                addCheckedAction(payments.getSubActions(), -1, getActivity(), sCards.get(i), "",
-                        GuidedAction.DEFAULT_CHECK_SET_ID);
-                if (i == sSelectedCard) {
-                    payments.getSubActions().get(i).setChecked(true);
-                }
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View view = super.onCreateView(inflater, container, savedInstanceState);
+            if (mExpandPaymentListInOnCreateView) {
+                expandAction(findActionById(PAYMENT), false);
             }
-            addAction(payments.getSubActions(), NEW_PAYMENT, "Add New Card", "");
-            if (sSelectedCard != -1) {
-                payments.setDescription(sCards.get(sSelectedCard));
-            }
-            notifyActionChanged(findActionPositionById(PAYMENT));
-            updateContinue(isPasswordValid() && isPaymentValid());
+            return view;
         }
 
         boolean isPaymentValid() {
@@ -452,7 +523,9 @@
             String title = getString(R.string.guidedstep_third_title);
             String breadcrumb = getString(R.string.guidedstep_third_breadcrumb);
             String description = getString(R.string.guidedstep_third_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -470,7 +543,7 @@
         public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
             String desc = "The description can be quite long as well.  " +
                     "Just be sure to set multilineDescription to true in the GuidedAction.";
-            actions.add(new GuidedAction.Builder()
+            actions.add(new GuidedAction.Builder(getActivity())
                     .title("Note that Guided Actions can have titles that are quite long.")
                     .description(desc)
                     .multilineDescription(true)
@@ -479,14 +552,14 @@
                     .focusable(false)
                     .build());
             for (int i = 0; i < OPTION_NAMES.length; i++) {
-                addCheckedAction(actions, RADIO_ID_BASE + i, getActivity(), OPTION_NAMES[i],
+                addCheckedAction(actions, RADIO_ID_BASE + i, OPTION_NAMES[i],
                         OPTION_DESCRIPTIONS[i], GuidedAction.DEFAULT_CHECK_SET_ID);
                 if (i == DEFAULT_OPTION) {
                     actions.get(actions.size() -1).setChecked(true);
                 }
             }
             for (int i = 0; i < OPTION_NAMES.length; i++) {
-                addCheckedAction(actions, CHECKBOX_ID_BASE + i, getActivity(), OPTION_NAMES[i],
+                addCheckedAction(actions, CHECKBOX_ID_BASE + i, OPTION_NAMES[i],
                         OPTION_DESCRIPTIONS[i], GuidedAction.CHECKBOX_CHECK_SET_ID);
             }
         }
@@ -531,7 +604,9 @@
             String title = getString(R.string.guidedstep_fourth_title);
             String breadcrumb = getString(R.string.guidedstep_fourth_breadcrumb);
             String description = "You chose: " + OPTION_NAMES[(int) getOption()];
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepHalfScreenActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepHalfScreenActivity.java
index 4e8b33f..9ee12c3 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepHalfScreenActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepHalfScreenActivity.java
@@ -22,9 +22,9 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v17.leanback.app.GuidedStepFragment;
-import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 
 import java.util.List;
@@ -52,7 +52,9 @@
             String title = getString(R.string.guidedstep_first_title);
             String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
             String description = getString(R.string.guidedstep_first_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -107,7 +109,9 @@
             String title = getString(R.string.guidedstep_second_title);
             String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
             String description = getString(R.string.guidedstep_second_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
index 6aaaa69..bb9aa1e 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from GuidedStepActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -19,9 +20,9 @@
 package com.example.android.leanback;
 
 import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -30,13 +31,14 @@
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
-import android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder;
 import android.support.v17.leanback.widget.GuidedDatePickerAction;
+import android.support.v4.content.res.ResourcesCompat;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.inputmethod.EditorInfo;
 
 import java.util.ArrayList;
@@ -96,49 +98,59 @@
         super.onRestoreInstanceState(savedInstanceState);
     }
 
-    private static void addAction(List<GuidedAction> actions, long id, String title, String desc) {
-        actions.add(new GuidedAction.Builder()
+    private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
+            String desc) {
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .build());
+        return action;
     }
 
-    private static void addAction(List<GuidedAction> actions, long id, String title, String desc,
-            List<GuidedAction> subActions) {
-        actions.add(new GuidedAction.Builder()
+    private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
+            String desc, List<GuidedAction> subActions) {
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .subActions(subActions)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(Context context, List<GuidedAction> actions,
+    private static GuidedAction addEditableAction(Context context, List<GuidedAction> actions,
             long id, String title, String desc) {
-        actions.add(new GuidedAction.Builder(context)
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(context)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .editable(true)
                 .icon(R.drawable.lb_ic_search_mic)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
+    private static GuidedAction addEditableAction(List<GuidedAction> actions, long id, String title,
             String editTitle, String desc) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .editTitle(editTitle)
                 .description(desc)
                 .editable(true)
                 .build());
+        return action;
     }
 
-    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
+    private static GuidedAction addEditableAction(List<GuidedAction> actions, long id, String title,
             String editTitle, int editInputType, String desc, String editDesc) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .editTitle(editTitle)
@@ -147,19 +159,24 @@
                 .editDescription(editDesc)
                 .editable(true)
                 .build());
+        return action;
     }
 
-    private static void addDatePickerAction(List<GuidedAction> actions, long id, String title) {
-        actions.add(new GuidedDatePickerAction.Builder(null)
+    private static GuidedDatePickerAction addDatePickerAction(List<GuidedAction> actions, long id,
+            String title) {
+        GuidedDatePickerAction action;
+        actions.add(action = new GuidedDatePickerAction.Builder(null)
                 .id(id)
                 .title(title)
                 .datePickerFormat("MY")
                 .build());
+        return action;
     }
 
-    private static void addEditableDescriptionAction(List<GuidedAction> actions, long id,
+    private static GuidedAction addEditableDescriptionAction(List<GuidedAction> actions, long id,
             String title, String desc, String editDescription, int descriptionEditInputType) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
@@ -167,16 +184,19 @@
                 .descriptionEditInputType(descriptionEditInputType)
                 .descriptionEditable(true)
                 .build());
+        return action;
     }
 
-    private static void addCheckedAction(List<GuidedAction> actions, long id, Context context,
+    private static GuidedAction addCheckedAction(List<GuidedAction> actions, long id,
             String title, String desc, int checkSetId) {
-        actions.add(new GuidedAction.Builder()
+        GuidedAction action;
+        actions.add(action = new GuidedAction.Builder(null)
                 .id(id)
                 .title(title)
                 .description(desc)
                 .checkSetId(checkSetId)
                 .build());
+        return action;
     }
 
     public static class FirstStepFragment extends GuidedStepSupportFragment {
@@ -191,7 +211,9 @@
             String title = getString(R.string.guidedstep_first_title);
             String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
             String description = getString(R.string.guidedstep_first_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -219,6 +241,11 @@
         }
     }
 
+    public interface NewPaymentFragmentTarget {
+        void onNewPaymentFragmentStarted();
+        void onNewPaymentAdded(int selection);
+    }
+
     static ArrayList<String> sCards = new ArrayList<String>();
     static int sSelectedCard = -1;
     static {
@@ -228,12 +255,26 @@
 
     public static class NewPaymentStepFragment extends GuidedStepSupportFragment {
 
+        NewPaymentFragmentTarget mNewPaymentTarget;
+
+        @Override
+        public void onCreate(Bundle savedInstance) {
+            super.onCreate(savedInstance);
+            Fragment targetFragment = getTargetFragment();
+            if (targetFragment instanceof NewPaymentFragmentTarget) {
+                mNewPaymentTarget = ((NewPaymentFragmentTarget) targetFragment);
+                mNewPaymentTarget.onNewPaymentFragmentStarted();
+            }
+        }
+
         @Override
         public Guidance onCreateGuidance(Bundle savedInstanceState) {
             String title = getString(R.string.guidedstep_newpayment_title);
             String breadcrumb = getString(R.string.guidedstep_newpayment_breadcrumb);
             String description = getString(R.string.guidedstep_newpayment_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -264,8 +305,11 @@
                 } else {
                     card = "Master "+cardNumber;
                 }
-                sSelectedCard = sCards.size();
+                int selection = sCards.size();
                 sCards.add(card);
+                if (mNewPaymentTarget != null) {
+                    mNewPaymentTarget.onNewPaymentAdded(selection);
+                }
                 popBackStackToGuidedStepSupportFragment(NewPaymentStepFragment.class,
                         FragmentManager.POP_BACK_STACK_INCLUSIVE);
             }
@@ -313,7 +357,27 @@
         }
     }
 
-    public static class SecondStepFragment extends GuidedStepSupportFragment {
+    public static class SecondStepFragment extends GuidedStepSupportFragment
+            implements NewPaymentFragmentTarget {
+
+
+        boolean mExpandPaymentListInOnCreateView;
+
+        @Override
+        public void onNewPaymentAdded(int selection) {
+            // if a new payment is added, we dont need expand the sub actions list.
+            mExpandPaymentListInOnCreateView = false;
+            sSelectedCard = selection;
+            updatePaymentAction(findActionById(PAYMENT));
+            findButtonActionById(GuidedAction.ACTION_ID_CONTINUE).setEnabled(sSelectedCard != -1);
+        }
+
+        @Override
+        public void onNewPaymentFragmentStarted() {
+            // if a new payment fragment is opened, when come back we should expand the payment
+            // sub actions list unless user created a new payment in onNewPaymentAdded
+            mExpandPaymentListInOnCreateView = true;
+        }
 
         public GuidedActionsStylist onCreateActionsStylist() {
             return new GuidedActionsStylist() {
@@ -334,7 +398,9 @@
             String title = getString(R.string.guidedstep_second_title);
             String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
             String description = getString(R.string.guidedstep_second_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -343,7 +409,7 @@
             addEditableAction(getContext(), actions, FIRST_NAME, "Pat", "Your first name");
             addEditableAction(getContext(), actions, LAST_NAME, "Smith", "Your last name");
             List<GuidedAction> subActions = new ArrayList<GuidedAction>();
-            addAction(actions, PAYMENT, "Select Payment", "", subActions);
+            updatePaymentAction(addAction(actions, PAYMENT, "Select Payment", "", subActions));
             addEditableDescriptionAction(actions, PASSWORD, "Password", "", "",
                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
         }
@@ -353,6 +419,7 @@
             actions.add(new GuidedAction.Builder(getActivity())
                     .clickAction(GuidedAction.ACTION_ID_CONTINUE)
                     .description("Continue")
+                    .enabled(isPasswordValid() && isPaymentValid())
                     .build());
         }
 
@@ -364,6 +431,20 @@
             }
         }
 
+        void updatePaymentAction(GuidedAction paymentAction) {
+            List<GuidedAction> subActions = paymentAction.getSubActions();
+            subActions.clear();
+            for (int i = 0; i < sCards.size(); i++) {
+                addCheckedAction(subActions, -1, sCards.get(i), "",
+                        GuidedAction.DEFAULT_CHECK_SET_ID);
+                if (i == sSelectedCard) {
+                    subActions.get(i).setChecked(true);
+                }
+            }
+            addAction(subActions, NEW_PAYMENT, "Add New Card", "");
+            paymentAction.setDescription(sSelectedCard == -1 ? "" : sCards.get(sSelectedCard));
+        }
+
         @Override
         public long onGuidedActionEditedAndProceed(GuidedAction action) {
             if (action.getId() == PASSWORD) {
@@ -400,30 +481,21 @@
                 return true;
             } else {
                 FragmentManager fm = getFragmentManager();
-                GuidedStepSupportFragment.add(fm, new NewPaymentStepFragment(), R.id.lb_guidedstep_host);
+                NewPaymentStepFragment newPaymentFragment = new NewPaymentStepFragment();
+                newPaymentFragment.setTargetFragment(this, 0);
+                GuidedStepSupportFragment.add(fm, newPaymentFragment, R.id.lb_guidedstep_host);
                 return false;
             }
         }
 
         @Override
-        public void onResume() {
-            super.onResume();
-            // when resumed, update sub actions list and selected index from data model.
-            GuidedAction payments = findActionById(PAYMENT);
-            payments.getSubActions().clear();
-            for (int i = 0; i < sCards.size(); i++) {
-                addCheckedAction(payments.getSubActions(), -1, getActivity(), sCards.get(i), "",
-                        GuidedAction.DEFAULT_CHECK_SET_ID);
-                if (i == sSelectedCard) {
-                    payments.getSubActions().get(i).setChecked(true);
-                }
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            View view = super.onCreateView(inflater, container, savedInstanceState);
+            if (mExpandPaymentListInOnCreateView) {
+                expandAction(findActionById(PAYMENT), false);
             }
-            addAction(payments.getSubActions(), NEW_PAYMENT, "Add New Card", "");
-            if (sSelectedCard != -1) {
-                payments.setDescription(sCards.get(sSelectedCard));
-            }
-            notifyActionChanged(findActionPositionById(PAYMENT));
-            updateContinue(isPasswordValid() && isPaymentValid());
+            return view;
         }
 
         boolean isPaymentValid() {
@@ -454,7 +526,9 @@
             String title = getString(R.string.guidedstep_third_title);
             String breadcrumb = getString(R.string.guidedstep_third_breadcrumb);
             String description = getString(R.string.guidedstep_third_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -472,7 +546,7 @@
         public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
             String desc = "The description can be quite long as well.  " +
                     "Just be sure to set multilineDescription to true in the GuidedAction.";
-            actions.add(new GuidedAction.Builder()
+            actions.add(new GuidedAction.Builder(getActivity())
                     .title("Note that Guided Actions can have titles that are quite long.")
                     .description(desc)
                     .multilineDescription(true)
@@ -481,14 +555,14 @@
                     .focusable(false)
                     .build());
             for (int i = 0; i < OPTION_NAMES.length; i++) {
-                addCheckedAction(actions, RADIO_ID_BASE + i, getActivity(), OPTION_NAMES[i],
+                addCheckedAction(actions, RADIO_ID_BASE + i, OPTION_NAMES[i],
                         OPTION_DESCRIPTIONS[i], GuidedAction.DEFAULT_CHECK_SET_ID);
                 if (i == DEFAULT_OPTION) {
                     actions.get(actions.size() -1).setChecked(true);
                 }
             }
             for (int i = 0; i < OPTION_NAMES.length; i++) {
-                addCheckedAction(actions, CHECKBOX_ID_BASE + i, getActivity(), OPTION_NAMES[i],
+                addCheckedAction(actions, CHECKBOX_ID_BASE + i, OPTION_NAMES[i],
                         OPTION_DESCRIPTIONS[i], GuidedAction.CHECKBOX_CHECK_SET_ID);
             }
         }
@@ -533,7 +607,9 @@
             String title = getString(R.string.guidedstep_fourth_title);
             String breadcrumb = getString(R.string.guidedstep_fourth_breadcrumb);
             String description = "You chose: " + OPTION_NAMES[(int) getOption()];
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportHalfScreenActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportHalfScreenActivity.java
index 388a559..8fd0025 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportHalfScreenActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/GuidedStepSupportHalfScreenActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from GuidedStepHalfScreenActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -24,9 +25,9 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v17.leanback.app.GuidedStepSupportFragment;
-import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 
 import java.util.List;
@@ -54,7 +55,9 @@
             String title = getString(R.string.guidedstep_first_title);
             String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
             String description = getString(R.string.guidedstep_first_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -109,7 +112,9 @@
             String title = getString(R.string.guidedstep_second_title);
             String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
             String description = getString(R.string.guidedstep_second_description);
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java
index 59155af..14c1db5 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java
@@ -16,30 +16,20 @@
 
 package com.example.android.leanback;
 
-import android.support.v7.widget.RecyclerView;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.OnChildSelectedListener;
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
-import android.graphics.BitmapFactory;
 import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Bundle;
+import android.support.v17.leanback.widget.HorizontalGridView;
+import android.support.v17.leanback.widget.OnChildViewHolderSelectedListener;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
-import android.util.SparseArray;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.TextView;
 
-import java.io.File;
-
 public class HorizontalGridTestActivity extends Activity {
     private static final String TAG = "HorizontalGridTestActivity";
     private static final boolean DEBUG = true;
@@ -48,7 +38,6 @@
     private static final boolean STAGGERED = true;
 
     private HorizontalGridView mHorizontalGridView;
-    private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
 
     private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
         @Override
@@ -58,7 +47,6 @@
                 Log.v(TAG, "onScrollStateChanged "
                         + (newState < stateNames.length ? stateNames[newState] : newState));
             }
-            mScrollState = newState;
         }
     };
 
@@ -68,12 +56,16 @@
 
         mHorizontalGridView.setWindowAlignment(HorizontalGridView.WINDOW_ALIGN_BOTH_EDGE);
         mHorizontalGridView.setWindowAlignmentOffsetPercent(35);
-        mHorizontalGridView.setOnChildSelectedListener(new OnChildSelectedListener() {
-            @Override
-            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
-                if (DEBUG) Log.d(TAG, "onChildSelected position=" + position +  " id="+id);
-            }
-        });
+        mHorizontalGridView.setOnChildViewHolderSelectedListener(
+                new OnChildViewHolderSelectedListener() {
+                    @Override
+                    public void onChildViewHolderSelected(RecyclerView parent,
+                                                          RecyclerView.ViewHolder child,
+                                                          int position, int subposition) {
+                        if (DEBUG) Log.d(TAG, "onChildSelected position=" + position);
+                    }
+
+                });
         return view;
     }
 
@@ -90,7 +82,7 @@
         mHorizontalGridView.setAdapter(new MyAdapter());
         setContentView(view);
 
-        mHorizontalGridView.setOnScrollListener(mScrollListener);
+        mHorizontalGridView.addOnScrollListener(mScrollListener);
     }
 
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
index 298ef70..a729b79 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/MainActivity.java
@@ -17,6 +17,7 @@
 package com.example.android.leanback;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -24,11 +25,7 @@
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
 import android.support.v4.app.ActivityOptionsCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
+import android.support.v4.content.res.ResourcesCompat;
 import java.util.List;
 
 /**
@@ -36,14 +33,11 @@
  */
 public class MainActivity extends Activity {
 
-    private GuidedStepFragment mGuidedStepFragment;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mGuidedStepFragment = new StepFragment();
-        GuidedStepFragment.addAsRoot(this, mGuidedStepFragment, android.R.id.content);
+        GuidedStepFragment.addAsRoot(this, new StepFragment(), android.R.id.content);
     }
 
     public static class StepFragment extends GuidedStepFragment {
@@ -53,7 +47,9 @@
             String title = getString(R.string.main_title);
             String breadcrumb = getString(R.string.main_breadcrumb);
             String description = "";
-            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
+            final Context context = getActivity();
+            Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
+                    R.drawable.ic_main_icon, context.getTheme());
             return new Guidance(title, description, breadcrumb, icon);
         }
 
@@ -104,6 +100,9 @@
             addAction(actions, DetailsPresenterSelectionActivity.class,
                     R.string.detail_presenter_options,
                     R.string.detail_presenter_options_description);
+            addAction(actions, SettingsActivity.class,
+                    R.string.settings,
+                    R.string.settings_description);
             addAction(actions, OnboardingActivity.class,
                     R.string.onboarding,
                     R.string.onboarding_description);
@@ -113,7 +112,7 @@
         }
 
         private void addAction(List<GuidedAction> actions, Class cls, int titleRes, int descRes) {
-            actions.add(new GuidedAction.Builder()
+            actions.add(new GuidedAction.Builder(getActivity())
                     .intent(new Intent(getActivity(), cls))
                     .title(getString(titleRes))
                     .description(getString(descRes))
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
index 4d04502..7a77766 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
@@ -13,12 +13,16 @@
  */
 package com.example.android.leanback;
 
-import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v17.leanback.app.DetailsBackgroundParallaxHelper;
+import android.support.v17.leanback.app.DetailsFragmentVideoHelper;
+import android.support.v17.leanback.app.MediaPlayerGlue;
+import android.support.v17.leanback.app.VideoFragment;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -36,6 +40,8 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
@@ -48,7 +54,6 @@
     private ArrayObjectAdapter mRowsAdapter;
     private PhotoItem mPhotoItem;
     final CardPresenter cardPresenter = new CardPresenter();
-    private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
 
     private static final int ACTION_PLAY = 1;
     private static final int ACTION_RENT = 2;
@@ -66,9 +71,17 @@
     private Action mActionBuy;
 
     private FullWidthDetailsOverviewSharedElementHelper mHelper;
+    private DetailsBackgroundParallaxHelper mParallaxHelper;
+    private DetailsFragmentVideoHelper mVideoHelper;
+    private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+    private int mBitmapMinVerticalOffset = -100;
+    private MediaPlayerGlue mMediaPlayerGlue;
+    private VideoFragment mVideoFragment;
+    private boolean mSetupDone;
 
     private void initializeTest() {
-        TEST_SHARED_ELEMENT_TRANSITION = null != getActivity().getWindow().getSharedElementEnterTransition();
+        TEST_SHARED_ELEMENT_TRANSITION = null != getActivity().getWindow()
+                .getSharedElementEnterTransition();
         TEST_OVERVIEW_ROW_ON_SECOND = !TEST_SHARED_ELEMENT_TRANSITION;
         TEST_ENTRANCE_TRANSITION = true;
     }
@@ -79,7 +92,9 @@
         super.onCreate(savedInstanceState);
         initializeTest();
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_title,
+                context.getTheme()));
         setTitle("Leanback Sample App");
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -90,8 +105,8 @@
         });
 
         mActionPlay = new Action(ACTION_PLAY, "Play");
-        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99",
-                getResources().getDrawable(R.drawable.ic_action_a));
+        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99", ResourcesCompat.getDrawable(
+                context.getResources(), R.drawable.ic_action_a, context.getTheme()));
         mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
 
         ClassPresenterSelector ps = new ClassPresenterSelector();
@@ -100,7 +115,8 @@
         dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
             @Override
             public void onActionClicked(Action action) {
-                Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
+                final Context context = getActivity();
+                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
                 int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
                 DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(indexOfOverviewRow);
                 if (action.getId() == ACTION_BUY) {
@@ -111,7 +127,8 @@
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
                     dor.setItem(mPhotoItem.getTitle() + "(Owned)");
-                    dor.setImageDrawable(getResources().getDrawable(R.drawable.details_img_16x9));
+                    dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                            R.drawable.details_img_16x9, context.getTheme()));
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -120,7 +137,7 @@
                     actions.clear(ACTION_RENT);
                     dor.setItem(mPhotoItem.getTitle() + "(Rented)");
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class);
+                    Intent intent = new Intent(context, PlaybackOverlayActivity.class);
                     getActivity().startActivity(intent);
                 }
             }
@@ -143,15 +160,15 @@
         setOnItemViewClickedListener(new OnItemViewClickedListener() {
             @Override
             public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemClicked: " + item + " row " + row);
-                if (item instanceof PhotoItem){
+                if (item instanceof PhotoItem) {
                     Intent intent = new Intent(getActivity(), DetailsActivity.class);
                     intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item);
 
                     Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
                             getActivity(),
-                            ((ImageCardView)itemViewHolder.view).getMainImageView(),
+                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                             DetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                     getActivity().startActivity(intent, bundle);
                 }
@@ -160,7 +177,7 @@
         setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
@@ -180,12 +197,7 @@
                 prepareEntranceTransition();
             }
         }
-    }
 
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(ITEM, mPhotoItem);
     }
 
     public void setItem(PhotoItem photoItem) {
@@ -204,9 +216,10 @@
                     mRowsAdapter.add(0, new ListRow(header, listRowAdapter));
                 }
 
-                Resources res = getActivity().getResources();
+                final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
-                dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId()));
+                dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                        mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
                 adapter.set(ACTION_RENT, mActionRent);
                 adapter.set(ACTION_BUY, mActionBuy);
@@ -241,13 +254,40 @@
         setAdapter(mRowsAdapter);
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mPhotoItem != null) {
-            mBackgroundHelper.setBackground(
-                    getActivity(), mPhotoItem.getImageResourceId());
+    void setupHelpers() {
+        if (!mSetupDone) {
+            mParallaxHelper = new DetailsBackgroundParallaxHelper.ParallaxBuilder(
+                    getActivity(), getParallaxManager())
+                    .setCoverImageMinVerticalOffset(mBitmapMinVerticalOffset)
+                    .build();
+            mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+
+            mMediaPlayerGlue.setHost(createPlaybackGlueHost());
+            mVideoHelper = new DetailsFragmentVideoHelper(mMediaPlayerGlue, getParallaxManager());
+            mVideoHelper.setBackgroundDrawable(mParallaxHelper.getCoverImageDrawable());
+            mSetupDone = true;
         }
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
+                R.drawable.spiderman);
+
+        setupHelpers();
+        mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
+        mMediaPlayerGlue.setArtist("A Googleer");
+        mMediaPlayerGlue.setTitle("Diving with Sharks");
+        mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
+
+        mBackgroundHelper.setDrawable(getActivity(), mParallaxHelper.getDrawable());
+        mParallaxHelper.setCoverImageBitmap(bitmap);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mMediaPlayerGlue.pause();
+    }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
index 8a43d8b..79ccb75 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from NewDetailsFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,12 +16,16 @@
  */
 package com.example.android.leanback;
 
-import android.support.v4.app.FragmentActivity;
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v17.leanback.app.DetailsBackgroundParallaxHelper;
+import android.support.v17.leanback.app.DetailsFragmentVideoHelper;
+import android.support.v17.leanback.app.MediaPlayerGlue;
+import android.support.v17.leanback.app.VideoSupportFragment;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -38,6 +43,8 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
@@ -50,7 +57,6 @@
     private ArrayObjectAdapter mRowsAdapter;
     private PhotoItem mPhotoItem;
     final CardPresenter cardPresenter = new CardPresenter();
-    private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
 
     private static final int ACTION_PLAY = 1;
     private static final int ACTION_RENT = 2;
@@ -68,9 +74,17 @@
     private Action mActionBuy;
 
     private FullWidthDetailsOverviewSharedElementHelper mHelper;
+    private DetailsBackgroundParallaxHelper mParallaxHelper;
+    private DetailsFragmentVideoHelper mVideoHelper;
+    private BackgroundHelper mBackgroundHelper = new BackgroundHelper();
+    private int mBitmapMinVerticalOffset = -100;
+    private MediaPlayerGlue mMediaPlayerGlue;
+    private VideoSupportFragment mVideoSupportFragment;
+    private boolean mSetupDone;
 
     private void initializeTest() {
-        TEST_SHARED_ELEMENT_TRANSITION = null != getActivity().getWindow().getSharedElementEnterTransition();
+        TEST_SHARED_ELEMENT_TRANSITION = null != getActivity().getWindow()
+                .getSharedElementEnterTransition();
         TEST_OVERVIEW_ROW_ON_SECOND = !TEST_SHARED_ELEMENT_TRANSITION;
         TEST_ENTRANCE_TRANSITION = true;
     }
@@ -81,7 +95,9 @@
         super.onCreate(savedInstanceState);
         initializeTest();
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_title,
+                context.getTheme()));
         setTitle("Leanback Sample App");
         setOnSearchClickedListener(new View.OnClickListener() {
             @Override
@@ -92,8 +108,8 @@
         });
 
         mActionPlay = new Action(ACTION_PLAY, "Play");
-        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99",
-                getResources().getDrawable(R.drawable.ic_action_a));
+        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99", ResourcesCompat.getDrawable(
+                context.getResources(), R.drawable.ic_action_a, context.getTheme()));
         mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
 
         ClassPresenterSelector ps = new ClassPresenterSelector();
@@ -102,7 +118,8 @@
         dorPresenter.setOnActionClickedListener(new OnActionClickedListener() {
             @Override
             public void onActionClicked(Action action) {
-                Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show();
+                final Context context = getActivity();
+                Toast.makeText(context, action.toString(), Toast.LENGTH_SHORT).show();
                 int indexOfOverviewRow = TEST_OVERVIEW_ROW_ON_SECOND ? 1 : 0;
                 DetailsOverviewRow dor = (DetailsOverviewRow) mRowsAdapter.get(indexOfOverviewRow);
                 if (action.getId() == ACTION_BUY) {
@@ -113,7 +130,8 @@
                     actions.clear(ACTION_RENT);
                     actions.clear(ACTION_BUY);
                     dor.setItem(mPhotoItem.getTitle() + "(Owned)");
-                    dor.setImageDrawable(getResources().getDrawable(R.drawable.details_img_16x9));
+                    dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                            R.drawable.details_img_16x9, context.getTheme()));
                 } else if (action.getId() == ACTION_RENT) {
                     // on the UI thread, we can modify actions adapter directly
                     SparseArrayObjectAdapter actions = (SparseArrayObjectAdapter)
@@ -122,7 +140,7 @@
                     actions.clear(ACTION_RENT);
                     dor.setItem(mPhotoItem.getTitle() + "(Rented)");
                 } else if (action.getId() == ACTION_PLAY) {
-                    Intent intent = new Intent(getActivity(), PlaybackOverlaySupportActivity.class);
+                    Intent intent = new Intent(context, PlaybackOverlaySupportActivity.class);
                     getActivity().startActivity(intent);
                 }
             }
@@ -145,15 +163,15 @@
         setOnItemViewClickedListener(new OnItemViewClickedListener() {
             @Override
             public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemClicked: " + item + " row " + row);
-                if (item instanceof PhotoItem){
+                if (item instanceof PhotoItem) {
                     Intent intent = new Intent(getActivity(), DetailsSupportActivity.class);
                     intent.putExtra(DetailsSupportActivity.EXTRA_ITEM, (PhotoItem) item);
 
                     Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
                             getActivity(),
-                            ((ImageCardView)itemViewHolder.view).getMainImageView(),
+                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                             DetailsSupportActivity.SHARED_ELEMENT_NAME).toBundle();
                     getActivity().startActivity(intent, bundle);
                 }
@@ -162,7 +180,7 @@
         setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
                 Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
@@ -182,12 +200,7 @@
                 prepareEntranceTransition();
             }
         }
-    }
 
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(ITEM, mPhotoItem);
     }
 
     public void setItem(PhotoItem photoItem) {
@@ -206,9 +219,10 @@
                     mRowsAdapter.add(0, new ListRow(header, listRowAdapter));
                 }
 
-                Resources res = getActivity().getResources();
+                final Context context = getActivity();
                 DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
-                dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId()));
+                dor.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                        mPhotoItem.getImageResourceId(), context.getTheme()));
                 SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
                 adapter.set(ACTION_RENT, mActionRent);
                 adapter.set(ACTION_BUY, mActionBuy);
@@ -243,13 +257,40 @@
         setAdapter(mRowsAdapter);
     }
 
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mPhotoItem != null) {
-            mBackgroundHelper.setBackground(
-                    getActivity(), mPhotoItem.getImageResourceId());
+    void setupHelpers() {
+        if (!mSetupDone) {
+            mParallaxHelper = new DetailsBackgroundParallaxHelper.ParallaxBuilder(
+                    getActivity(), getParallaxManager())
+                    .setCoverImageMinVerticalOffset(mBitmapMinVerticalOffset)
+                    .build();
+            mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+
+            mMediaPlayerGlue.setHost(createPlaybackGlueHost());
+            mVideoHelper = new DetailsFragmentVideoHelper(mMediaPlayerGlue, getParallaxManager());
+            mVideoHelper.setBackgroundDrawable(mParallaxHelper.getCoverImageDrawable());
+            mSetupDone = true;
         }
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
+                R.drawable.spiderman);
+
+        setupHelpers();
+        mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
+        mMediaPlayerGlue.setArtist("A Googleer");
+        mMediaPlayerGlue.setTitle("Diving with Sharks");
+        mMediaPlayerGlue.setVideoUrl("http://techslides.com/demos/sample-videos/small.mp4");
+
+        mBackgroundHelper.setDrawable(getActivity(), mParallaxHelper.getDrawable());
+        mParallaxHelper.setCoverImageBitmap(bitmap);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mMediaPlayerGlue.pause();
+    }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoFragment.java
index 5868c26..0aa2e70 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoFragment.java
@@ -51,6 +51,7 @@
 
     private Animator mContentAnimator;
 
+    @SuppressWarnings("deprecation")
     @Override
     public void onAttach(android.app.Activity activity) {
         super.onAttach(activity);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoSupportFragment.java
index 32f38f3..2d150b4 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingDemoSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from OnboardingDemoFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -53,6 +54,7 @@
 
     private Animator mContentAnimator;
 
+    @SuppressWarnings("deprecation")
     @Override
     public void onAttach(android.app.Activity activity) {
         super.onAttach(activity);
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
index e23a7e6..f0a2275 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from OnboardingActivity.java.  DO NOT MODIFY. */
 
 /*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
index e282f83..308c7f4 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.support.v17.leanback.app.MediaPlayerGlue;
 import android.support.v17.leanback.app.PlaybackControlGlue;
+import android.support.v17.leanback.app.PlaybackGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
@@ -31,7 +33,7 @@
 import android.view.View;
 import android.widget.Toast;
 
-abstract class PlaybackControlHelper extends PlaybackControlGlue {
+abstract class PlaybackControlHelper extends MediaPlayerGlue {
     /**
      * Change the location of the thumbs up/down controls
      */
@@ -54,17 +56,17 @@
     private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
     private PlaybackControlsRow.PictureInPictureAction mPipAction;
 
-    private Handler mHandler = new Handler();
+    private static Handler sHandler = new Handler();
     private final Runnable mUpdateProgressRunnable = new Runnable() {
         @Override
         public void run() {
             updateProgress();
-            mHandler.postDelayed(this, getUpdatePeriod());
+            sHandler.postDelayed(this, getUpdatePeriod());
         }
     };
 
-    public PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment) {
-        super(context, fragment, sFastForwardSpeeds);
+    public PlaybackControlHelper(Context context, PlaybackGlue.PlaybackGlueHost host) {
+        super(context, host, sFastForwardSpeeds, sFastForwardSpeeds);
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
         mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
@@ -228,7 +230,7 @@
     }
 
     void onPlaybackComplete(final boolean ended) {
-        mHandler.post(new Runnable() {
+        sHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
@@ -275,9 +277,9 @@
 
     @Override
     public void enableProgressUpdating(boolean enable) {
-        mHandler.removeCallbacks(mUpdateProgressRunnable);
+        sHandler.removeCallbacks(mUpdateProgressRunnable);
         if (enable) {
             mUpdateProgressRunnable.run();
         }
     }
-};
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java
deleted file mode 100644
index a538a44..0000000
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlSupportHelper.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/* This file is auto-generated from PlaybackControlHelper.java.  DO NOT MODIFY. */
-
-/*
- * 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 com.example.android.leanback;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.v17.leanback.app.PlaybackControlSupportGlue;
-import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.Toast;
-
-abstract class PlaybackControlSupportHelper extends PlaybackControlSupportGlue {
-    /**
-     * Change the location of the thumbs up/down controls
-     */
-    private static final boolean THUMBS_PRIMARY = true;
-
-    private static final String FAUX_TITLE = "A short song of silence";
-    private static final String FAUX_SUBTITLE = "2014";
-    private static final int FAUX_DURATION = 33 * 1000;
-
-    // These should match the playback service FF behavior
-    private static int[] sFastForwardSpeeds = { 2, 3, 4, 5 };
-
-    private boolean mIsPlaying;
-    private int mSpeed = PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED;
-    private long mStartTime;
-    private long mStartPosition = 0;
-
-    private PlaybackControlsRow.RepeatAction mRepeatAction;
-    private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
-    private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
-    private PlaybackControlsRow.PictureInPictureAction mPipAction;
-
-    private Handler mHandler = new Handler();
-    private final Runnable mUpdateProgressRunnable = new Runnable() {
-        @Override
-        public void run() {
-            updateProgress();
-            mHandler.postDelayed(this, getUpdatePeriod());
-        }
-    };
-
-    public PlaybackControlSupportHelper(Context context, PlaybackOverlaySupportFragment fragment) {
-        super(context, fragment, sFastForwardSpeeds);
-        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
-        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
-        mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
-        mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
-    }
-
-    @Override
-    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
-        PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
-
-        ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector());
-        getControlsRow().setSecondaryActionsAdapter(adapter);
-        if (!THUMBS_PRIMARY) {
-            adapter.add(mThumbsDownAction);
-        }
-        if (android.os.Build.VERSION.SDK_INT > 23) {
-            adapter.add(mPipAction);
-        }
-        adapter.add(mRepeatAction);
-        if (!THUMBS_PRIMARY) {
-            adapter.add(mThumbsUpAction);
-        }
-
-        return presenter;
-    }
-
-    @Override
-    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
-            PresenterSelector presenterSelector) {
-        SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
-        if (THUMBS_PRIMARY) {
-            adapter.set(PlaybackControlSupportGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction);
-            adapter.set(PlaybackControlSupportGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction);
-        }
-        return adapter;
-    }
-
-    @Override
-    public void onActionClicked(Action action) {
-        if (shouldDispatchAction(action)) {
-            dispatchAction(action);
-            return;
-        }
-        super.onActionClicked(action);
-    }
-
-    @Override
-    public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
-        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
-            Action action = getControlsRow().getActionForKeyCode(keyEvent.getKeyCode());
-            if (shouldDispatchAction(action)) {
-                dispatchAction(action);
-                return true;
-            }
-        }
-        return super.onKey(view, keyCode, keyEvent);
-    }
-
-    private boolean shouldDispatchAction(Action action) {
-        return action == mRepeatAction || action == mThumbsUpAction || action == mThumbsDownAction;
-    }
-
-    private void dispatchAction(Action action) {
-        Toast.makeText(getContext(), action.toString(), Toast.LENGTH_SHORT).show();
-        PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action;
-        multiAction.nextIndex();
-        notifyActionChanged(multiAction);
-    }
-
-    private void notifyActionChanged(PlaybackControlsRow.MultiAction action) {
-        int index;
-        index = getPrimaryActionsAdapter().indexOf(action);
-        if (index >= 0) {
-            getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
-        } else {
-            index = getSecondaryActionsAdapter().indexOf(action);
-            if (index >= 0) {
-                getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
-            }
-        }
-    }
-
-    private SparseArrayObjectAdapter getPrimaryActionsAdapter() {
-        return (SparseArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
-    }
-
-    private ArrayObjectAdapter getSecondaryActionsAdapter() {
-        return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter();
-    }
-
-    @Override
-    public boolean hasValidMedia() {
-        return true;
-    }
-
-    @Override
-    public boolean isMediaPlaying() {
-        return mIsPlaying;
-    }
-
-    @Override
-    public CharSequence getMediaTitle() {
-        return FAUX_TITLE;
-    }
-
-    @Override
-    public CharSequence getMediaSubtitle() {
-        return FAUX_SUBTITLE;
-    }
-
-    @Override
-    public int getMediaDuration() {
-        return FAUX_DURATION;
-    }
-
-    @Override
-    public Drawable getMediaArt() {
-        return null;
-    }
-
-    @Override
-    public long getSupportedActions() {
-        return PlaybackControlSupportGlue.ACTION_PLAY_PAUSE |
-                PlaybackControlSupportGlue.ACTION_FAST_FORWARD |
-                PlaybackControlSupportGlue.ACTION_REWIND;
-    }
-
-    @Override
-    public int getCurrentSpeedId() {
-        return mSpeed;
-    }
-
-    @Override
-    public int getCurrentPosition() {
-        int speed;
-        if (mSpeed == PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED) {
-            speed = 0;
-        } else if (mSpeed == PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL) {
-            speed = 1;
-        } else if (mSpeed >= PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0) {
-            int index = mSpeed - PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0;
-            speed = getFastForwardSpeeds()[index];
-        } else if (mSpeed <= -PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0) {
-            int index = -mSpeed - PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0;
-            speed = -getRewindSpeeds()[index];
-        } else {
-            return -1;
-        }
-        long position = mStartPosition +
-                (System.currentTimeMillis() - mStartTime) * speed;
-        if (position > getMediaDuration()) {
-            position = getMediaDuration();
-            onPlaybackComplete(true);
-        } else if (position < 0) {
-            position = 0;
-            onPlaybackComplete(false);
-        }
-        return (int) position;
-    }
-
-    void onPlaybackComplete(final boolean ended) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
-                    pausePlayback();
-                } else {
-                    startPlayback(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL);
-                }
-                mStartPosition = 0;
-                onStateChanged();
-            }
-        });
-    }
-
-    @Override
-    protected void startPlayback(int speed) {
-        if (speed == mSpeed) {
-            return;
-        }
-        mStartPosition = getCurrentPosition();
-        mSpeed = speed;
-        mIsPlaying = true;
-        mStartTime = System.currentTimeMillis();
-    }
-
-    @Override
-    protected void pausePlayback() {
-        if (mSpeed == PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED) {
-            return;
-        }
-        mStartPosition = getCurrentPosition();
-        mSpeed = PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED;
-        mIsPlaying = false;
-    }
-
-    @Override
-    protected void skipToNext() {
-        // Not supported
-    }
-
-    @Override
-    protected void skipToPrevious() {
-        // Not supported
-    }
-
-    @Override
-    public void enableProgressUpdating(boolean enable) {
-        mHandler.removeCallbacks(mUpdateProgressRunnable);
-        if (enable) {
-            mUpdateProgressRunnable.run();
-        }
-    }
-};
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
index c918774..a76c603 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
@@ -14,43 +14,30 @@
 package com.example.android.leanback;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Handler;
-import android.support.v17.leanback.app.PlaybackControlGlue;
+import android.support.v17.leanback.app.PlaybackFragment;
+import android.support.v17.leanback.app.PlaybackFragmentGlueHost;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction;
-import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction;
-import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.HeaderItem;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.ListRowPresenter;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
 
 public class PlaybackOverlayFragment
-        extends android.support.v17.leanback.app.PlaybackOverlayFragment
+        extends android.support.v17.leanback.app.VideoFragment
         implements PlaybackOverlayActivity.PictureInPictureListener {
     private static final String TAG = "leanback.PlaybackControlsFragment";
 
     /**
      * Change this to choose a different overlay background.
      */
-    private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT;
+    private static final int BACKGROUND_TYPE = PlaybackFragment.BG_LIGHT;
 
     /**
      * Change the number of related content rows.
@@ -68,25 +55,6 @@
     private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter;
     private ListRowPresenter mListRowPresenter;
 
-    private OnItemViewClickedListener mOnItemViewClickedListener = new OnItemViewClickedListener() {
-        @Override
-        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                  RowPresenter.ViewHolder rowViewHolder, Row row) {
-            Log.i(TAG, "onItemClicked: " + item + " row " + row);
-            if (item instanceof Action) {
-                mGlue.onActionClicked((Action) item);
-            }
-        }
-    };
-
-    private OnItemViewSelectedListener mOnItemViewSelectedListener = new OnItemViewSelectedListener() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
-            Log.i(TAG, "onItemSelected: " + item + " row " + row);
-        }
-    };
-
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -97,13 +65,12 @@
         super.onCreate(savedInstanceState);
 
         setBackgroundType(BACKGROUND_TYPE);
-        setOnItemViewSelectedListener(mOnItemViewSelectedListener);
 
         createComponents(getActivity());
     }
 
     private void createComponents(Context context) {
-        mGlue = new PlaybackControlHelper(context, this) {
+        mGlue = new PlaybackControlHelper(context, new PlaybackFragmentGlueHost(this)) {
             @Override
             public int getUpdatePeriod() {
                 int totalTime = getControlsRow().getTotalTime();
@@ -134,7 +101,7 @@
             }
         };
 
-        mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener);
+        //mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
         mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter();
         mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN);
@@ -163,7 +130,6 @@
             HeaderItem header = new HeaderItem(i, "Row " + i);
             getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter));
         }
-
     }
 
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportActivity.java
index e1b01e1..56f1df9 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from PlaybackOverlayActivity.java.  DO NOT MODIFY. */
 
 /*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
index e8ea8f5..109a203 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from PlaybackOverlayFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -16,43 +17,30 @@
 package com.example.android.leanback;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.os.Handler;
-import android.support.v17.leanback.app.PlaybackControlGlue;
+import android.support.v17.leanback.app.PlaybackSupportFragment;
+import android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction;
-import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction;
-import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.HeaderItem;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.ListRowPresenter;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
 
 public class PlaybackOverlaySupportFragment
-        extends android.support.v17.leanback.app.PlaybackOverlaySupportFragment
+        extends android.support.v17.leanback.app.VideoSupportFragment
         implements PlaybackOverlaySupportActivity.PictureInPictureListener {
     private static final String TAG = "leanback.PlaybackControlsFragment";
 
     /**
      * Change this to choose a different overlay background.
      */
-    private static final int BACKGROUND_TYPE = PlaybackOverlaySupportFragment.BG_LIGHT;
+    private static final int BACKGROUND_TYPE = PlaybackSupportFragment.BG_LIGHT;
 
     /**
      * Change the number of related content rows.
@@ -66,29 +54,10 @@
 
     private static final int ROW_CONTROLS = 0;
 
-    private PlaybackControlSupportHelper mGlue;
+    private PlaybackControlHelper mGlue;
     private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter;
     private ListRowPresenter mListRowPresenter;
 
-    private OnItemViewClickedListener mOnItemViewClickedListener = new OnItemViewClickedListener() {
-        @Override
-        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                  RowPresenter.ViewHolder rowViewHolder, Row row) {
-            Log.i(TAG, "onItemClicked: " + item + " row " + row);
-            if (item instanceof Action) {
-                mGlue.onActionClicked((Action) item);
-            }
-        }
-    };
-
-    private OnItemViewSelectedListener mOnItemViewSelectedListener = new OnItemViewSelectedListener() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
-            Log.i(TAG, "onItemSelected: " + item + " row " + row);
-        }
-    };
-
     public SparseArrayObjectAdapter getAdapter() {
         return (SparseArrayObjectAdapter) super.getAdapter();
     }
@@ -99,13 +68,12 @@
         super.onCreate(savedInstanceState);
 
         setBackgroundType(BACKGROUND_TYPE);
-        setOnItemViewSelectedListener(mOnItemViewSelectedListener);
 
         createComponents(getActivity());
     }
 
     private void createComponents(Context context) {
-        mGlue = new PlaybackControlSupportHelper(context, this) {
+        mGlue = new PlaybackControlHelper(context, new PlaybackSupportFragmentGlueHost(this)) {
             @Override
             public int getUpdatePeriod() {
                 int totalTime = getControlsRow().getTotalTime();
@@ -136,7 +104,7 @@
             }
         };
 
-        mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener);
+        //mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
         mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter();
         mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN);
@@ -165,7 +133,6 @@
             HeaderItem header = new HeaderItem(i, "Row " + i);
             getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter));
         }
-
     }
 
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
index c44557d..0565865 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java
@@ -24,7 +24,6 @@
 public class RowsActivity extends Activity
 {
     private RowsFragment mRowsFragment;
-    private TitleHelper mTitleHelper;
 
     /** Called when the activity is first created. */
     @Override
@@ -50,8 +49,8 @@
         });
 
         BrowseFrameLayout frameLayout = (BrowseFrameLayout) findViewById(R.id.rows_frame);
-        mTitleHelper = new TitleHelper(frameLayout, titleView);
-        frameLayout.setOnFocusSearchListener(mTitleHelper.getOnFocusSearchListener());
-        mRowsFragment.setTitleHelper(mTitleHelper);
+        TitleHelper titleHelper = new TitleHelper(frameLayout, titleView);
+        frameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
+        mRowsFragment.setTitleHelper(titleHelper);
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java
index ea524f0..3fa6560 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java
@@ -37,7 +37,6 @@
     // Row heights default to wrap content
     private static final boolean USE_FIXED_ROW_HEIGHT = false;
 
-    private ArrayObjectAdapter mRowsAdapter;
     private TitleHelper mTitleHelper;
 
     public void setTitleHelper(TitleHelper titleHelper) {
@@ -76,7 +75,7 @@
             lrp.setExpandedRowHeight(cardPresenter.getExpandedRowHeight(getActivity()));
         }
 
-        mRowsAdapter = new ArrayObjectAdapter(lrp);
+        ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(lrp);
 
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
@@ -89,10 +88,10 @@
             listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_7));
             listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_8));
             HeaderItem header = new HeaderItem(i, "Row " + i);
-            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+            rowsAdapter.add(new ListRow(header, listRowAdapter));
         }
 
-        setAdapter(mRowsAdapter);
+        setAdapter(rowsAdapter);
     }
 
     private final class ItemViewClickedListener implements OnItemViewClickedListener {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
index 2035806..dfdfad9 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from RowsActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -15,18 +16,17 @@
  */
 package com.example.android.leanback;
 
+import android.support.v4.app.FragmentActivity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.TitleHelper;
 import android.support.v17.leanback.widget.TitleView;
-import android.support.v4.app.FragmentActivity;
 import android.view.View;
 
 public class RowsSupportActivity extends FragmentActivity
 {
     private RowsSupportFragment mRowsSupportFragment;
-    private TitleHelper mTitleHelper;
 
     /** Called when the activity is first created. */
     @Override
@@ -52,8 +52,8 @@
         });
 
         BrowseFrameLayout frameLayout = (BrowseFrameLayout) findViewById(R.id.rows_frame);
-        mTitleHelper = new TitleHelper(frameLayout, titleView);
-        frameLayout.setOnFocusSearchListener(mTitleHelper.getOnFocusSearchListener());
-        mRowsSupportFragment.setTitleHelper(mTitleHelper);
+        TitleHelper titleHelper = new TitleHelper(frameLayout, titleView);
+        frameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
+        mRowsSupportFragment.setTitleHelper(titleHelper);
     }
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportFragment.java
index 6d66f77..39285bc 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from RowsFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -39,7 +40,6 @@
     // Row heights default to wrap content
     private static final boolean USE_FIXED_ROW_HEIGHT = false;
 
-    private ArrayObjectAdapter mRowsAdapter;
     private TitleHelper mTitleHelper;
 
     public void setTitleHelper(TitleHelper titleHelper) {
@@ -78,7 +78,7 @@
             lrp.setExpandedRowHeight(cardPresenter.getExpandedRowHeight(getActivity()));
         }
 
-        mRowsAdapter = new ArrayObjectAdapter(lrp);
+        ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(lrp);
 
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
@@ -91,10 +91,10 @@
             listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_7));
             listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_8));
             HeaderItem header = new HeaderItem(i, "Row " + i);
-            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+            rowsAdapter.add(new ListRow(header, listRowAdapter));
         }
 
-        setAdapter(mRowsAdapter);
+        setAdapter(rowsAdapter);
     }
 
     private final class ItemViewClickedListener implements OnItemViewClickedListener {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchActivity.java
index 374382b..5f69037 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchActivity.java
@@ -26,7 +26,7 @@
     private static boolean DEBUG = true;
 
     /** If using internal speech recognizer, you must have RECORD_AUDIO permission */
-    private static boolean USE_INTERNAL_SPEECH_RECOGNIZER = true;
+    private static final boolean USE_INTERNAL_SPEECH_RECOGNIZER = true;
     private static final int REQUEST_SPEECH = 1;
 
     private SearchFragment mFragment;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchDetailsSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchDetailsSupportActivity.java
index c1d59b2..2ff812e 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchDetailsSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchDetailsSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from SearchDetailsActivity.java.  DO NOT MODIFY. */
 
 /*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java
index b55b82f..7152bae 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java
@@ -1,9 +1,9 @@
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.HeaderItem;
 import android.support.v17.leanback.widget.ImageCardView;
@@ -14,6 +14,8 @@
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -33,7 +35,9 @@
 
         mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Sample App");
         setSearchResultProvider(this);
         setOnItemViewClickedListener(new ItemViewClickedListener());
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportActivity.java
index 25e5cbf..2909dcd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from SearchActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -28,7 +29,7 @@
     private static boolean DEBUG = true;
 
     /** If using internal speech recognizer, you must have RECORD_AUDIO permission */
-    private static boolean USE_INTERNAL_SPEECH_RECOGNIZER = true;
+    private static final boolean USE_INTERNAL_SPEECH_RECOGNIZER = true;
     private static final int REQUEST_SPEECH = 1;
 
     private SearchSupportFragment mFragment;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportFragment.java
index 35c5eb2..0a3baf2 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchSupportFragment.java
@@ -1,11 +1,12 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from SearchFragment.java.  DO NOT MODIFY. */
 
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
-import android.support.v4.app.ActivityOptionsCompat;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.HeaderItem;
 import android.support.v17.leanback.widget.ImageCardView;
@@ -16,6 +17,8 @@
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.res.ResourcesCompat;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -35,7 +38,9 @@
 
         mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Sample App");
         setSearchResultProvider(this);
         setOnItemViewClickedListener(new ItemViewClickedListener());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsActivity.java
similarity index 62%
copy from v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java
copy to samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsActivity.java
index 90366aa..7be2483 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-package android.support.v7.app;
+package com.example.android.leanback;
 
-import android.support.v7.appcompat.test.R;
-import android.support.v7.testutils.BaseTestActivity;
+import android.app.Activity;
+import android.os.Bundle;
 
-public class WindowDecorActionBarActivity extends BaseTestActivity {
+
+public class SettingsActivity extends Activity {
 
     @Override
-    protected int getContentViewLayoutResId() {
-        return R.layout.window_decor_content;
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.settings);
     }
-
 }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsFragment.java
new file mode 100644
index 0000000..9afec59
--- /dev/null
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SettingsFragment.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.leanback;
+
+import android.os.Bundle;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v17.preference.LeanbackPreferenceFragment;
+import android.support.v17.preference.LeanbackSettingsFragment;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.widget.Toast;
+
+import java.util.Arrays;
+
+
+
+public class SettingsFragment extends LeanbackSettingsFragment {
+
+
+    private static final int sPreferenceResId = R.xml.prefs;
+
+    @Override
+    public void onPreferenceStartInitialScreen() {
+        startPreferenceFragment(buildPreferenceFragment(null));
+    }
+
+    @Override
+    public boolean onPreferenceStartFragment(PreferenceFragment preferenceFragment,
+                                             Preference preference) {
+        return false;
+    }
+
+    @Override
+    public boolean onPreferenceStartScreen(PreferenceFragment preferenceFragment,
+                                           PreferenceScreen preferenceScreen) {
+        PreferenceFragment frag = buildPreferenceFragment(preferenceScreen.getKey());
+        frag.setTargetFragment(preferenceFragment, 0);
+        startPreferenceFragment(frag);
+        return true;
+    }
+
+
+    private PreferenceFragment buildPreferenceFragment(String rootKey) {
+        PreferenceFragment fragment = new PrefFragment();
+        Bundle args = new Bundle();
+        args.putString(PreferenceFragment.ARG_PREFERENCE_ROOT, rootKey);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    public static class PrefFragment extends LeanbackPreferenceFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+        }
+
+        @Override
+        public void onCreatePreferences(Bundle bundle, String rootKey) {
+            setPreferencesFromResource(sPreferenceResId, rootKey);
+        }
+
+        @Override
+        public boolean onPreferenceTreeClick(Preference preference) {
+            final String[] keys = {"prefs_wifi_connect_wps", "prefs_date", "prefs_time",
+                    "prefs_date_time_use_timezone", "app_banner_sample_app", "pref_force_stop",
+                    "pref_uninstall", "pref_more_info"};
+            if (Arrays.asList(keys).contains(preference.getKey())) {
+                Toast.makeText(getActivity(), "Implement your own action handler.",
+                        Toast.LENGTH_SHORT).show();
+                return true;
+            }
+            return super.onPreferenceTreeClick(preference);
+        }
+
+    }
+}
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
index 5c80e0b..0302f68 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/StringPresenter.java
@@ -13,7 +13,9 @@
  */
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -23,11 +25,12 @@
 
     public ViewHolder onCreateViewHolder(ViewGroup parent) {
         Log.d(TAG, "onCreateViewHolder");
-        TextView tv = new TextView(parent.getContext());
+        final Context context = parent.getContext();
+        TextView tv = new TextView(context);
         tv.setFocusable(true);
         tv.setFocusableInTouchMode(true);
-        tv.setBackground(
-                parent.getContext().getResources().getDrawable(R.drawable.text_bg));
+        tv.setBackground(ResourcesCompat.getDrawable(context.getResources(), R.drawable.text_bg,
+                context.getTheme()));
         return new ViewHolder(tv);
     }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
index fe664dd..68de793 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java
@@ -13,21 +13,20 @@
  */
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridPresenter;
-import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
 
 public class VerticalGridFragment extends android.support.v17.leanback.app.VerticalGridFragment {
     private static final String TAG = "leanback.VerticalGridFragment";
@@ -52,7 +51,9 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Vertical Grid Demo");
 
         setupFragment();
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportActivity.java
index 0754fb8..7f80b9f 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from VerticalGridActivity.java.  DO NOT MODIFY. */
 
 /*
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
index cd88f5a..e8f0cc8 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridSupportFragment.java
@@ -1,4 +1,5 @@
-/* This file is auto-generated from VerticalGridFragment.  DO NOT MODIFY. */
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from VerticalGridFragment.java.  DO NOT MODIFY. */
 
 /*
  * Copyright (C) 2014 The Android Open Source Project
@@ -15,21 +16,20 @@
  */
 package com.example.android.leanback;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridPresenter;
-import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
 
 public class VerticalGridSupportFragment extends android.support.v17.leanback.app.VerticalGridSupportFragment {
     private static final String TAG = "leanback.VerticalGridSupportFragment";
@@ -54,7 +54,9 @@
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.ic_title));
+        final Context context = getActivity();
+        setBadgeDrawable(ResourcesCompat.getDrawable(context.getResources(),
+                R.drawable.ic_title, context.getTheme()));
         setTitle("Leanback Vertical Grid Demo");
 
         setupFragment();
diff --git a/samples/SupportLeanbackShowcase/app/build.gradle b/samples/SupportLeanbackShowcase/app/build.gradle
index 2b817d3..b2ff15b 100644
--- a/samples/SupportLeanbackShowcase/app/build.gradle
+++ b/samples/SupportLeanbackShowcase/app/build.gradle
@@ -1,13 +1,13 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 'android-N'
-    buildToolsVersion "24.0.0 rc3"
+    compileSdkVersion 24
+    buildToolsVersion "24.0.2"
 
     defaultConfig {
         applicationId "android.support.v17.leanback.supportleanbackshowcase"
-        minSdkVersion '17'
-        targetSdkVersion 'N'
+        minSdkVersion 17
+        targetSdkVersion 24
         versionCode 1
         versionName "1.0"
         multiDexEnabled true
diff --git a/samples/SupportLeanbackShowcase/build.gradle b/samples/SupportLeanbackShowcase/build.gradle
index b8ec678..74f1e76 100644
--- a/samples/SupportLeanbackShowcase/build.gradle
+++ b/samples/SupportLeanbackShowcase/build.gradle
@@ -1,7 +1,7 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 ext {
   // This will be set by local.properties file. By default it
-  // will use the public release version (23.2.1). You can run
+  // will use the public release version (24.0.0). You can run
   // python build-local.py to build the local verison. That script will
   // figure out the local library version based on maven metadata for leanback
   // library and update local.properties file. Gradle build file in turn
@@ -9,7 +9,7 @@
   Properties properties = new Properties()
   properties.load(project.rootProject.file('local.properties').newDataInputStream())
   supportLibVersion = properties.getProperty('LIBRARY_VERSION')
-  supportLibVersion = supportLibVersion ? supportLibVersion : "23.2.1"
+  supportLibVersion = supportLibVersion ? supportLibVersion : "24.0.0"
   localRepo = properties.getProperty('LOCAL_REPO')
 }
 
@@ -18,7 +18,7 @@
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.1.0'
+        classpath 'com.android.tools.build:gradle:2.2.1'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/samples/SupportLeanbackShowcase/gradle/wrapper/gradle-wrapper.properties b/samples/SupportLeanbackShowcase/gradle/wrapper/gradle-wrapper.properties
index fdb8024..5686dee 100644
--- a/samples/SupportLeanbackShowcase/gradle/wrapper/gradle-wrapper.properties
+++ b/samples/SupportLeanbackShowcase/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Thu Sep 01 17:18:32 PDT 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/samples/SupportPercentDemos/Android.mk b/samples/SupportPercentDemos/Android.mk
index e2d4e4a7..54c980f 100644
--- a/samples/SupportPercentDemos/Android.mk
+++ b/samples/SupportPercentDemos/Android.mk
@@ -18,20 +18,15 @@
 # We need to add some special AAPT flags to generate R classes
 # for resources that are included from the libraries.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_PACKAGE_NAME := SupportPercentDemos
 LOCAL_MODULE_TAGS := samples
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 7
 LOCAL_DEX_PREOPT := false
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
         android-support-percent \
         android-support-v4 \
         android-support-v13
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/percent/res
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.percent
 include $(BUILD_PACKAGE)
diff --git a/samples/SupportPreferenceDemos/res/values/strings.xml b/samples/SupportPreferenceDemos/res/values/strings.xml
index 2fe2c61..e6effdf 100644
--- a/samples/SupportPreferenceDemos/res/values/strings.xml
+++ b/samples/SupportPreferenceDemos/res/values/strings.xml
@@ -22,6 +22,12 @@
 
     <string name="root_title">Demo Preferences</string>
 
+    <string name="title_basic_preference">Basic preference</string>
+    <string name="summary_basic_preference">This is a basic preference</string>
+
+    <string name="title_stylish_preference"><b>Very</b> <i>stylish</i> <u>preference</u></string>
+    <string name="summary_stylish_preference">This is a <b>very</b> <i>stylish</i> <u>preference</u></string>
+
     <string name="inline_preferences">In-line preferences</string>
     <string name="dialog_based_preferences">Dialog-based preferences</string>
     <string name="launch_preferences">Launch preferences</string>
diff --git a/samples/SupportPreferenceDemos/res/xml/preferences.xml b/samples/SupportPreferenceDemos/res/xml/preferences.xml
index 0c79349..f469af2 100644
--- a/samples/SupportPreferenceDemos/res/xml/preferences.xml
+++ b/samples/SupportPreferenceDemos/res/xml/preferences.xml
@@ -21,6 +21,16 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:title="@string/root_title">
 
+    <Preference
+        android:key="basic_preference"
+        android:title="@string/title_basic_preference"
+        android:summary="@string/summary_basic_preference" />
+
+    <Preference
+        android:key="stylish_preference"
+        android:title="@string/title_stylish_preference"
+        android:summary="@string/summary_stylish_preference" />
+
     <PreferenceCategory
         android:title="@string/inline_preferences">
 
diff --git a/samples/SupportTransitionDemos/Android.mk b/samples/SupportTransitionDemos/Android.mk
index abc1ad9..6ebc7af 100644
--- a/samples/SupportTransitionDemos/Android.mk
+++ b/samples/SupportTransitionDemos/Android.mk
@@ -18,25 +18,17 @@
 # We need to add some special AAPT flags to generate R classes
 # for resources that are included from the libraries.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_PACKAGE_NAME := SupportTransitionDemos
 LOCAL_MODULE_TAGS := samples
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 14
 LOCAL_DEX_PREOPT := false
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
         android-support-v4 \
         android-support-v7-appcompat \
         android-support-transition
-LOCAL_RESOURCE_DIR = \
-        $(LOCAL_PATH)/res \
-        frameworks/support/v7/appcompat/res \
-        frameworks/support/transition/res
-LOCAL_AAPT_FLAGS := \
-        --auto-add-overlay \
-        --extra-packages android.support.v7.appcompat \
-        --extra-packages android.support.v7.recyclerview \
-        --extra-packages android.support.transition \
-        --no-version-vectors
+LOCAL_AAPT_FLAGS := --no-version-vectors
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 include $(BUILD_PACKAGE)
diff --git a/samples/SupportTransitionDemos/AndroidManifest.xml b/samples/SupportTransitionDemos/AndroidManifest.xml
index 479b0a5..4028f47 100644
--- a/samples/SupportTransitionDemos/AndroidManifest.xml
+++ b/samples/SupportTransitionDemos/AndroidManifest.xml
@@ -37,8 +37,8 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".widget.BasicUsage"
-                  android:label="@string/basic"
+        <activity android:name=".widget.SceneUsage"
+                  android:label="@string/scene"
                   android:theme="@style/Theme.Transition">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -54,5 +54,14 @@
                 <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".widget.BeginDelayedUsage"
+                  android:label="@string/beginDelayed"
+                  android:theme="@style/Theme.Transition">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.support.transition.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/samples/SupportTransitionDemos/res/layout/begin_delayed.xml b/samples/SupportTransitionDemos/res/layout/begin_delayed.xml
new file mode 100644
index 0000000..8aa1bba
--- /dev/null
+++ b/samples/SupportTransitionDemos/res/layout/begin_delayed.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:minHeight="?attr/actionBarSize"
+            android:background="?attr/colorPrimary"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+            android:elevation="4dp"/>
+
+    <FrameLayout
+            android:id="@+id/root"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:padding="16dp">
+
+        <Button
+                android:id="@+id/button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/begin"/>
+
+    </FrameLayout>
+
+</LinearLayout>
diff --git a/samples/SupportTransitionDemos/res/layout/basic_usage.xml b/samples/SupportTransitionDemos/res/layout/scene_usage.xml
similarity index 100%
rename from samples/SupportTransitionDemos/res/layout/basic_usage.xml
rename to samples/SupportTransitionDemos/res/layout/scene_usage.xml
diff --git a/samples/SupportTransitionDemos/res/values/strings.xml b/samples/SupportTransitionDemos/res/values/strings.xml
index bc1029d..9e528cd 100644
--- a/samples/SupportTransitionDemos/res/values/strings.xml
+++ b/samples/SupportTransitionDemos/res/values/strings.xml
@@ -16,7 +16,9 @@
 
 <resources>
     <string name="activity_sample_code">Support Transition Demos</string>
-    <string name="basic">Basic</string>
-    <string name="custom">Custom</string>
+    <string name="scene">Scene</string>
+    <string name="custom">Custom Transition</string>
+    <string name="beginDelayed">Begin Delayed Transition</string>
     <string name="toggle">Toggle</string>
+    <string name="begin">Begin</string>
 </resources>
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java
new file mode 100644
index 0000000..713e76d
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BeginDelayedUsage.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.os.Bundle;
+import android.support.transition.TransitionManager;
+import android.support.v4.view.GravityCompat;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import com.example.android.support.transition.R;
+
+public class BeginDelayedUsage extends TransitionUsageBase {
+
+    private FrameLayout mRoot;
+    private Button mButton;
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.begin_delayed;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mRoot = (FrameLayout) findViewById(R.id.root);
+        mButton = (Button) findViewById(R.id.button);
+        mButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                toggle();
+            }
+        });
+    }
+
+    private void toggle() {
+        TransitionManager.beginDelayedTransition(mRoot);
+        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mButton.getLayoutParams();
+        if ((params.gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) == GravityCompat.END) {
+            params.gravity = params.gravity ^ GravityCompat.END | GravityCompat.START;
+        } else {
+            params.gravity = params.gravity ^ GravityCompat.START | GravityCompat.END;
+        }
+        mButton.setLayoutParams(params);
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/CustomUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/CustomUsage.java
index 2bd9d27..53af776 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/CustomUsage.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/CustomUsage.java
@@ -28,7 +28,7 @@
  * This demonstrates usage of a custom Transition. See {@link ChangeColor} for the actual
  * implementation of a custom Transition.
  */
-public class CustomUsage extends TransitionUsageBase {
+public class CustomUsage extends SceneUsageBase {
 
     private Transition mTransition;
 
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BasicUsage.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsage.java
similarity index 91%
rename from samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BasicUsage.java
rename to samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsage.java
index 11b460e..61e209f 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/BasicUsage.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsage.java
@@ -23,9 +23,9 @@
 import android.view.ViewGroup;
 
 /**
- * This demonstrates basic usage of the Transition API.
+ * This demonstrates basic usage of the Transition Scene.
  */
-public class BasicUsage extends TransitionUsageBase {
+public class SceneUsage extends SceneUsageBase {
 
     @Override
     Scene[] setUpScenes(ViewGroup root) {
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java
new file mode 100644
index 0000000..f2fd38a
--- /dev/null
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/SceneUsageBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.transition.widget;
+
+import android.os.Bundle;
+import android.support.transition.Scene;
+import android.support.transition.TransitionManager;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.example.android.support.transition.R;
+
+abstract class SceneUsageBase extends TransitionUsageBase {
+
+    private Scene[] mScenes;
+
+    private int mCurrentScene;
+
+    abstract Scene[] setUpScenes(ViewGroup root);
+
+    abstract void go(Scene scene);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FrameLayout root = (FrameLayout) findViewById(R.id.root);
+        mScenes = setUpScenes(root);
+        TransitionManager.go(mScenes[0]);
+    }
+
+    @Override
+    int getLayoutResId() {
+        return R.layout.scene_usage;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.basic_usage, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_toggle:
+                mCurrentScene = (mCurrentScene + 1) % mScenes.length;
+                go(mScenes[mCurrentScene]);
+                return true;
+        }
+        return false;
+    }
+
+}
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
index 83c3a29..0a085f2 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
@@ -16,6 +16,7 @@
 
 package com.example.android.support.transition.widget;
 
+import android.support.annotation.LayoutRes;
 import com.example.android.support.transition.R;
 
 import android.os.Bundle;
@@ -33,43 +34,17 @@
  */
 abstract class TransitionUsageBase extends AppCompatActivity {
 
-    protected Scene[] mScenes;
-
-    private int mCurrentScene;
-
-    abstract Scene[] setUpScenes(ViewGroup root);
-
-    abstract void go(Scene scene);
+    @LayoutRes
+    abstract int getLayoutResId();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.basic_usage);
+        setContentView(getLayoutResId());
 
         // Retrieve the Toolbar from our content view, and set it as the action bar
         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
         setSupportActionBar(toolbar);
-
-        FrameLayout root = (FrameLayout) findViewById(R.id.root);
-        mScenes = setUpScenes(root);
-        TransitionManager.go(mScenes[0]);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.basic_usage, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_toggle:
-                mCurrentScene = (mCurrentScene + 1) % mScenes.length;
-                go(mScenes[mCurrentScene]);
-                return true;
-        }
-        return false;
     }
 
 }
diff --git a/samples/SupportVectorDrawable/animated/Android.mk b/samples/SupportVectorDrawable/animated/Android.mk
index 1a34f53..ae01691 100644
--- a/samples/SupportVectorDrawable/animated/Android.mk
+++ b/samples/SupportVectorDrawable/animated/Android.mk
@@ -16,6 +16,8 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_USE_AAPT2 := true
+
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SDK_VERSION := current
@@ -26,13 +28,12 @@
 
 LOCAL_PACKAGE_NAME := SupportAnimatedVectorDrawable
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-animatedvectordrawable \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+        android-support-animatedvectordrawable \
         android-support-vectordrawable \
         android-support-v4
 
-LOCAL_AAPT_FLAGS += --auto-add-overlay \
-        --extra-packages android.support.graphics.drawable \
-        --no-version-vectors
+LOCAL_AAPT_FLAGS += --no-version-vectors
 
 include $(BUILD_PACKAGE)
 
diff --git a/samples/SupportVectorDrawable/static/Android.mk b/samples/SupportVectorDrawable/static/Android.mk
index 88d76c5..bdc102a 100644
--- a/samples/SupportVectorDrawable/static/Android.mk
+++ b/samples/SupportVectorDrawable/static/Android.mk
@@ -1,21 +1,21 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_USE_AAPT2 := true
+
 LOCAL_MODULE_TAGS := samples tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := SupportVectorDrawable
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-vectordrawable android-support-v4
+LOCAL_STATIC_ANDROID_LIBRARIES := android-support-vectordrawable android-support-v4
 
 LOCAL_SDK_VERSION := current
 
 LOCAL_MIN_SDK_VERSION := 7
 
-LOCAL_AAPT_FLAGS += --auto-add-overlay \
-        --extra-packages android.support.graphics.drawable \
-        --no-version-vectors
+LOCAL_AAPT_FLAGS += --no-version-vectors
 
 include $(BUILD_PACKAGE)
 
diff --git a/transition/Android.mk b/transition/Android.mk
index 254b758..c468ef1 100644
--- a/transition/Android.mk
+++ b/transition/Android.mk
@@ -14,84 +14,30 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.  The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between Transition and platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-base
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
-    android-support-v4
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-base
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
-    android-support-v4
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of KitKat APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-ics
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
-    android-support-v4
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Lollipop APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-kitkat
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
-    android-support-v4
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Marshmallow APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-transition-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-api21
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
-    android-support-v4
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
-# The R class is automatically excluded from the generated library.
-# Applications that use this library must specify LOCAL_RESOURCE_DIR
-# in their makefiles to include the resources in their package.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-transition \
+#       android-support-v4
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-transition
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-transition-api23
-LOCAL_JAVA_LIBRARIES := android-support-transition-res \
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,base) \
+    $(call all-java-files-under,ics) \
+    $(call all-java-files-under,kitkat) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,api23) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations \
     android-support-v4
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/transition/api21/android/support/transition/SceneApi21.java b/transition/api21/android/support/transition/SceneApi21.java
index 5c44836..1e8f0ba 100644
--- a/transition/api21/android/support/transition/SceneApi21.java
+++ b/transition/api21/android/support/transition/SceneApi21.java
@@ -16,9 +16,13 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
+@RequiresApi(21)
+@TargetApi(21)
 class SceneApi21 extends SceneWrapper {
 
     @Override
diff --git a/transition/api21/android/support/transition/SceneStaticsApi21.java b/transition/api21/android/support/transition/SceneStaticsApi21.java
index d6c8a28..547ca70 100644
--- a/transition/api21/android/support/transition/SceneStaticsApi21.java
+++ b/transition/api21/android/support/transition/SceneStaticsApi21.java
@@ -16,9 +16,13 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
+@RequiresApi(21)
+@TargetApi(21)
 class SceneStaticsApi21 extends SceneStaticsImpl {
 
     @Override
diff --git a/transition/api23/android/support/transition/TransitionApi23.java b/transition/api23/android/support/transition/TransitionApi23.java
index 3f06f43..0df0ec5 100644
--- a/transition/api23/android/support/transition/TransitionApi23.java
+++ b/transition/api23/android/support/transition/TransitionApi23.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(23)
+@TargetApi(23)
 class TransitionApi23 extends TransitionKitKat {
 
     @Override
diff --git a/transition/build.gradle b/transition/build.gradle
index faf8483..4d4e96f 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -3,6 +3,7 @@
 archivesBaseName = 'transition'
 
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-v4')
 
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
@@ -27,10 +28,18 @@
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['base', 'ics', 'kitkat', 'api21', 'api23', 'src']
-        main.res.srcDirs 'res', 'res-public'
-        main.assets.srcDir 'assets'
-        main.resources.srcDir 'src'
+        main.java.srcDirs = [
+                'base',
+                'ics',
+                'kitkat',
+                'api21',
+                'api23',
+                'src'
+        ]
+        main.res.srcDirs = [
+                'res',
+                'res-public'
+        ]
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/src'
diff --git a/transition/ics/android/support/transition/AutoTransitionPort.java b/transition/ics/android/support/transition/AutoTransitionPort.java
index 071813f..f3d4583 100644
--- a/transition/ics/android/support/transition/AutoTransitionPort.java
+++ b/transition/ics/android/support/transition/AutoTransitionPort.java
@@ -16,6 +16,9 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
 /**
  * Utility class for creating a default transition that automatically fades,
  * moves, and resizes views during a scene change.
@@ -24,6 +27,8 @@
  * tag <code>autoTransition</code>, along with the other standard
  * attributes of {@link android.R.styleable#Transition}.</p>
  */
+@RequiresApi(14)
+@TargetApi(14)
 class AutoTransitionPort extends TransitionSetPort {
 
     /**
diff --git a/transition/ics/android/support/transition/ChangeBoundsIcs.java b/transition/ics/android/support/transition/ChangeBoundsIcs.java
index 4f52c7d..61b7ac1 100644
--- a/transition/ics/android/support/transition/ChangeBoundsIcs.java
+++ b/transition/ics/android/support/transition/ChangeBoundsIcs.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
 class ChangeBoundsIcs extends TransitionIcs implements ChangeBoundsInterface {
 
     public ChangeBoundsIcs(TransitionInterface transition) {
diff --git a/transition/ics/android/support/transition/ChangeBoundsPort.java b/transition/ics/android/support/transition/ChangeBoundsPort.java
index 6151018..d9db2c7 100644
--- a/transition/ics/android/support/transition/ChangeBoundsPort.java
+++ b/transition/ics/android/support/transition/ChangeBoundsPort.java
@@ -20,10 +20,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -37,6 +39,8 @@
  * tag <code>changeBounds</code>, along with the other standard
  * attributes of {@link android.R.styleable#Transition}.</p>
  */
+@RequiresApi(14)
+@TargetApi(14)
 class ChangeBoundsPort extends TransitionPort {
 
     private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
diff --git a/transition/ics/android/support/transition/FadeIcs.java b/transition/ics/android/support/transition/FadeIcs.java
index 28ed45d..ead8c00 100644
--- a/transition/ics/android/support/transition/FadeIcs.java
+++ b/transition/ics/android/support/transition/FadeIcs.java
@@ -17,8 +17,12 @@
 package android.support.transition;
 
 import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class FadeIcs extends TransitionIcs implements VisibilityImpl {
 
     public FadeIcs(TransitionInterface transition) {
diff --git a/transition/ics/android/support/transition/FadePort.java b/transition/ics/android/support/transition/FadePort.java
index c131449..79673f5 100644
--- a/transition/ics/android/support/transition/FadePort.java
+++ b/transition/ics/android/support/transition/FadePort.java
@@ -19,7 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.View;
@@ -55,6 +56,8 @@
  * attributes of {@link android.R.styleable#Fade} and
  * {@link android.R.styleable#Transition}.</p>
  */
+@RequiresApi(14)
+@TargetApi(14)
 class FadePort extends VisibilityPort {
 
     /**
diff --git a/transition/ics/android/support/transition/RectEvaluator.java b/transition/ics/android/support/transition/RectEvaluator.java
index e0892e5..c85ed18 100644
--- a/transition/ics/android/support/transition/RectEvaluator.java
+++ b/transition/ics/android/support/transition/RectEvaluator.java
@@ -17,11 +17,15 @@
 package android.support.transition;
 
 import android.animation.TypeEvaluator;
+import android.annotation.TargetApi;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 
 /**
  * This evaluator can be used to perform type interpolation between <code>Rect</code> values.
  */
+@RequiresApi(14)
+@TargetApi(14)
 class RectEvaluator implements TypeEvaluator<Rect> {
 
     /**
diff --git a/transition/ics/android/support/transition/SceneIcs.java b/transition/ics/android/support/transition/SceneIcs.java
index 0d0d84e..01e0508 100644
--- a/transition/ics/android/support/transition/SceneIcs.java
+++ b/transition/ics/android/support/transition/SceneIcs.java
@@ -16,9 +16,13 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class SceneIcs extends SceneImpl {
 
     /* package */ ScenePort mScene;
diff --git a/transition/ics/android/support/transition/ScenePort.java b/transition/ics/android/support/transition/ScenePort.java
index 6fc5c25..34b2d7b 100644
--- a/transition/ics/android/support/transition/ScenePort.java
+++ b/transition/ics/android/support/transition/ScenePort.java
@@ -16,7 +16,9 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,6 +30,8 @@
  * animate the various property changes that take place during the
  * scene change.
  */
+@RequiresApi(14)
+@TargetApi(14)
 final class ScenePort {
 
     Runnable mEnterAction, mExitAction;
diff --git a/transition/ics/android/support/transition/SceneStaticsIcs.java b/transition/ics/android/support/transition/SceneStaticsIcs.java
index 87f39e2..bcc7451 100644
--- a/transition/ics/android/support/transition/SceneStaticsIcs.java
+++ b/transition/ics/android/support/transition/SceneStaticsIcs.java
@@ -16,9 +16,13 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class SceneStaticsIcs extends SceneStaticsImpl {
 
     @Override
diff --git a/transition/ics/android/support/transition/TransitionIcs.java b/transition/ics/android/support/transition/TransitionIcs.java
index 8cd5fae..832b59e 100644
--- a/transition/ics/android/support/transition/TransitionIcs.java
+++ b/transition/ics/android/support/transition/TransitionIcs.java
@@ -18,12 +18,16 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionIcs extends TransitionImpl {
 
     /* package */ TransitionPort mTransition;
diff --git a/transition/ics/android/support/transition/TransitionManagerIcs.java b/transition/ics/android/support/transition/TransitionManagerIcs.java
index 19b0175..d277ae7 100644
--- a/transition/ics/android/support/transition/TransitionManagerIcs.java
+++ b/transition/ics/android/support/transition/TransitionManagerIcs.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionManagerIcs extends TransitionManagerImpl {
 
     private final TransitionManagerPort mTransitionManager = new TransitionManagerPort();
diff --git a/transition/ics/android/support/transition/TransitionManagerPort.java b/transition/ics/android/support/transition/TransitionManagerPort.java
index 47eaff0..3acad62 100644
--- a/transition/ics/android/support/transition/TransitionManagerPort.java
+++ b/transition/ics/android/support/transition/TransitionManagerPort.java
@@ -16,6 +16,8 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.view.ViewCompat;
@@ -29,6 +31,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionManagerPort {
     // TODO: how to handle enter/exit?
 
diff --git a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java b/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
index 656ff47..aab7083 100644
--- a/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
+++ b/transition/ics/android/support/transition/TransitionManagerStaticsIcs.java
@@ -16,8 +16,12 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionManagerStaticsIcs extends TransitionManagerStaticsImpl {
 
     @Override
diff --git a/transition/ics/android/support/transition/TransitionPort.java b/transition/ics/android/support/transition/TransitionPort.java
index 516f93b..4e6a9d7 100644
--- a/transition/ics/android/support/transition/TransitionPort.java
+++ b/transition/ics/android/support/transition/TransitionPort.java
@@ -19,6 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.LongSparseArray;
@@ -33,6 +35,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+@RequiresApi(14)
+@TargetApi(14)
 abstract class TransitionPort implements Cloneable {
 
     static final boolean DBG = false;
diff --git a/transition/ics/android/support/transition/TransitionSetIcs.java b/transition/ics/android/support/transition/TransitionSetIcs.java
index 744033e..b3fcd8f 100644
--- a/transition/ics/android/support/transition/TransitionSetIcs.java
+++ b/transition/ics/android/support/transition/TransitionSetIcs.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionSetIcs extends TransitionIcs implements TransitionSetImpl {
 
     private TransitionSetPort mTransitionSet;
diff --git a/transition/ics/android/support/transition/TransitionSetPort.java b/transition/ics/android/support/transition/TransitionSetPort.java
index a834bca..1d5ddd6 100644
--- a/transition/ics/android/support/transition/TransitionSetPort.java
+++ b/transition/ics/android/support/transition/TransitionSetPort.java
@@ -17,6 +17,8 @@
 package android.support.transition;
 
 import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.util.AndroidRuntimeException;
 import android.view.View;
@@ -26,6 +28,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionSetPort extends TransitionPort {
 
     /**
diff --git a/transition/ics/android/support/transition/TransitionValuesMaps.java b/transition/ics/android/support/transition/TransitionValuesMaps.java
index e1274ec..ddf05da 100644
--- a/transition/ics/android/support/transition/TransitionValuesMaps.java
+++ b/transition/ics/android/support/transition/TransitionValuesMaps.java
@@ -16,11 +16,15 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.LongSparseArray;
 import android.util.SparseArray;
 import android.view.View;
 
+@RequiresApi(14)
+@TargetApi(14)
 class TransitionValuesMaps {
 
     public ArrayMap<View, TransitionValues> viewValues = new ArrayMap<>();
diff --git a/transition/ics/android/support/transition/ViewGroupOverlay.java b/transition/ics/android/support/transition/ViewGroupOverlay.java
index 07c9853..da91466 100644
--- a/transition/ics/android/support/transition/ViewGroupOverlay.java
+++ b/transition/ics/android/support/transition/ViewGroupOverlay.java
@@ -16,11 +16,15 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class ViewGroupOverlay extends ViewOverlay {
 
     ViewGroupOverlay(Context context, ViewGroup hostView, View requestingView) {
diff --git a/transition/ics/android/support/transition/ViewOverlay.java b/transition/ics/android/support/transition/ViewOverlay.java
index ef6793c..a95d8f1 100644
--- a/transition/ics/android/support/transition/ViewOverlay.java
+++ b/transition/ics/android/support/transition/ViewOverlay.java
@@ -17,10 +17,12 @@
 package android.support.transition;
 
 import android.R;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ViewCompat;
 import android.view.MotionEvent;
@@ -34,6 +36,8 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+@RequiresApi(14)
+@TargetApi(14)
 class ViewOverlay {
 
     /**
diff --git a/transition/ics/android/support/transition/VisibilityIcs.java b/transition/ics/android/support/transition/VisibilityIcs.java
index 28c6e61..f2290cf 100644
--- a/transition/ics/android/support/transition/VisibilityIcs.java
+++ b/transition/ics/android/support/transition/VisibilityIcs.java
@@ -17,8 +17,12 @@
 package android.support.transition;
 
 import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.ViewGroup;
 
+@RequiresApi(14)
+@TargetApi(14)
 class VisibilityIcs extends TransitionIcs implements VisibilityImpl {
 
     @Override
diff --git a/transition/ics/android/support/transition/VisibilityPort.java b/transition/ics/android/support/transition/VisibilityPort.java
index ebe5cb8..e740b19 100644
--- a/transition/ics/android/support/transition/VisibilityPort.java
+++ b/transition/ics/android/support/transition/VisibilityPort.java
@@ -17,6 +17,8 @@
 package android.support.transition;
 
 import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -31,6 +33,8 @@
  * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)},
  * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)},
  */
+@RequiresApi(14)
+@TargetApi(14)
 abstract class VisibilityPort extends TransitionPort {
 
     private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
diff --git a/transition/ics/android/support/transition/WindowIdPort.java b/transition/ics/android/support/transition/WindowIdPort.java
index f768b2e..148332e 100644
--- a/transition/ics/android/support/transition/WindowIdPort.java
+++ b/transition/ics/android/support/transition/WindowIdPort.java
@@ -16,8 +16,10 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
 import android.os.IBinder;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 
@@ -27,6 +29,8 @@
  * <p>Since the use of WindowId in Transition API is limited to identifying windows, we can just
  * wrap a window token and use it as an identifier.</p>
  */
+@RequiresApi(14)
+@TargetApi(14)
 class WindowIdPort {
 
     private final IBinder mToken;
diff --git a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java b/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
index 704c32f..e8575d4 100644
--- a/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
+++ b/transition/kitkat/android/support/transition/ChangeBoundsKitKat.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class ChangeBoundsKitKat extends TransitionKitKat implements ChangeBoundsInterface {
 
     public ChangeBoundsKitKat(TransitionInterface transition) {
diff --git a/transition/kitkat/android/support/transition/FadeKitKat.java b/transition/kitkat/android/support/transition/FadeKitKat.java
index 7edd220..26992af 100644
--- a/transition/kitkat/android/support/transition/FadeKitKat.java
+++ b/transition/kitkat/android/support/transition/FadeKitKat.java
@@ -19,6 +19,11 @@
 import android.animation.Animator;
 import android.view.ViewGroup;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class FadeKitKat extends TransitionKitKat implements VisibilityImpl {
 
     public FadeKitKat(TransitionInterface transition) {
diff --git a/transition/kitkat/android/support/transition/SceneKitKat.java b/transition/kitkat/android/support/transition/SceneKitKat.java
index 50d3c47..ae7ad10 100644
--- a/transition/kitkat/android/support/transition/SceneKitKat.java
+++ b/transition/kitkat/android/support/transition/SceneKitKat.java
@@ -23,6 +23,11 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class SceneKitKat extends SceneWrapper {
 
     private static Field sEnterAction;
diff --git a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java b/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
index b6e3659..94868d0 100644
--- a/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
+++ b/transition/kitkat/android/support/transition/SceneStaticsKitKat.java
@@ -19,6 +19,11 @@
 import android.content.Context;
 import android.view.ViewGroup;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class SceneStaticsKitKat extends SceneStaticsImpl {
 
     @Override
diff --git a/transition/kitkat/android/support/transition/SceneWrapper.java b/transition/kitkat/android/support/transition/SceneWrapper.java
index 982db19..cad5db9 100644
--- a/transition/kitkat/android/support/transition/SceneWrapper.java
+++ b/transition/kitkat/android/support/transition/SceneWrapper.java
@@ -18,6 +18,11 @@
 
 import android.view.ViewGroup;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 abstract class SceneWrapper extends SceneImpl {
 
     /* package */ android.transition.Scene mScene;
diff --git a/transition/kitkat/android/support/transition/TransitionKitKat.java b/transition/kitkat/android/support/transition/TransitionKitKat.java
index e9e60e5..c608f66 100644
--- a/transition/kitkat/android/support/transition/TransitionKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionKitKat.java
@@ -25,6 +25,11 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class TransitionKitKat extends TransitionImpl {
 
     /* package */ android.transition.Transition mTransition;
diff --git a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
index 9c33cba..3326600 100644
--- a/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionManagerKitKat.java
@@ -18,6 +18,11 @@
 
 import android.transition.TransitionManager;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class TransitionManagerKitKat extends TransitionManagerImpl {
 
     private final android.transition.TransitionManager mTransitionManager = new TransitionManager();
diff --git a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java b/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
index 6c2b618..e7c927b 100644
--- a/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionManagerStaticsKitKat.java
@@ -18,6 +18,11 @@
 
 import android.view.ViewGroup;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class TransitionManagerStaticsKitKat extends TransitionManagerStaticsImpl {
 
     @Override
diff --git a/transition/kitkat/android/support/transition/TransitionSetKitKat.java b/transition/kitkat/android/support/transition/TransitionSetKitKat.java
index 591df63..f880d71 100644
--- a/transition/kitkat/android/support/transition/TransitionSetKitKat.java
+++ b/transition/kitkat/android/support/transition/TransitionSetKitKat.java
@@ -16,6 +16,11 @@
 
 package android.support.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class TransitionSetKitKat extends TransitionKitKat implements TransitionSetImpl {
 
     private android.transition.TransitionSet mTransitionSet;
diff --git a/transition/kitkat/android/support/transition/VisibilityKitKat.java b/transition/kitkat/android/support/transition/VisibilityKitKat.java
index 8437a76..ca603ae 100644
--- a/transition/kitkat/android/support/transition/VisibilityKitKat.java
+++ b/transition/kitkat/android/support/transition/VisibilityKitKat.java
@@ -19,6 +19,11 @@
 import android.animation.Animator;
 import android.view.ViewGroup;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(19)
+@TargetApi(19)
 class VisibilityKitKat extends TransitionKitKat implements VisibilityImpl {
 
     @Override
diff --git a/transition/tests/src/android/support/transition/FadeTest.java b/transition/tests/src/android/support/transition/FadeTest.java
index 0ebd6cc..ba6440c 100644
--- a/transition/tests/src/android/support/transition/FadeTest.java
+++ b/transition/tests/src/android/support/transition/FadeTest.java
@@ -22,9 +22,8 @@
 import static org.junit.Assert.assertThat;
 
 import android.animation.Animator;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -37,16 +36,12 @@
     private View mView;
     private ViewGroup mRoot;
 
+    @UiThreadTest
     @Before
     public void setUp() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRoot = rule.getActivity().getRoot();
-                mView = new View(rule.getActivity());
-                mRoot.addView(mView, new ViewGroup.LayoutParams(100, 100));
-            }
-        });
+        mRoot = rule.getActivity().getRoot();
+        mView = new View(rule.getActivity());
+        mRoot.addView(mView, new ViewGroup.LayoutParams(100, 100));
     }
 
     @Test
diff --git a/transition/tests/src/android/support/transition/SceneTest.java b/transition/tests/src/android/support/transition/SceneTest.java
index 6333d59..f192327 100644
--- a/transition/tests/src/android/support/transition/SceneTest.java
+++ b/transition/tests/src/android/support/transition/SceneTest.java
@@ -16,18 +16,18 @@
 
 package android.support.transition;
 
-import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.sameInstance;
 
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
 import android.support.transition.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.sameInstance;
+import org.junit.Test;
 
 @MediumTest
 public class SceneTest extends BaseTest {
diff --git a/transition/tests/src/android/support/transition/TransitionActivity.java b/transition/tests/src/android/support/transition/TransitionActivity.java
index 253e11a..ff9dbcc 100644
--- a/transition/tests/src/android/support/transition/TransitionActivity.java
+++ b/transition/tests/src/android/support/transition/TransitionActivity.java
@@ -16,13 +16,13 @@
 
 package android.support.transition;
 
+import android.app.Activity;
 import android.os.Bundle;
 import android.support.transition.test.R;
-import android.support.v4.app.FragmentActivity;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-public class TransitionActivity extends FragmentActivity {
+public class TransitionActivity extends Activity {
 
     private FrameLayout mRoot;
 
diff --git a/transition/tests/src/android/support/transition/TransitionManagerTest.java b/transition/tests/src/android/support/transition/TransitionManagerTest.java
index e07f333..21bf413 100644
--- a/transition/tests/src/android/support/transition/TransitionManagerTest.java
+++ b/transition/tests/src/android/support/transition/TransitionManagerTest.java
@@ -21,10 +21,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.sameInstance;
 
-import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
 import android.support.transition.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.ViewGroup;
 
 import org.junit.Before;
@@ -63,12 +62,12 @@
     }
 
     @Test
-    public void testGo_exitAction() {
+    public void testGo_exitAction() throws Throwable {
         final CheckCalledRunnable enter = new CheckCalledRunnable();
         final CheckCalledRunnable exit = new CheckCalledRunnable();
         mScenes[0].setEnterAction(enter);
         mScenes[0].setExitAction(exit);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        rule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertThat(enter.wasCalled(), is(false));
@@ -79,7 +78,7 @@
             }
         });
         // Let the main thread catch up with the scene change
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        rule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 TransitionManager.go(mScenes[1]);
@@ -89,10 +88,10 @@
     }
 
     @Test
-    public void testGo_transitionListenerStart() {
-        final SyncTransitionListener listener
-                = new SyncTransitionListener(SyncTransitionListener.EVENT_START);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testGo_transitionListenerStart() throws Throwable {
+        final SyncTransitionListener listener =
+                new SyncTransitionListener(SyncTransitionListener.EVENT_START);
+        rule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Transition transition = new AutoTransition();
@@ -106,10 +105,10 @@
     }
 
     @Test
-    public void testGo_transitionListenerEnd() {
-        final SyncTransitionListener listener
-                = new SyncTransitionListener(SyncTransitionListener.EVENT_END);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+    public void testGo_transitionListenerEnd() throws Throwable {
+        final SyncTransitionListener listener =
+                new SyncTransitionListener(SyncTransitionListener.EVENT_END);
+        rule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Transition transition = new AutoTransition();
diff --git a/transition/tests/src/android/support/transition/TransitionTest.java b/transition/tests/src/android/support/transition/TransitionTest.java
index 1fe9474..0e87629 100644
--- a/transition/tests/src/android/support/transition/TransitionTest.java
+++ b/transition/tests/src/android/support/transition/TransitionTest.java
@@ -25,8 +25,8 @@
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
 import android.support.transition.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
diff --git a/v13/Android.mk b/v13/Android.mk
index e1e6062..1b30d99 100644
--- a/v13/Android.mk
+++ b/v13/Android.mk
@@ -14,63 +14,34 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Note: the source code is in java/, not src/, because this code is also part of
-# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-ics
-LOCAL_SDK_VERSION := 14
-LOCAL_SRC_FILES := $(call all-java-files-under, ics)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Ice Cream Sandwich APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-ics-mr1
-LOCAL_SDK_VERSION := 15
-LOCAL_SRC_FILES := $(call all-java-files-under, ics-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-ics
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of MNC APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-mnc
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-ics-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of NYC APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-nyc
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-mnc
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of NYC MR-1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v13-nyc-mr1
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, api25)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13-nyc
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+#   LOCAL_STATIC_ANDROID_LIBRARIES := \
+#       android-support-v13 \
+#       android-support-v4
+#
+# in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v13
-LOCAL_SDK_VERSION := 13
-LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+        $(call all-java-files-under, ics) \
+        $(call all-java-files-under, ics-mr1) \
+        $(call all-java-files-under, api23) \
+        $(call all-java-files-under, api24) \
+        $(call all-java-files-under, api25) \
+        $(call all-java-files-under, java)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 \
-        android-support-v13-nyc-mr1
+# Some projects expect to inherit android-support-v4 from
+# android-support-v13, so we need to keep it static until they can be fixed.
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+        android-support-v4
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+        android-support-v4 \
+        android-support-annotations
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/v13/api23/android/support/v13/app/FragmentCompat23.java b/v13/api23/android/support/v13/app/FragmentCompat23.java
index 5027240..1364e84 100644
--- a/v13/api23/android/support/v13/app/FragmentCompat23.java
+++ b/v13/api23/android/support/v13/app/FragmentCompat23.java
@@ -16,8 +16,12 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(23)
+@TargetApi(23)
 class FragmentCompat23 {
     public static void requestPermissions(Fragment fragment, String[] permissions,
             int requestCode) {
diff --git a/v13/api24/android/support/v13/app/FragmentCompatApi24.java b/v13/api24/android/support/v13/app/FragmentCompatApi24.java
index 4baf02a..2b41d25 100644
--- a/v13/api24/android/support/v13/app/FragmentCompatApi24.java
+++ b/v13/api24/android/support/v13/app/FragmentCompatApi24.java
@@ -17,8 +17,12 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(24)
+@TargetApi(24)
 class FragmentCompatApi24 {
     public static void setUserVisibleHint(Fragment f, boolean isVisible) {
         f.setUserVisibleHint(isVisible);
diff --git a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java b/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
index 363cf8d..fd38b79 100644
--- a/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
+++ b/v13/api24/android/support/v13/view/DragAndDropPermissionsCompatApi24.java
@@ -16,10 +16,14 @@
 
 package android.support.v13.view;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
+import android.support.annotation.RequiresApi;
 import android.view.DragAndDropPermissions;
 import android.view.DragEvent;
 
+@RequiresApi(24)
+@TargetApi(24)
 class DragAndDropPermissionsCompatApi24 {
     public static Object request(Activity activity, DragEvent dragEvent) {
         return activity.requestDragAndDropPermissions(dragEvent);
diff --git a/v13/api24/android/support/v13/view/ViewCompatApi24.java b/v13/api24/android/support/v13/view/ViewCompatApi24.java
index 725cad0..422dbb9 100644
--- a/v13/api24/android/support/v13/view/ViewCompatApi24.java
+++ b/v13/api24/android/support/v13/view/ViewCompatApi24.java
@@ -16,9 +16,13 @@
 
 package android.support.v13.view;
 
+import android.annotation.TargetApi;
 import android.content.ClipData;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(24)
+@TargetApi(24)
 class ViewCompatApi24 {
     public static boolean startDragAndDrop(View v, ClipData data,
            View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
diff --git a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
index e5d20a5..3eef2ba 100644
--- a/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/EditorInfoCompatApi25.java
@@ -16,9 +16,12 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.os.Bundle;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.inputmethod.EditorInfo;
 
+@RequiresApi(25)
+@TargetApi(25)
 final class EditorInfoCompatApi25 {
     public static void setContentMimeTypes(EditorInfo editorInfo, String[] contentMimeTypes) {
         editorInfo.contentMimeTypes = contentMimeTypes;
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
index a29bedd..41de756 100644
--- a/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/InputConnectionCompatApi25.java
@@ -16,12 +16,15 @@
 
 package android.support.v13.view.inputmethod;
 
+import android.annotation.TargetApi;
 import android.os.Bundle;
-import android.view.inputmethod.BaseInputConnection;
+import android.support.annotation.RequiresApi;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
 import android.view.inputmethod.InputContentInfo;
 
+@RequiresApi(25)
+@TargetApi(25)
 final class InputConnectionCompatApi25 {
 
     public static boolean commitContent(InputConnection ic, Object inputContentInfo, int flags,
diff --git a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java b/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
index c485ca6..c51199e 100644
--- a/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
+++ b/v13/api25/android/support/v13/view/inputmethod/InputContentInfoCompatApi25.java
@@ -16,11 +16,14 @@
 
 package android.support.v13.view.inputmethod;
 
+import android.annotation.TargetApi;
 import android.content.ClipDescription;
 import android.net.Uri;
-import android.os.Parcel;
+import android.support.annotation.RequiresApi;
 import android.view.inputmethod.InputContentInfo;
 
+@RequiresApi(25)
+@TargetApi(25)
 final class InputContentInfoCompatApi25 {
 
     public static Object create(Uri contentUri, ClipDescription description, Uri linkUri) {
diff --git a/v13/build.gradle b/v13/build.gradle
index 4baaa27..5e1a026 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -1,33 +1,28 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'support-v13'
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
+dependencies {
+    compile project(':support-annotations')
+    compile project(':support-v4')
+}
 
-createApiSourceSets(project, gradle.ext.studioCompat.modules.v13.apiTargets)
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.v13.dependencies)
 android {
-    compileSdkVersion 13
+    compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 13
-        // TODO: get target from branch
-        //targetSdkVersion 19
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['java']
-        main.aidl.srcDirs = ['java']
-
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/java'
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
+        main.java.srcDirs = [
+                'ics',
+                'ics-mr1',
+                'api23',
+                'api24',
+                'api25',
+                'java'
+        ]
     }
 
     lintOptions {
@@ -70,11 +65,6 @@
         from android.sourceSets.main.java.srcDirs
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java b/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
index ea40376..2da10ae 100644
--- a/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
+++ b/v13/ics-mr1/android/support/v13/app/FragmentCompatICSMR1.java
@@ -16,8 +16,12 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(15)
+@TargetApi(15)
 class FragmentCompatICSMR1 {
     public static void setUserVisibleHint(Fragment f, boolean isVisible) {
         if (f.getFragmentManager() != null) {
diff --git a/v13/ics/android/support/v13/app/FragmentCompatICS.java b/v13/ics/android/support/v13/app/FragmentCompatICS.java
index 1beb704..ff40337 100644
--- a/v13/ics/android/support/v13/app/FragmentCompatICS.java
+++ b/v13/ics/android/support/v13/app/FragmentCompatICS.java
@@ -16,8 +16,12 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(14)
+@TargetApi(14)
 class FragmentCompatICS {
     public static void setMenuVisibility(Fragment f, boolean visible) {
         f.setMenuVisibility(visible);
diff --git a/v13/java/android/support/v13/app/ActivityCompat.java b/v13/java/android/support/v13/app/ActivityCompat.java
index 8059a68..8f9a767 100644
--- a/v13/java/android/support/v13/app/ActivityCompat.java
+++ b/v13/java/android/support/v13/app/ActivityCompat.java
@@ -16,7 +16,9 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
+import android.support.annotation.RequiresApi;
 import android.support.v13.view.DragAndDropPermissionsCompat;
 import android.view.DragEvent;
 
@@ -24,6 +26,8 @@
  * Helper for accessing features in {@link android.app.Activity}
  * introduced after API level 13 in a backwards compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public class ActivityCompat extends android.support.v4.app.ActivityCompat {
 
     /**
diff --git a/v13/java/android/support/v13/app/FragmentCompat.java b/v13/java/android/support/v13/app/FragmentCompat.java
index 38e4a48..99e4a80 100644
--- a/v13/java/android/support/v13/app/FragmentCompat.java
+++ b/v13/java/android/support/v13/app/FragmentCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -23,6 +24,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.BuildCompat;
 
 import java.util.Arrays;
@@ -31,6 +33,8 @@
  * Helper for accessing features in {@link Fragment} introduced after
  * API level 13 in a backwards compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public class FragmentCompat {
     interface FragmentCompatImpl {
         void setMenuVisibility(Fragment f, boolean visible);
diff --git a/v13/java/android/support/v13/app/FragmentPagerAdapter.java b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
index 4ea48ab..78d8b89 100644
--- a/v13/java/android/support/v13/app/FragmentPagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentPagerAdapter.java
@@ -16,10 +16,12 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.support.v4.view.PagerAdapter;
 import android.util.Log;
@@ -62,6 +64,8 @@
  * {@sample frameworks/support/samples/Support13Demos/res/layout/fragment_pager_list.xml
  *      complete}
  */
+@RequiresApi(13)
+@TargetApi(13)
 public abstract class FragmentPagerAdapter extends PagerAdapter {
     private static final String TAG = "FragmentPagerAdapter";
     private static final boolean DEBUG = false;
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index ca316c9..2579688 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -16,11 +16,13 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.FragmentPagerAdapter;
 import android.support.v4.view.PagerAdapter;
 import android.util.Log;
@@ -65,6 +67,8 @@
  * {@sample frameworks/support/samples/Support4Demos/res/layout/fragment_pager_list.xml
  *      complete}
  */
+@RequiresApi(13)
+@TargetApi(13)
 public abstract class FragmentStatePagerAdapter extends PagerAdapter {
     private static final String TAG = "FragmentStatePagerAdapter";
     private static final boolean DEBUG = false;
diff --git a/v13/java/android/support/v13/app/FragmentTabHost.java b/v13/java/android/support/v13/app/FragmentTabHost.java
index 6cfd0ad..ba5d659 100644
--- a/v13/java/android/support/v13/app/FragmentTabHost.java
+++ b/v13/java/android/support/v13/app/FragmentTabHost.java
@@ -16,6 +16,7 @@
 
 package android.support.v13.app;
 
+import android.annotation.TargetApi;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -24,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,6 +41,8 @@
  * used with the platform {@link android.app.Fragment} APIs.  You will not
  * normally use this, instead using action bar tabs.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public class FragmentTabHost extends TabHost
         implements TabHost.OnTabChangeListener {
     private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
diff --git a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
index 4f5d9d8..f10a548 100644
--- a/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
+++ b/v13/java/android/support/v13/view/DragAndDropPermissionsCompat.java
@@ -16,7 +16,9 @@
 
 package android.support.v13.view;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.os.BuildCompat;
 import android.view.DragEvent;
@@ -27,6 +29,8 @@
  * Helper for accessing features in {@link android.view.DragAndDropPermissions}
  * introduced after API level 13 in a backwards compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public final class DragAndDropPermissionsCompat {
 
     interface DragAndDropPermissionsCompatImpl {
diff --git a/v13/java/android/support/v13/view/DragStartHelper.java b/v13/java/android/support/v13/view/DragStartHelper.java
index d3b51cc..b1fd913 100644
--- a/v13/java/android/support/v13/view/DragStartHelper.java
+++ b/v13/java/android/support/v13/view/DragStartHelper.java
@@ -17,7 +17,9 @@
 package android.support.v13.view;
 
 
+import android.annotation.TargetApi;
 import android.graphics.Point;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.InputDeviceCompat;
 import android.support.v4.view.MotionEventCompat;
 import android.view.MotionEvent;
@@ -68,6 +70,8 @@
  * }
  * </pre>
  */
+@RequiresApi(13)
+@TargetApi(13)
 public class DragStartHelper {
     final private View mView;
     final private OnDragStartListener mListener;
diff --git a/v13/java/android/support/v13/view/ViewCompat.java b/v13/java/android/support/v13/view/ViewCompat.java
index 971d70d..0fd3234 100644
--- a/v13/java/android/support/v13/view/ViewCompat.java
+++ b/v13/java/android/support/v13/view/ViewCompat.java
@@ -16,7 +16,9 @@
 
 package android.support.v13.view;
 
+import android.annotation.TargetApi;
 import android.content.ClipData;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.BuildCompat;
 import android.view.View;
 
@@ -24,6 +26,8 @@
  * Helper for accessing features in {@link View} introduced after API
  * level 13 in a backwards compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public class ViewCompat extends android.support.v4.view.ViewCompat {
     interface ViewCompatImpl {
         boolean startDragAndDrop(View v, ClipData data, View.DragShadowBuilder shadowBuilder,
diff --git a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
index 79d7066..17976a8 100644
--- a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,9 +16,11 @@
 
 package android.support.v13.view.inputmethod;
 
+import android.annotation.TargetApi;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.BuildCompat;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -27,6 +29,8 @@
  * Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
  * compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public final class EditorInfoCompat {
 
     private interface EditorInfoCompatImpl {
diff --git a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
index 07a76aa..5f7b012 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,12 +16,14 @@
 
 package android.support.v13.view.inputmethod;
 
+import android.annotation.TargetApi;
 import android.content.ClipDescription;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.BuildCompat;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
@@ -32,6 +34,8 @@
  * Helper for accessing features in {@link InputConnection} introduced after API level 13 in a
  * backwards compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public final class InputConnectionCompat {
 
     private interface InputConnectionCompatImpl {
diff --git a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
index 43ad7ff..9379020 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputContentInfoCompat.java
@@ -16,16 +16,20 @@
 
 package android.support.v13.view.inputmethod;
 
+import android.annotation.TargetApi;
 import android.content.ClipDescription;
 import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.BuildCompat;
 
 /**
  * Helper for accessing features in InputContentInfo introduced after API level 13 in a backwards
  * compatible fashion.
  */
+@RequiresApi(13)
+@TargetApi(13)
 public final class InputContentInfoCompat {
 
     private interface InputContentInfoCompatImpl {
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index f359486..7a0b846 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -22,15 +22,13 @@
 #       android-support-v7-preference \
 #       android-support-v7-appcompat \
 #       android-support-v7-recyclerview \
-#       android-support-v4 \
-#       android-support-annotations
+#       android-support-v4
 #
 # in their makefiles to include the resources in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v14-preference
-LOCAL_SDK_VERSION := 14
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
diff --git a/v14/preference/build.gradle b/v14/preference/build.gradle
index b834dd5..33d616e 100644
--- a/v14/preference/build.gradle
+++ b/v14/preference/build.gradle
@@ -30,6 +30,10 @@
 android {
     compileSdkVersion project.ext.currentSdk
 
+    defaultConfig {
+        minSdkVersion 14
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
diff --git a/v14/preference/res/drawable-v21/preference_list_divider_material.xml b/v14/preference/res/drawable-v21/preference_list_divider_material.xml
new file mode 100644
index 0000000..a5913de
--- /dev/null
+++ b/v14/preference/res/drawable-v21/preference_list_divider_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2015 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:tint="?android:attr/colorForeground">
+    <solid android:color="#1f000000" />
+    <size
+        android:height="1dp"
+        android:width="1dp" />
+</shape>
diff --git a/v14/preference/res/drawable/preference_list_divider_material.xml b/v14/preference/res/drawable/preference_list_divider_material.xml
index a5913de..fc91d07 100644
--- a/v14/preference/res/drawable/preference_list_divider_material.xml
+++ b/v14/preference/res/drawable/preference_list_divider_material.xml
@@ -15,8 +15,7 @@
   limitations under the License
   -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:tint="?android:attr/colorForeground">
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
     <solid android:color="#1f000000" />
     <size
         android:height="1dp"
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index 14b02ab..c6a50b4 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -14,165 +14,63 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.  The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-leanback-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  Base sub-library contains classes both needed by api-level specific libraries
-#  (e.g. KitKat) and final static library.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-common
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, common)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  A helper sub-library that makes direct use of API 23.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-api23
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, api23)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  A helper sub-library that makes direct use of API 21.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  A helper sub-library that makes direct use of KitKat APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-kitkat
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  A helper sub-library that makes direct use of JBMR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-leanback-jbmr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jbmr2)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
 #   LOCAL_STATIC_ANDROID_LIBRARIES := \
 #       android-support-v17-leanback \
 #       android-support-v7-recyclerview \
-#       android-support-v4 \
-#       android-support-annotations
+#       android-support-v4
 #
 # in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v17-leanback
-LOCAL_SDK_VERSION := 17
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-v17-leanback-kitkat \
-    android-support-v17-leanback-jbmr2 \
-    android-support-v17-leanback-api23 \
-    android-support-v17-leanback-api21 \
-    android-support-v17-leanback-common
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v17-leanback-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, common) \
+    $(call all-java-files-under, jbmr2) \
+    $(call all-java-files-under, kitkat) \
+    $(call all-java-files-under, api21) \
+    $(call all-java-files-under, api23) \
+    $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-recyclerview \
-    android-support-v4 \
+    android-support-compat \
+    android-support-core-ui \
+    android-support-media-compat \
+    android-support-fragment \
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
-# ===========================================================
-# Common Droiddoc vars
-leanback.docs.src_files := \
-    $(call all-java-files-under, src) \
-    $(call all-html-files-under, src)
-leanback.docs.java_libraries := \
-    android-support-annotations \
-    android-support-v4 \
-    android-support-v7-recyclerview \
-    android-support-v17-leanback-res \
-    android-support-v17-leanback
-
 # Documentation
 # ===========================================================
 include $(CLEAR_VARS)
-
+LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v17-leanback
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_MODULE_TAGS := optional
-
-gen_res_src_dirs := $(call intermediates-dir-for,JAVA_LIBRARIES,android-support-v17-leanback-res,,COMMON)/src
-
-LOCAL_SRC_FILES := $(leanback.docs.src_files)
-LOCAL_ADDITIONAL_JAVA_DIR := $(gen_res_src_dirs)
-
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-html-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+    android-support-annotations \
+    android-support-v4 \
+    android-support-v7-recyclerview \
+    android-support-v17-leanback
+LOCAL_ADDITIONAL_JAVA_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,android-support-v17-leanback-res,,COMMON)/src
 LOCAL_IS_HOST_MODULE := false
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
-
-LOCAL_JAVA_LIBRARIES := $(leanback.docs.java_libraries)
-
 LOCAL_DROIDDOC_OPTIONS := \
     -offlinemode \
     -hdf android.whichdoc offline \
     -federate Android http://developer.android.com \
     -federationapi Android prebuilts/sdk/api/17.txt \
     -hide 113
-
 include $(BUILD_DROIDDOC)
-
-# Cleanup temp vars
-# ===========================================================
-leanback.docs.src_files :=
-leanback.docs.java_libraries :=
-gen_res_src_dirs :=
-leanback_internal_api_file :=
-leanback_stubs_stamp :=
-leanback.docs.stubpackages :=
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
index 3cfc94f..d4f063d 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
@@ -18,9 +18,11 @@
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 import android.transition.Fade;
@@ -39,6 +41,8 @@
  * Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734)
  * @hide
  */
+@RequiresApi(21)
+@TargetApi(21)
 @RestrictTo(GROUP_ID)
 public class FadeAndShortSlide extends Visibility {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
index 866b904..f5a1cc7 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/SlideNoPropagation.java
@@ -13,7 +13,9 @@
  */
 package android.support.v17.leanback.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.transition.Slide;
 import android.util.AttributeSet;
@@ -23,6 +25,8 @@
 /**
  * @hide
  */
+@RequiresApi(21)
+@TargetApi(21)
 @RestrictTo(GROUP_ID)
 public class SlideNoPropagation extends Slide {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
index 1206ea8..1fe0874 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
@@ -14,8 +14,10 @@
 package android.support.v17.leanback.transition;
 
 import android.R;
-import android.app.Fragment;
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
 import android.transition.ChangeTransform;
 import android.transition.Transition;
 import android.transition.TransitionManager;
@@ -24,6 +26,8 @@
 import android.view.Window;
 import android.view.animation.AnimationUtils;
 
+@RequiresApi(21)
+@TargetApi(21)
 final class TransitionHelperApi21 {
 
     TransitionHelperApi21() {
@@ -109,4 +113,19 @@
     public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         viewGroup.setTransitionGroup(transitionGroup);
     }
+
+    public static void setEpicenterCallback(Object transitionObject,
+            final TransitionEpicenterCallback callback) {
+        Transition transition = (Transition) transitionObject;
+        if (callback == null) {
+            transition.setEpicenterCallback(null);
+        } else {
+            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
+                @Override
+                public Rect onGetEpicenter(Transition transition) {
+                    return callback.onGetEpicenter(transition);
+                }
+            });
+        }
+    }
 }
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
index a423995..d493bdd 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
@@ -1,5 +1,7 @@
 package android.support.v17.leanback.transition;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 
@@ -21,6 +23,8 @@
  * will not blink out or shift suddenly when the transition is interrupted.
  * @hide
  */
+@RequiresApi(21)
+@TargetApi(21)
 @RestrictTo(GROUP_ID)
 class TranslationAnimationCreator {
 
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
index a013ba1..c0ba7dd 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
@@ -13,11 +13,15 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.util.SparseArray;
+import android.annotation.TargetApi;
 import android.graphics.Outline;
-import android.view.ViewOutlineProvider;
+import android.support.annotation.RequiresApi;
+import android.util.SparseArray;
 import android.view.View;
+import android.view.ViewOutlineProvider;
 
+@RequiresApi(21)
+@TargetApi(21)
 class RoundedRectHelperApi21 {
 
     private static SparseArray<ViewOutlineProvider> sRoundedRectProvider;
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
index 66f7687..35f2c51 100644
--- a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
@@ -13,11 +13,14 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ShadowHelperApi21 {
 
     static class ShadowImpl {
diff --git a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
index 19cf5d6..82ba369 100644
--- a/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/app/PermissionHelper23.java
@@ -13,6 +13,11 @@
  */
 package android.support.v17.leanback.app;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(23)
+@TargetApi(23)
 class PermissionHelper23 {
 
     public static void requestPermissions(android.app.Fragment fragment, String[] permissions,
diff --git a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
index c4760d4..f00f43d 100644
--- a/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
+++ b/v17/leanback/api23/android/support/v17/leanback/widget/ForegroundHelperApi23.java
@@ -13,16 +13,13 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.drawable.ColorDrawable;
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
-import android.view.ViewGroup;
+import android.support.annotation.RequiresApi;
 import android.view.View;
-import android.view.ViewOutlineProvider;
 
+@RequiresApi(23)
+@TargetApi(23)
 class ForegroundHelperApi23 {
 
     public static Drawable getForeground(View view) {
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index 52bd161..270f552 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -1,9 +1,11 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'leanback-v17'
 
 dependencies {
-    compile project(':support-v4')
+    compile project(':support-compat')
+    compile project(':support-core-ui')
+    compile project(':support-media-compat')
+    compile project(':support-fragment')
     compile project(':support-recyclerview-v7')
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -18,22 +20,24 @@
 }
 
 android {
-    // WARNING: should be 17
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 17
-        // TODO: get target from branch
-        //targetSdkVersion 19
-
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
-        main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'api23', 'src']
-        main.res.srcDirs = ['res']
+        main.java.srcDirs = [
+                'common',
+                'jbmr2',
+                'kitkat',
+                'api21',
+                'api23',
+                'src'
+        ]
+        main.res.srcDir 'res'
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/java'
diff --git a/v17/leanback/common/android/support/v17/leanback/transition/TransitionEpicenterCallback.java b/v17/leanback/common/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
new file mode 100644
index 0000000..ec7f84c
--- /dev/null
+++ b/v17/leanback/common/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.transition;
+
+import android.graphics.Rect;
+
+/**
+ * Class to get the epicenter of Transition.
+ * @hide
+ */
+public abstract class TransitionEpicenterCallback {
+
+    /**
+     * Implementers must override to return the epicenter of the Transition in screen
+     * coordinates.
+     *
+     * @param transition The transition for which the epicenter applies.
+     * @return The Rect region of the epicenter of <code>transition</code> or null if
+     * there is no epicenter.
+     */
+    public abstract Rect onGetEpicenter(Object transition);
+}
+
diff --git a/v17/leanback/generatev4.py b/v17/leanback/generatev4.py
index fb23876..2e1eaeb 100755
--- a/v17/leanback/generatev4.py
+++ b/v17/leanback/generatev4.py
@@ -19,8 +19,9 @@
 
 print "Generate v4 fragment related code for leanback"
 
-cls = ['Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers',
-      'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded', 'GuidedStep', 'Onboarding']
+cls = ['Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers',
+      'PlaybackOverlay', 'Playback', 'Rows', 'Search', 'VerticalGrid', 'Branded',
+      'GuidedStep', 'Onboarding', 'Video']
 
 for w in cls:
     print "copy {}Fragment to {}SupportFragment".format(w, w)
@@ -28,6 +29,7 @@
     file = open('src/android/support/v17/leanback/app/{}Fragment.java'.format(w), 'r')
     outfile = open('src/android/support/v17/leanback/app/{}SupportFragment.java'.format(w), 'w')
 
+    outfile.write("// CHECKSTYLE:OFF Generated code\n")
     outfile.write("/* This file is auto-generated from {}Fragment.java.  DO NOT MODIFY. */\n\n".format(w))
 
     for line in file:
@@ -43,13 +45,44 @@
     file.close()
     outfile.close()
 
-file = open('src/android/support/v17/leanback/app/PlaybackControlGlue.java', 'r')
-outfile = open('src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java', 'w')
-outfile.write("/* This file is auto-generated from PlaybackControlGlue.java.  DO NOT MODIFY. */\n\n")
+print "copy VideoFragmentGlueHost to VideoSupportFragmentGlueHost".format(w, w)
+file = open('src/android/support/v17/leanback/app/VideoFragmentGlueHost.java'.format(w), 'r')
+outfile = open('src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java'.format(w), 'w')
+
+outfile.write("// CHECKSTYLE:OFF Generated code\n")
+outfile.write("/* This file is auto-generated from {}VideoFragmentGlueHost.java.  DO NOT MODIFY. */\n\n".format(w))
+
 for line in file:
-    line = line.replace('PlaybackControlGlue', 'PlaybackControlSupportGlue');
-    line = line.replace('PlaybackOverlayFragment', 'PlaybackOverlaySupportFragment');
+    line = line.replace('IS_FRAMEWORK_FRAGMENT = true', 'IS_FRAMEWORK_FRAGMENT = false');
+    line = line.replace('VideoSupportFragmentGlueHost'.format(w), 'VideoSupportFragmentGlueHost'.format(w))
+    line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
+    line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+    line = line.replace('activity.getFragmentManager()', 'activity.getSupportFragmentManager()')
+    line = line.replace('Activity activity', 'FragmentActivity activity')
+    line = line.replace('VideoFragment', 'VideoSupportFragment')
+    line = line.replace('PlaybackFragmentGlueHost', 'PlaybackSupportFragmentGlueHost')
+    line = line.replace('(Activity', '(FragmentActivity')
     outfile.write(line)
 file.close()
 outfile.close()
 
+print "copy PlaybackFragmentGlueHost to PlaybackSupportFragmentGlueHost".format(w, w)
+file = open('src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java'.format(w), 'r')
+outfile = open('src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java'.format(w), 'w')
+
+outfile.write("// CHECKSTYLE:OFF Generated code\n")
+outfile.write("/* This file is auto-generated from {}PlaybackFragmentGlueHost.java.  DO NOT MODIFY. */\n\n".format(w))
+
+for line in file:
+    line = line.replace('IS_FRAMEWORK_FRAGMENT = true', 'IS_FRAMEWORK_FRAGMENT = false');
+    line = line.replace('VideoSupportFragmentGlueHost'.format(w), 'VideoSupportFragmentGlueHost'.format(w))
+    line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
+    line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+    line = line.replace('activity.getFragmentManager()', 'activity.getSupportFragmentManager()')
+    line = line.replace('Activity activity', 'FragmentActivity activity')
+    line = line.replace('PlaybackFragment', 'PlaybackSupportFragment')
+    line = line.replace('PlaybackFragmentGlueHost', 'PlaybackSupportFragmentGlueHost')
+    line = line.replace('(Activity', '(FragmentActivity')
+    outfile.write(line)
+file.close()
+outfile.close()
diff --git a/v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java b/v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java
deleted file mode 100644
index 70b8ce9..0000000
--- a/v17/leanback/jbmr2/android/support/v17/leanback/os/TraceHelperJbmr2.java
+++ /dev/null
@@ -1,29 +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.v17.leanback.os;
-
-import android.os.Trace;
-
-class TraceHelperJbmr2 {
-
-    public static void beginSection(String section) {
-        Trace.beginSection(section);
-    }
-
-    public static void endSection() {
-        Trace.endSection();
-    }
-}
diff --git a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
index ad53425..4ee6b29 100644
--- a/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
+++ b/v17/leanback/jbmr2/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
@@ -13,11 +13,15 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.v17.leanback.R;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+@RequiresApi(18)
+@TargetApi(18)
 class ShadowHelperJbmr2 {
 
     static class ShadowImpl {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
index 0cc9081..b6a82b7 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
@@ -13,13 +13,15 @@
  */
 package android.support.v17.leanback.transition;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.support.v17.leanback.R;
 import android.view.Gravity;
 import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
 
+@RequiresApi(19)
+@TargetApi(19)
 class LeanbackTransitionHelperKitKat {
 
     static public Object loadTitleInTransition(Context context) {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
index 2bdc3aa..5fbf414 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/Scale.java
@@ -17,11 +17,15 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.ViewGroup;
 import android.transition.Transition;
 import android.transition.TransitionValues;
 
+@RequiresApi(19)
+@TargetApi(19)
 class Scale extends Transition {
     private static final String PROPNAME_SCALE = "android:leanback:scale";
 
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
index 686c4fa..e8e4c10 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/SlideKitkat.java
@@ -17,15 +17,16 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
+import android.support.annotation.RequiresApi;
+import android.support.v17.leanback.R;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Property;
 import android.view.Gravity;
 import android.view.View;
@@ -33,15 +34,13 @@
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
-import android.transition.Visibility;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-import android.support.v17.leanback.R;
 
 /**
  * Slide distance toward/from a edge.
  * This is a limited Slide implementation for KitKat without propagation support.
  */
+@RequiresApi(19)
+@TargetApi(19)
 class SlideKitkat extends Visibility {
     private static final String TAG = "SlideKitkat";
 
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
index 221b84a..777b34b 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
@@ -15,7 +15,9 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.transition.AutoTransition;
 import android.transition.ChangeBounds;
 import android.transition.Fade;
@@ -33,6 +35,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
+@RequiresApi(19)
+@TargetApi(19)
 final class TransitionHelperKitkat {
 
     TransitionHelperKitkat() {
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
index 2b095fa..49cb35e 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/widget/BackgroundHelperKitkat.java
@@ -15,9 +15,13 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(19)
+@TargetApi(19)
 class BackgroundHelperKitkat {
 
     public static void setBackgroundPreservingAlpha(View view, Drawable drawable) {
diff --git a/v17/leanback/res/layout/lb_details_fragment.xml b/v17/leanback/res/layout/lb_details_fragment.xml
index 15328e4..bb02902 100644
--- a/v17/leanback/res/layout/lb_details_fragment.xml
+++ b/v17/leanback/res/layout/lb_details_fragment.xml
@@ -14,16 +14,24 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v17.leanback.widget.BrowseFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/dummy"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
 
-    <android.support.v17.leanback.widget.BrowseFrameLayout
+    <FrameLayout
+        android:id="@+id/video_surface_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <FrameLayout
+            android:id="@+id/details_background_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+    <FrameLayout
         android:id="@+id/details_fragment_root"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:descendantFocusability="afterDescendants"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
 
@@ -32,6 +40,6 @@
             android:layout_width="wrap_content"
             android:layout_height="match_parent" />
 
-    </android.support.v17.leanback.widget.BrowseFrameLayout>
+    </FrameLayout>
 
-</FrameLayout>
+</android.support.v17.leanback.widget.BrowseFrameLayout>
diff --git a/v17/leanback/res/layout/lb_details_overview.xml b/v17/leanback/res/layout/lb_details_overview.xml
index a32f393..7943150 100644
--- a/v17/leanback/res/layout/lb_details_overview.xml
+++ b/v17/leanback/res/layout/lb_details_overview.xml
@@ -84,7 +84,7 @@
             android:focusableInTouchMode="true"
             android:paddingStart="@dimen/lb_details_overview_description_margin_start"
             android:paddingEnd="@dimen/lb_details_overview_description_margin_end"
-            lb:horizontalMargin="@dimen/lb_details_overview_action_items_margin"
+            android:horizontalSpacing="@dimen/lb_details_overview_action_items_spacing"
             lb:rowHeight="@dimen/lb_details_overview_actions_height" />
 
         </LinearLayout>
diff --git a/v17/leanback/res/layout/lb_divider.xml b/v17/leanback/res/layout/lb_divider.xml
index 389b3ab..e05223b 100644
--- a/v17/leanback/res/layout/lb_divider.xml
+++ b/v17/leanback/res/layout/lb_divider.xml
@@ -23,5 +23,5 @@
     android:layout_height="1dp"
     android:focusable="false"
     android:focusableInTouchMode="false"
-    android:background="?android:attr/listDivider"
-    />
+    android:importantForAccessibility="no"
+    android:background="?android:attr/listDivider" />
diff --git a/v17/leanback/res/layout/lb_fullwidth_details_overview.xml b/v17/leanback/res/layout/lb_fullwidth_details_overview.xml
index 415bd82..234abb2 100644
--- a/v17/leanback/res/layout/lb_fullwidth_details_overview.xml
+++ b/v17/leanback/res/layout/lb_fullwidth_details_overview.xml
@@ -61,7 +61,7 @@
                 android:focusableInTouchMode="true"
                 android:paddingStart="@dimen/lb_details_v2_description_margin_start"
                 android:paddingEnd="@dimen/lb_details_v2_description_margin_end"
-                lb:horizontalMargin="@dimen/lb_details_overview_action_items_margin"
+                android:horizontalSpacing="@dimen/lb_details_overview_action_items_spacing"
                 lb:rowHeight="@dimen/lb_details_v2_actions_height" />
         </LinearLayout>
 
diff --git a/v17/leanback/res/layout/lb_guidedactions.xml b/v17/leanback/res/layout/lb_guidedactions.xml
index e5e2289..9857f22 100644
--- a/v17/leanback/res/layout/lb_guidedactions.xml
+++ b/v17/leanback/res/layout/lb_guidedactions.xml
@@ -31,7 +31,9 @@
         android:visibility="gone"
         android:background="?attr/guidedActionsBackgroundDark" />
 
-    <android.support.v17.leanback.widget.NonOverlappingRelativeLayout
+    <!-- special relativelayout will assign guidedactions_sub_list's topMargin using percentage
+         defined by theme attribute guidedStepKeyline -->
+    <android.support.v17.leanback.widget.GuidedActionsRelativeLayout
         android:id="@+id/guidedactions_content"
         android:transitionName="guidedactions_content"
         android:transitionGroup="false"
@@ -55,9 +57,12 @@
         <android.support.v17.leanback.widget.VerticalGridView
             android:transitionGroup="true"
             android:id="@+id/guidedactions_sub_list"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginTop="-1dip"
             style="?attr/guidedSubActionsListStyle"
             android:visibility="invisible" />
 
-    </android.support.v17.leanback.widget.NonOverlappingRelativeLayout>
+    </android.support.v17.leanback.widget.GuidedActionsRelativeLayout>
 
 </RelativeLayout>
diff --git a/v17/leanback/res/layout/lb_picker_column.xml b/v17/leanback/res/layout/lb_picker_column.xml
index 1f218a6..afa6dee 100644
--- a/v17/leanback/res/layout/lb_picker_column.xml
+++ b/v17/leanback/res/layout/lb_picker_column.xml
@@ -20,6 +20,7 @@
     android:layout_width="wrap_content"
     android:layout_height="@dimen/picker_item_height"
     android:importantForAccessibility="no"
+    android:verticalSpacing="@dimen/picker_item_spacing"
     lb:columnWidth="wrap_content"
     android:clipToPadding="false"
     android:paddingStart="@dimen/picker_column_horizontal_padding"
diff --git a/v17/leanback/res/layout/lb_playback_fragment.xml b/v17/leanback/res/layout/lb_playback_fragment.xml
new file mode 100644
index 0000000..7d1e531
--- /dev/null
+++ b/v17/leanback/res/layout/lb_playback_fragment.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/playback_fragment_root"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <FrameLayout
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/playback_controls_dock"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"/>
+</FrameLayout>
diff --git a/v17/leanback/res/layout/lb_row_header.xml b/v17/leanback/res/layout/lb_row_header.xml
index 67da907..8962e9a 100644
--- a/v17/leanback/res/layout/lb_row_header.xml
+++ b/v17/leanback/res/layout/lb_row_header.xml
@@ -15,11 +15,26 @@
      limitations under the License.
 -->
 
-<android.support.v17.leanback.widget.RowHeaderView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/row_header"
-    android:importantForAccessibility="no"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    style="?rowHeaderStyle"
-    />
+<android:LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <android.support.v17.leanback.widget.RowHeaderView
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/row_header"
+            android:importantForAccessibility="no"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="?rowHeaderStyle"/>
+
+    <TextView
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/row_header_description"
+            android:importantForAccessibility="no"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="?rowHeaderDescriptionStyle" />
+
+</android:LinearLayout>
diff --git a/v17/leanback/res/layout/lb_video_surface.xml b/v17/leanback/res/layout/lb_video_surface.xml
new file mode 100644
index 0000000..0f15468
--- /dev/null
+++ b/v17/leanback/res/layout/lb_video_surface.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/video_surface"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/documents-archive/AndroidManifest.xml b/v17/leanback/res/layout/video_surface_fragment.xml
similarity index 70%
copy from documents-archive/AndroidManifest.xml
copy to v17/leanback/res/layout/video_surface_fragment.xml
index 2cd0f7a..959a386 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/v17/leanback/res/layout/video_surface_fragment.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!--
+     Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+</SurfaceView>
\ No newline at end of file
diff --git a/v17/leanback/res/transition-v21/lb_details_enter_transition.xml b/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
index a9ebb3d..618ef64 100644
--- a/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
+++ b/v17/leanback/res/transition-v21/lb_details_enter_transition.xml
@@ -24,7 +24,8 @@
           <target android:excludeId="@id/title_badge" />
           <target android:excludeId="@id/title_text" />
           <target android:excludeId="@id/title_orb" />
-      </targets>
+          <target android:excludeId="@id/details_background_view" />
+      </targets>4
   </transition>
   <fade
       android:interpolator="@android:interpolator/linear_out_slow_in"
diff --git a/v17/leanback/res/transition-v21/lb_details_return_transition.xml b/v17/leanback/res/transition-v21/lb_details_return_transition.xml
index f31ebcb..2a70cc9 100644
--- a/v17/leanback/res/transition-v21/lb_details_return_transition.xml
+++ b/v17/leanback/res/transition-v21/lb_details_return_transition.xml
@@ -23,6 +23,7 @@
           <target android:excludeId="@id/title_badge" />
           <target android:excludeId="@id/title_text" />
           <target android:excludeId="@id/title_orb" />
+          <target android:excludeId="@id/details_background_view" />
       </targets>
   </transition>
   <fade
diff --git a/v17/leanback/res/values-bs-rBA/strings.xml b/v17/leanback/res/values-bs-rBA/strings.xml
index cdfe434..63a76e2 100644
--- a/v17/leanback/res/values-bs-rBA/strings.xml
+++ b/v17/leanback/res/values-bs-rBA/strings.xml
@@ -53,5 +53,5 @@
     <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
     <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
     <string name="lb_onboarding_get_started" msgid="6961440391306351139">"ZAPOČNITE"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Sljedeća"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="2918313444257732434">"Naprijed"</string>
 </resources>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 0e97f76..870d958 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -28,10 +28,14 @@
         <!-- Allow DPAD key to navigate out of last row, for HorizontalGridView, it's the
              bottom edge, for VerticalGridView it's the "end" edge.  Default value is true.  -->
         <attr name="focusOutSideEnd" format="boolean" />
-        <!-- Defining margin between two items horizontally -->
+        <!-- Deprecated, use android:horizontalSpacing -->
         <attr name="horizontalMargin" format="dimension" />
-        <!-- Defining margin between two items vertically -->
+        <!-- Deprecated, use android:verticalSpacing -->
         <attr name="verticalMargin" format="dimension" />
+        <!-- Defining space between two items horizontally -->
+        <attr name="android:horizontalSpacing" />
+        <!-- Defining space between two items vertically -->
+        <attr name="android:verticalSpacing" />
         <!-- Defining gravity of child view -->
         <attr name="android:gravity" />
     </declare-styleable>
@@ -249,6 +253,10 @@
         <attr name="rowHorizontalGridStyle" format="reference" />
         <!-- header style inside a row -->
         <attr name="rowHeaderStyle" format="reference" />
+
+        <!-- header description style inside a row -->
+        <attr name="rowHeaderDescriptionStyle" format="reference" />
+
         <!-- style for the layout that hosting Header inside a row -->
         <attr name="rowHeaderDockStyle" format="reference" />
 
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index ec9102c..bebb115 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -20,6 +20,7 @@
 
     <color name="lb_browse_title_color">#EEEEEE</color>
     <color name="lb_browse_header_color">#FFFFFF</color>
+    <color name="lb_browse_header_description_color">#AAFFFFFF</color>
 
     <color name="lb_list_item_unselected_text_color">#FFF1F1F1</color>
     <color name="lb_background_protection">#99000000</color>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index e0a366a..4902585 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -38,8 +38,9 @@
     <item name="lb_browse_rows_scale" type="fraction">80%</item>
 
     <!-- Derived from the ux spec of 48dp baseline to baseline -->
-    <dimen name="lb_browse_headers_vertical_margin">21dp</dimen>
+    <dimen name="lb_browse_headers_vertical_spacing">21dp</dimen>
     <dimen name="lb_browse_header_text_size">20sp</dimen>
+    <dimen name="lb_browse_header_description_text_size">14sp</dimen>
     <dimen name="lb_browse_header_height">24dp</dimen>
     <dimen name="lb_browse_section_header_text_size">16sp</dimen>
     <dimen name="lb_browse_header_fading_length">12dp</dimen>
@@ -52,8 +53,8 @@
     <dimen name="lb_browse_row_hovercard_max_width">400dp</dimen>
     <dimen name="lb_browse_row_hovercard_title_font_size">18sp</dimen>
     <dimen name="lb_browse_row_hovercard_description_font_size">14sp</dimen>
-    <dimen name="lb_browse_item_horizontal_margin">8dp</dimen>
-    <dimen name="lb_browse_item_vertical_margin">8dp</dimen>
+    <dimen name="lb_browse_item_horizontal_spacing">8dp</dimen>
+    <dimen name="lb_browse_item_vertical_spacing">8dp</dimen>
     <dimen name="lb_browse_selected_row_top_padding">20dp</dimen>
     <dimen name="lb_browse_expanded_selected_row_top_padding">16dp</dimen>
     <dimen name="lb_browse_expanded_row_no_hovercard_bottom_padding">28dp</dimen>
@@ -75,7 +76,7 @@
     <dimen name="lb_details_overview_description_margin_bottom">12dp</dimen>
     <dimen name="lb_details_overview_image_margin_horizontal">24dp</dimen>
     <dimen name="lb_details_overview_image_margin_vertical">24dp</dimen>
-    <dimen name="lb_details_overview_action_items_margin">16dp</dimen>
+    <dimen name="lb_details_overview_action_items_spacing">16dp</dimen>
     <item name="lb_details_overview_action_select_duration" format="integer" type="dimen">150</item>
     <dimen name="lb_details_overview_actions_padding_start">294dp</dimen>
     <dimen name="lb_details_overview_actions_padding_end">132dp</dimen>
@@ -165,7 +166,7 @@
     <dimen name="lb_control_button_text_size">22sp</dimen>
 
     <dimen name="lb_error_image_max_height">120dp</dimen>
-    <integer name="lb_error_message_max_lines">1</integer>
+    <integer name="lb_error_message_max_lines">3</integer>
     <dimen name="lb_error_message_max_width">600dp</dimen>
     <dimen name="lb_error_message_text_size">16sp</dimen>
     <dimen name="lb_error_under_image_baseline_margin">36dp</dimen>
@@ -277,6 +278,8 @@
     <dimen name="lb_guidedactions_item_space_between_title_and_description">2dp</dimen>
     <dimen name="lb_guidedactions_item_description_font_size">12sp</dimen>
     <dimen name="lb_guidedactions_sublist_bottom_margin">28dp</dimen>
+    <dimen name="lb_guidedactions_sublist_padding_top">8dip</dimen>
+    <dimen name="lb_guidedactions_sublist_padding_bottom">8dip</dimen>
 
     <integer name="lb_guidedactions_item_animation_duration">100</integer>
     <integer name="lb_guidedactions_item_title_min_lines">1</integer>
@@ -285,7 +288,9 @@
     <!-- end GuidedStepFragment -->
 
     <!-- height for picker item. -->
-    <dimen name="picker_item_height">64dp</dimen>
+    <dimen name="picker_item_height">32dp</dimen>
+    <!-- vertical space between two picker item -->
+    <dimen name="picker_item_spacing">32dp</dimen>
     <!-- picker column horizontal padding-->
     <dimen name="picker_column_horizontal_padding">8dp</dimen>
     <!-- picker separator horizontal padding -->
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 0666cdd..dbe090a 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -42,6 +42,11 @@
     <style name="TextAppearance.Leanback.Row.Header" parent="TextAppearance.Leanback.Header">
     </style>
 
+    <style name="TextAppearance.Leanback.Row.Header.Description" parent="TextAppearance.Leanback.Header">
+        <item name="android:textSize">@dimen/lb_browse_header_description_text_size</item>
+        <item name="android:textColor">@color/lb_browse_header_description_color</item>
+    </style>
+
     <style name="TextAppearance.Leanback.SearchTextEdit" parent="TextAppearance.Leanback">
         <item name="android:textSize">@dimen/lb_search_bar_text_size</item>
     </style>
@@ -211,7 +216,7 @@
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
         <item name="focusOutFront">true</item>
         <item name="focusOutEnd">true</item>
-        <item name="verticalMargin">@dimen/lb_browse_headers_vertical_margin</item>
+        <item name="android:verticalSpacing">@dimen/lb_browse_headers_vertical_spacing</item>
         <item name="android:focusable">true</item>
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:contentDescription">@string/lb_navigation_menu_contentDescription</item>
@@ -245,10 +250,10 @@
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
         <item name="android:paddingEnd">?attr/browsePaddingEnd</item>
-        <item name="android:paddingBottom">@dimen/lb_browse_item_vertical_margin</item>
-        <item name="android:paddingTop">@dimen/lb_browse_item_vertical_margin</item>
-        <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
-        <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
+        <item name="android:paddingBottom">@dimen/lb_browse_item_vertical_spacing</item>
+        <item name="android:paddingTop">@dimen/lb_browse_item_vertical_spacing</item>
+        <item name="android:horizontalSpacing">@dimen/lb_browse_item_horizontal_spacing</item>
+        <item name="android:verticalSpacing">@dimen/lb_browse_item_vertical_spacing</item>
         <item name="focusOutFront">true</item>
     </style>
 
@@ -260,8 +265,8 @@
         <item name="android:paddingBottom">@dimen/lb_vertical_grid_padding_bottom</item>
         <item name="android:paddingTop">?attr/browseRowsMarginTop</item>
         <item name="android:gravity">center_horizontal</item>
-        <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
-        <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
+        <item name="android:horizontalSpacing">@dimen/lb_browse_item_horizontal_spacing</item>
+        <item name="android:verticalSpacing">@dimen/lb_browse_item_vertical_spacing</item>
         <item name="focusOutFront">true</item>
     </style>
 
@@ -269,6 +274,10 @@
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Row.Header</item>
     </style>
 
+    <style name="Widget.Leanback.Row.Header.Description" parent="Widget.Leanback.Header">
+        <item name="android:textAppearance">@style/TextAppearance.Leanback.Row.Header.Description</item>
+    </style>
+
     <style name="Widget.Leanback.Row.HeaderDock">
         <item name="android:paddingStart">?attr/browsePaddingStart</item>
     </style>
@@ -571,7 +580,7 @@
         <item name="android:focusableInTouchMode">false</item>
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
-        <item name="verticalMargin">@dimen/lb_guidedactions_list_vertical_spacing</item>
+        <item name="android:verticalSpacing">@dimen/lb_guidedactions_list_vertical_spacing</item>
         <item name="android:paddingStart">@dimen/lb_guidedactions_list_padding_start</item>
         <item name="android:paddingEnd">@dimen/lb_guidedactions_list_padding_end</item>
         <item name="focusOutEnd">false</item>
@@ -580,6 +589,9 @@
 
     <!-- Style for the vertical grid of sub actions in a GuidedActionsStylist's default layout. -->
     <style name="Widget.Leanback.GuidedSubActionsListStyle" parent="Widget.Leanback.GuidedActionsListStyle">
+        <item name="android:paddingTop">@dimen/lb_guidedactions_sublist_padding_top</item>
+        <item name="android:paddingBottom">@dimen/lb_guidedactions_sublist_padding_bottom</item>
+        <item name="android:clipToPadding">false</item>
         <item name="android:focusable">true</item>
         <item name="android:focusableInTouchMode">true</item>
         <item name="focusOutSideStart">false</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index 2c2ef3c..c6d6baa 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -60,6 +60,7 @@
         <item name="browseTitleViewStyle">@style/Widget.Leanback.TitleView</item>
 
         <item name="rowHeaderStyle">@style/Widget.Leanback.Row.Header</item>
+        <item name="rowHeaderDescriptionStyle">@style/Widget.Leanback.Row.Header.Description</item>
         <item name="rowHoverCardTitleStyle">@style/Widget.Leanback.Row.HoverCardTitle</item>
         <item name="rowHoverCardDescriptionStyle">@style/Widget.Leanback.Row.HoverCardDescription</item>
         <item name="rowHeaderDockStyle">@style/Widget.Leanback.Row.HeaderDock</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 0c490ea..8809ef4 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -13,13 +13,6 @@
  */
 package android.support.v17.leanback.app;
 
-import java.lang.ref.WeakReference;
-
-import android.support.annotation.ColorInt;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.support.v17.leanback.R;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -32,11 +25,17 @@
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.Handler;
+import android.support.annotation.ColorInt;
+import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.BackgroundHelper;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.animation.FastOutLinearInInterpolator;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -44,10 +43,10 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.animation.Interpolator;
 import android.view.animation.AnimationUtils;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.content.ContextCompat;
+import android.view.animation.Interpolator;
+
+import java.lang.ref.WeakReference;
 
 /**
  * Supports background image continuity between multiple Activities.
@@ -85,7 +84,7 @@
 public final class BackgroundManager {
 
     interface FragmentStateQueriable {
-        public boolean isResumed();
+        boolean isResumed();
     }
 
     static final String TAG = "BackgroundManager";
@@ -392,12 +391,12 @@
             DrawableWrapper imageOutWrapper = findWrapperById(R.id.background_imageout);
 
             mColorFilter = null;
-            if (imageInWrapper != null && imageInWrapper.getAlpha() == FULL_ALPHA &&
-                    dimWrapper.getDrawable() instanceof ColorDrawable) {
+            if (imageInWrapper != null && imageInWrapper.getAlpha() == FULL_ALPHA
+                    && dimWrapper.getDrawable() instanceof ColorDrawable) {
                 int dimColor = ((ColorDrawable) dimWrapper.getDrawable()).getColor();
-                if (Color.red(dimColor) == 0 &&
-                        Color.green(dimColor) == 0 &&
-                        Color.blue(dimColor) == 0) {
+                if (Color.red(dimColor) == 0
+                        && Color.green(dimColor) == 0
+                        && Color.blue(dimColor) == 0) {
                     int dimAlpha = 255 - Color.alpha(dimColor);
                     int color = Color.argb(getAlpha(), dimAlpha, dimAlpha, dimAlpha);
                     mColorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY);
@@ -416,8 +415,8 @@
         @Override
         public void draw(Canvas canvas) {
             DrawableWrapper imageInWrapper = findWrapperById(R.id.background_imagein);
-            if (imageInWrapper != null && imageInWrapper.getDrawable() != null &&
-                    imageInWrapper.getColorFilter() != null) {
+            if (imageInWrapper != null && imageInWrapper.getDrawable() != null
+                    && imageInWrapper.getColorFilter() != null) {
                 imageInWrapper.getDrawable().draw(canvas);
             } else {
                 super.draw(canvas);
@@ -589,9 +588,6 @@
      * for this Activity.
      */
     public static BackgroundManager getInstance(Activity activity) {
-        if (activity instanceof FragmentActivity) {
-            return getSupportInstance((FragmentActivity) activity);
-        }
         BackgroundFragment fragment = (BackgroundFragment) activity.getFragmentManager()
                 .findFragmentByTag(FRAGMENT_TAG);
         if (fragment != null) {
@@ -602,24 +598,10 @@
             // manager is null: this is a fragment restored by FragmentManager,
             // fall through to create a BackgroundManager attach to it.
         }
-        return new BackgroundManager(activity, false);
+        return new BackgroundManager(activity);
     }
 
-    private static BackgroundManager getSupportInstance(FragmentActivity activity) {
-        BackgroundSupportFragment fragment = (BackgroundSupportFragment) activity
-                .getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
-        if (fragment != null) {
-            BackgroundManager manager = fragment.getBackgroundManager();
-            if (manager != null) {
-                return manager;
-            }
-            // manager is null: this is a fragment restored by FragmentManager,
-            // fall through to create a BackgroundManager attach to it.
-        }
-        return new BackgroundManager(activity, true);
-    }
-
-    private BackgroundManager(Activity activity, boolean isSupportFragmentActivity) {
+    private BackgroundManager(Activity activity) {
         mContext = activity;
         mService = BackgroundContinuityService.getInstance();
         mHeightPx = mContext.getResources().getDisplayMetrics().heightPixels;
@@ -648,11 +630,7 @@
         }
         ta.recycle();
 
-        if (isSupportFragmentActivity) {
-            createSupportFragment((FragmentActivity) activity);
-        } else {
-            createFragment(activity);
-        }
+        createFragment(activity);
     }
 
     private void createFragment(Activity activity) {
@@ -664,26 +642,8 @@
             activity.getFragmentManager().beginTransaction().add(fragment, FRAGMENT_TAG).commit();
         } else {
             if (fragment.getBackgroundManager() != null) {
-                throw new IllegalStateException("Created duplicated BackgroundManager for same " +
-                        "activity, please use getInstance() instead");
-            }
-        }
-        fragment.setBackgroundManager(this);
-        mFragmentState = fragment;
-    }
-
-    private void createSupportFragment(FragmentActivity activity) {
-        // Use a fragment to ensure the background manager gets detached properly.
-        BackgroundSupportFragment fragment = (BackgroundSupportFragment) activity
-                .getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
-        if (fragment == null) {
-            fragment = new BackgroundSupportFragment();
-            activity.getSupportFragmentManager().beginTransaction().add(fragment, FRAGMENT_TAG)
-                    .commit();
-        } else {
-            if (fragment.getBackgroundManager() != null) {
-                throw new IllegalStateException("Created duplicated BackgroundManager for same " +
-                    "activity, please use getInstance() instead");
+                throw new IllegalStateException("Created duplicated BackgroundManager for same "
+                        + "activity, please use getInstance() instead");
             }
         }
         fragment.setBackgroundManager(this);
@@ -691,23 +651,23 @@
     }
 
     DrawableWrapper getImageInWrapper() {
-        return mLayerDrawable == null ? null :
-                mLayerDrawable.findWrapperById(R.id.background_imagein);
+        return mLayerDrawable == null
+                ? null : mLayerDrawable.findWrapperById(R.id.background_imagein);
     }
 
     DrawableWrapper getImageOutWrapper() {
-        return mLayerDrawable == null ? null :
-                mLayerDrawable.findWrapperById(R.id.background_imageout);
+        return mLayerDrawable == null
+                ? null : mLayerDrawable.findWrapperById(R.id.background_imageout);
     }
 
     DrawableWrapper getDimWrapper() {
-        return mLayerDrawable == null ? null :
-                mLayerDrawable.findWrapperById(R.id.background_dim);
+        return mLayerDrawable == null
+                ? null : mLayerDrawable.findWrapperById(R.id.background_dim);
     }
 
     private DrawableWrapper getColorWrapper() {
-        return mLayerDrawable == null ? null :
-                mLayerDrawable.findWrapperById(R.id.background_color);
+        return mLayerDrawable == null
+                ? null : mLayerDrawable.findWrapperById(R.id.background_color);
     }
 
     /**
@@ -719,12 +679,15 @@
             return;
         }
         if (mLayerDrawable == null) {
-            if (DEBUG) Log.v(TAG, "onActivityStart " + this +
-                    " released state, syncing with service");
+            if (DEBUG) {
+                Log.v(TAG, "onActivityStart " + this + " released state, syncing with service");
+            }
             syncWithService();
         } else {
-            if (DEBUG) Log.v(TAG, "onActivityStart " + this + " updating service color "
-                    + mBackgroundColor + " drawable " + mBackgroundDrawable);
+            if (DEBUG) {
+                Log.v(TAG, "onActivityStart " + this + " updating service color "
+                        + mBackgroundColor + " drawable " + mBackgroundDrawable);
+            }
             mService.setColor(mBackgroundColor);
             mService.setDrawable(mBackgroundDrawable);
         }
@@ -777,7 +740,7 @@
     }
 
     /**
-     * Makes the background visible on the given Window.  The background manager must be attached
+     * Makes the background visible on the given Window. The background manager must be attached
      * when the background is set.
      */
     public void attach(Window window) {
@@ -819,7 +782,10 @@
         attachToView(backgroundView);
     }
 
-    private void attachToView(View sceneRoot) {
+    /**
+     * Adds the composite drawable to the given view.
+     */
+    public void attachToView(View sceneRoot) {
         mBgView = sceneRoot;
         mAttached = true;
         syncWithService();
@@ -1023,14 +989,14 @@
     }
 
     /**
-     * Sets the given bitmap into the background. When using setBitmap to set the
+     * Sets the given bitmap into the background. When using setCoverImageBitmap to set the
      * background, the provided bitmap will be scaled and cropped to correctly
      * fit within the dimensions of the view. The timing for when this becomes
      * visible in the app is undefined and may take place after a small delay.
      */
     public void setBitmap(Bitmap bitmap) {
         if (DEBUG) {
-            Log.v(TAG, "setBitmap " + bitmap);
+            Log.v(TAG, "setCoverImageBitmap " + bitmap);
         }
 
         if (bitmap == null) {
@@ -1066,8 +1032,10 @@
             matrix.setScale(scale, scale);
             matrix.preTranslate(-dx, 0);
 
-            if (DEBUG) Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight() +
-                    " scale " + scale + " dx " + dx);
+            if (DEBUG) {
+                Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight()
+                        + " scale " + scale + " dx " + dx);
+            }
         }
 
         BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap, matrix);
@@ -1193,6 +1161,7 @@
     }
 
     private void showWallpaper(boolean show) {
+        if (DEBUG) Log.v(TAG, "showWallpaper called with " + show);
         if (mWindow == null) {
             return;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundSupportFragment.java
deleted file mode 100644
index 72cb5a1..0000000
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundSupportFragment.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/* This file is auto-generated from BackgroundFragment.java.  DO NOT MODIFY. */
-
-/*
- * 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.v17.leanback.app;
-
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.Fragment;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-/**
- * Fragment used by the background manager.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public final class BackgroundSupportFragment extends Fragment implements
-        BackgroundManager.FragmentStateQueriable {
-    private BackgroundManager mBackgroundManager;
-
-    void setBackgroundManager(BackgroundManager backgroundManager) {
-        mBackgroundManager = backgroundManager;
-    }
-
-    BackgroundManager getBackgroundManager() {
-        return mBackgroundManager;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        // mBackgroundManager might be null:
-        // if BackgroundSupportFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.onActivityStart();
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        // mBackgroundManager might be null:
-        // if BackgroundSupportFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.onResume();
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        // mBackgroundManager might be null:
-        // if BackgroundSupportFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.detach();
-        }
-    }
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
index 0f6aae3..3b4c851 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
@@ -13,17 +13,15 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.util.StateMachine.STATUS_EXECUTED;
+
 import android.os.Bundle;
-import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.transition.TransitionListener;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
 import android.support.v17.leanback.util.StateMachine;
 import android.support.v17.leanback.util.StateMachine.State;
-
-import static android.support.v17.leanback.util.StateMachine.*;
+import android.view.View;
+import android.view.ViewTreeObserver;
 
 /**
  * @hide
@@ -217,11 +215,14 @@
     void onExecuteEntranceTransition() {
         // wait till views get their initial position before start transition
         final View view = getView();
-        view.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
+        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
             @Override
             public boolean onPreDraw() {
                 view.getViewTreeObserver().removeOnPreDrawListener(this);
+                if (getActivity() == null || getView() == null) {
+                    // bail out if fragment is destroyed immediately after startEntranceTransition
+                    return true;
+                }
                 internalCreateEntranceTransition();
                 if (mEntranceTransition != null) {
                     onEntranceTransitionStart();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
index c9489d1..98a8f98 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
@@ -219,7 +219,7 @@
         }
     }
 
-    final VerticalGridView getVerticalGridView() {
+    public final VerticalGridView getVerticalGridView() {
         return mVerticalGridView;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
index 6fc741d..281c493 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseRowSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BaseRowFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -221,7 +222,7 @@
         }
     }
 
-    final VerticalGridView getVerticalGridView() {
+    public final VerticalGridView getVerticalGridView() {
         return mVerticalGridView;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
index 8a818e0..8de54a7 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BaseFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,17 +16,15 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.util.StateMachine.STATUS_EXECUTED;
+
 import android.os.Bundle;
-import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.transition.TransitionListener;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
 import android.support.v17.leanback.util.StateMachine;
 import android.support.v17.leanback.util.StateMachine.State;
-
-import static android.support.v17.leanback.util.StateMachine.*;
+import android.view.View;
+import android.view.ViewTreeObserver;
 
 /**
  * @hide
@@ -219,11 +218,14 @@
     void onExecuteEntranceTransition() {
         // wait till views get their initial position before start transition
         final View view = getView();
-        view.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
+        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
             @Override
             public boolean onPreDraw() {
                 view.getViewTreeObserver().removeOnPreDrawListener(this);
+                if (getActivity() == null || getView() == null) {
+                    // bail out if fragment is destroyed immediately after startEntranceTransition
+                    return true;
+                }
                 internalCreateEntranceTransition();
                 if (mEntranceTransition != null) {
                     onEntranceTransitionStart();
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java
index ee5c479..1a0d81f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrandedSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrandedFragment.java.  DO NOT MODIFY. */
 
 /*
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 0f7cfa0..4630c79 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -13,6 +13,8 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentManager.BackStackEntry;
@@ -52,8 +54,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
 /**
  * A fragment for creating Leanback browse screens. It is composed of a
  * RowsFragment and a HeadersFragment.
@@ -502,7 +502,6 @@
             item = adapter.get(position);
         }
 
-        mSelectedPosition = position;
         boolean oldIsPageRow = mIsPageRow;
         mIsPageRow = item instanceof PageRow;
         boolean swap;
@@ -610,8 +609,8 @@
     /** The headers fragment is disabled and will never be shown. */
     public static final int HEADERS_DISABLED = 3;
 
-    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry
-            = new MainFragmentAdapterRegistry();
+    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry =
+            new MainFragmentAdapterRegistry();
     MainFragmentAdapter mMainFragmentAdapter;
     Fragment mMainFragment;
     HeadersFragment mHeadersFragment;
@@ -803,6 +802,13 @@
     }
 
     /**
+     * @return Current main fragment or null if not created.
+     */
+    public Fragment getMainFragment() {
+        return mMainFragment;
+    }
+
+    /**
      * Get currently bound HeadersFragment or null if HeadersFragment has not been created yet.
      * @return Currently bound HeadersFragment or null if HeadersFragment has not been created yet.
      */
@@ -949,14 +955,14 @@
             }
             if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
 
-            if (getTitleView() != null && focused != getTitleView() &&
-                    direction == View.FOCUS_UP) {
+            if (getTitleView() != null && focused != getTitleView()
+                    && direction == View.FOCUS_UP) {
                 return getTitleView();
             }
-            if (getTitleView() != null && getTitleView().hasFocus() &&
-                    direction == View.FOCUS_DOWN) {
-                return mCanShowHeaders && mShowingHeaders ?
-                        mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
+            if (getTitleView() != null && getTitleView().hasFocus()
+                    && direction == View.FOCUS_DOWN) {
+                return mCanShowHeaders && mShowingHeaders
+                        ? mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
             boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
@@ -997,17 +1003,18 @@
             }
             // Make sure not changing focus when requestFocus() is called.
             if (mCanShowHeaders && mShowingHeaders) {
-                if (mHeadersFragment != null && mHeadersFragment.getView() != null &&
-                        mHeadersFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
+                if (mHeadersFragment != null && mHeadersFragment.getView() != null
+                        && mHeadersFragment.getView().requestFocus(
+                                direction, previouslyFocusedRect)) {
                     return true;
                 }
             }
-            if (mMainFragment != null && mMainFragment.getView() != null &&
-                    mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
+            if (mMainFragment != null && mMainFragment.getView() != null
+                    && mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
-            if (getTitleView() != null &&
-                    getTitleView().requestFocus(direction, previouslyFocusedRect)) {
+            if (getTitleView() != null
+                    && getTitleView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
             return false;
@@ -1120,11 +1127,11 @@
                     .getMainFragmentAdapter();
             mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
 
-            mIsPageRow = savedInstanceState != null ?
-                    savedInstanceState.getBoolean(IS_PAGE_ROW, false) : false;
+            mIsPageRow = savedInstanceState != null
+                    ? savedInstanceState.getBoolean(IS_PAGE_ROW, false) : false;
 
-            mSelectedPosition = savedInstanceState != null ?
-                    savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
+            mSelectedPosition = savedInstanceState != null
+                    ? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
 
             if (!mIsPageRow) {
                 if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
@@ -1212,8 +1219,8 @@
 
     void createHeadersTransition() {
         mHeadersTransition = TransitionHelper.loadTransition(getActivity(),
-                mShowingHeaders ?
-                R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
+                mShowingHeaders
+                        ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
 
         TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
             @Override
@@ -1410,13 +1417,17 @@
             return;
         }
 
+        mSelectedPosition = position;
+        if (mHeadersFragment == null || mMainFragmentAdapter == null) {
+            // onDestroyView() called
+            return;
+        }
         mHeadersFragment.setSelectedPosition(position, smooth);
         replaceMainFragment(position);
 
         if (mMainFragmentRowsAdapter != null) {
             mMainFragmentRowsAdapter.setSelectedPosition(position, smooth);
         }
-        mSelectedPosition = position;
 
         updateTitleViewVisibility();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index c88438e..4979a24 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,6 +16,8 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentManager.BackStackEntry;
@@ -54,8 +57,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
 /**
  * A fragment for creating Leanback browse screens. It is composed of a
  * RowsSupportFragment and a HeadersSupportFragment.
@@ -504,7 +505,6 @@
             item = adapter.get(position);
         }
 
-        mSelectedPosition = position;
         boolean oldIsPageRow = mIsPageRow;
         mIsPageRow = item instanceof PageRow;
         boolean swap;
@@ -612,8 +612,8 @@
     /** The headers fragment is disabled and will never be shown. */
     public static final int HEADERS_DISABLED = 3;
 
-    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry
-            = new MainFragmentAdapterRegistry();
+    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry =
+            new MainFragmentAdapterRegistry();
     MainFragmentAdapter mMainFragmentAdapter;
     Fragment mMainFragment;
     HeadersSupportFragment mHeadersSupportFragment;
@@ -805,6 +805,13 @@
     }
 
     /**
+     * @return Current main fragment or null if not created.
+     */
+    public Fragment getMainFragment() {
+        return mMainFragment;
+    }
+
+    /**
      * Get currently bound HeadersSupportFragment or null if HeadersSupportFragment has not been created yet.
      * @return Currently bound HeadersSupportFragment or null if HeadersSupportFragment has not been created yet.
      */
@@ -951,14 +958,14 @@
             }
             if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
 
-            if (getTitleView() != null && focused != getTitleView() &&
-                    direction == View.FOCUS_UP) {
+            if (getTitleView() != null && focused != getTitleView()
+                    && direction == View.FOCUS_UP) {
                 return getTitleView();
             }
-            if (getTitleView() != null && getTitleView().hasFocus() &&
-                    direction == View.FOCUS_DOWN) {
-                return mCanShowHeaders && mShowingHeaders ?
-                        mHeadersSupportFragment.getVerticalGridView() : mMainFragment.getView();
+            if (getTitleView() != null && getTitleView().hasFocus()
+                    && direction == View.FOCUS_DOWN) {
+                return mCanShowHeaders && mShowingHeaders
+                        ? mHeadersSupportFragment.getVerticalGridView() : mMainFragment.getView();
             }
 
             boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
@@ -999,17 +1006,18 @@
             }
             // Make sure not changing focus when requestFocus() is called.
             if (mCanShowHeaders && mShowingHeaders) {
-                if (mHeadersSupportFragment != null && mHeadersSupportFragment.getView() != null &&
-                        mHeadersSupportFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
+                if (mHeadersSupportFragment != null && mHeadersSupportFragment.getView() != null
+                        && mHeadersSupportFragment.getView().requestFocus(
+                                direction, previouslyFocusedRect)) {
                     return true;
                 }
             }
-            if (mMainFragment != null && mMainFragment.getView() != null &&
-                    mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
+            if (mMainFragment != null && mMainFragment.getView() != null
+                    && mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
-            if (getTitleView() != null &&
-                    getTitleView().requestFocus(direction, previouslyFocusedRect)) {
+            if (getTitleView() != null
+                    && getTitleView().requestFocus(direction, previouslyFocusedRect)) {
                 return true;
             }
             return false;
@@ -1122,11 +1130,11 @@
                     .getMainFragmentAdapter();
             mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
 
-            mIsPageRow = savedInstanceState != null ?
-                    savedInstanceState.getBoolean(IS_PAGE_ROW, false) : false;
+            mIsPageRow = savedInstanceState != null
+                    ? savedInstanceState.getBoolean(IS_PAGE_ROW, false) : false;
 
-            mSelectedPosition = savedInstanceState != null ?
-                    savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
+            mSelectedPosition = savedInstanceState != null
+                    ? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
 
             if (!mIsPageRow) {
                 if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
@@ -1214,8 +1222,8 @@
 
     void createHeadersTransition() {
         mHeadersTransition = TransitionHelper.loadTransition(getActivity(),
-                mShowingHeaders ?
-                R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
+                mShowingHeaders
+                        ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
 
         TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
             @Override
@@ -1412,13 +1420,17 @@
             return;
         }
 
+        mSelectedPosition = position;
+        if (mHeadersSupportFragment == null || mMainFragmentAdapter == null) {
+            // onDestroyView() called
+            return;
+        }
         mHeadersSupportFragment.setSelectedPosition(position, smooth);
         replaceMainFragment(position);
 
         if (mMainFragmentRowsAdapter != null) {
             mMainFragmentRowsAdapter.setSelectedPosition(position, smooth);
         }
-        mSelectedPosition = position;
 
         updateTitleViewVisibility();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundParallaxHelper.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundParallaxHelper.java
new file mode 100644
index 0000000..48d2060
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundParallaxHelper.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.graphics.BoundsRule;
+import android.support.v17.leanback.graphics.CompositeDrawable;
+import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
+import android.support.v17.leanback.widget.Parallax;
+import android.support.v17.leanback.widget.ParallaxRecyclerViewSource;
+import android.util.TypedValue;
+
+/**
+ * Helper class responsible for wiring in parallax effect in
+ * {@link android.support.v17.leanback.app.DetailsFragment}. The default effect will render
+ * a drawable like the following -
+ * <pre>
+ *        ***************************
+ *        *        Cover Image      *
+ *        ***************************
+ *        *    DetailsOverviewRow   *
+ *        *                         *
+ *        ***************************
+ *        *        Solid Color      *
+ *        *         Related         *
+ *        *         Content         *
+ *        ***************************
+ * </pre>
+ * As the user scrolls through the page, the bounds of the bitmap and related content section
+ * will be updated to simulate the parallax effect. Users have to do the following to setup the
+ * parallax -
+ *
+ * <ul>
+ * <li>First users should use {@link ParallaxBuilder} class to set the appropriate attributes
+ * and call build() to create an instance of {@link DetailsBackgroundParallaxHelper}.
+ * Users must set {@link DetailsParallaxManager} on {@link ParallaxBuilder} for it to obtain the
+ * {@link Parallax} instance. Finally they should set the drawable obtained by calling
+ * {@link #getDrawable} as the background of their current activity.
+ * <pre>
+ * {@code
+ *     public void onStart() {
+ *         super.onStart();
+ *         mParallaxHelper = DetailsBackgroundParallaxHelper.ParallaxBuilder
+ *             .newBuilder(parallaxManager, context)
+ *             .setCoverImageMinVerticalOffset(-300)
+ *             .build();
+ *          mBackgroundManager.setDrawable(mParallaxHelper.getDrawable());
+ *      }
+ * }
+ * </pre>
+ * </li>
+ * </li>
+ * <li>Finally, users can set the bitmap through {@link #setCoverImageBitmap(Bitmap)} call.
+ * <pre>
+ * {@code
+ *     public void onBitmapLoaded(Bitmap bitmap) {
+ *         mParallaxHelper.setCoverImageBitmap(bitmap);
+ *     }
+ * }
+ * </pre>
+ * </li>
+ * </ul>
+ *
+ * In case the color is not set, it will use defaultBrandColorDark from LeanbackTheme.
+ */
+public final class DetailsBackgroundParallaxHelper {
+    private DetailsParallaxManager mDetailsParallaxManager;
+    private CompositeDrawable mCompositeDrawable;
+    private FitWidthBitmapDrawable mCoverImageDrawable;
+    private ColorDrawable mSolidColorDrawable;
+    private int mCoverImageMinVerticalOffset;
+
+    DetailsBackgroundParallaxHelper(
+            Context context,
+            DetailsParallaxManager detailsParallaxManager,
+            int coverImageMinVerticalOffset,
+            int color) {
+        this.mCoverImageMinVerticalOffset = coverImageMinVerticalOffset;
+        mCompositeDrawable = new CompositeDrawable();
+        mCoverImageDrawable = new FitWidthBitmapDrawable();
+        mSolidColorDrawable = new ColorDrawable(color);
+        mCompositeDrawable.addChildDrawable(mCoverImageDrawable);
+        mCompositeDrawable.addChildDrawable(mSolidColorDrawable);
+        mCompositeDrawable.getChildAt(0).getBoundsRule().mBottom = BoundsRule.inheritFromParent(1f);
+        mCompositeDrawable.getChildAt(1).getBoundsRule().mTop = BoundsRule.inheritFromParent(1f);
+        mDetailsParallaxManager = detailsParallaxManager;
+        setupParallaxEffect(context);
+    }
+
+    /**
+     * Returns the first child of {@link CompositeDrawable} which is the cover image.
+     */
+    public Drawable getCoverImageDrawable() {
+        return mCompositeDrawable.getChildAt(0).getDrawable();
+    }
+
+    /**
+     * Builder class used for creating an instance of {@link DetailsBackgroundParallaxHelper}.
+     */
+    public static class ParallaxBuilder {
+        // Default value for image translation is -100px.
+        private int mCoverImageMinVerticalOffset = -100;
+        private int mColor;
+        private boolean mIsColorSet;
+        private final DetailsParallaxManager mDetailsParallaxManager;
+        private final Context mContext;
+
+        /**
+         * Returns an instance of itself.
+         *
+         * @param detailsParallaxManager class responsible for creating {@link Parallax} instance.
+         * @param context Context used for loading resources.
+         */
+        public ParallaxBuilder(@NonNull Context context,
+                               @NonNull DetailsParallaxManager detailsParallaxManager) {
+            if (detailsParallaxManager == null || context == null) {
+                throw new IllegalArgumentException("Must set DetailsParallaxManager and Context.");
+            }
+            this.mDetailsParallaxManager = detailsParallaxManager;
+            this.mContext = context;
+        }
+
+        /**
+         * Sets the minimum top position the image is going to translate to during the
+         * parallax motion.
+         */
+        public ParallaxBuilder setCoverImageMinVerticalOffset(int minTop) {
+            this.mCoverImageMinVerticalOffset = minTop;
+            return this;
+        }
+
+        /**
+         * Sets the color for the bottom section of the
+         * {@link android.support.v17.leanback.app.DetailsFragment}.
+         */
+        public ParallaxBuilder setColor(int color) {
+            this.mColor = color;
+            mIsColorSet = true;
+            return this;
+        }
+
+        /**
+         * Builds and returns an instance of {@link DetailsBackgroundParallaxHelper}.
+         */
+        public DetailsBackgroundParallaxHelper build() {
+            if (!mIsColorSet) {
+                mColor = getDefaultBackgroundColor(mContext);
+            }
+
+            return new DetailsBackgroundParallaxHelper(mContext,
+                    mDetailsParallaxManager, mCoverImageMinVerticalOffset, mColor);
+        }
+
+        private int getDefaultBackgroundColor(Context context) {
+            TypedValue outValue = new TypedValue();
+            if (context.getTheme().resolveAttribute(R.attr.defaultBrandColorDark, outValue, true)) {
+                return context.getResources().getColor(outValue.resourceId);
+            }
+            return context.getResources().getColor(R.color.lb_default_brand_color_dark);
+        }
+    }
+
+    /**
+     * Returns the special drawable instance that is used to simulate the parallax effect. Users
+     * must set this drawable as the background for their activity.
+     */
+    public Drawable getDrawable() {
+        return mCompositeDrawable;
+    }
+
+    /**
+     * Sets the bitmap in drawable instance returned during {@link #getDrawable()} call.
+     */
+    public void setCoverImageBitmap(Bitmap bitmap) {
+        if (bitmap == null) {
+            throw new IllegalArgumentException("Invalid bitmap");
+        }
+        mCoverImageDrawable.setBitmap(bitmap);
+    }
+
+    /**
+     * Changes the background color of the related content section.
+     */
+    public void setColor(@ColorInt int color) {
+        mSolidColorDrawable.setColor(color);
+    }
+
+    /**
+     * Sets up the cover image parallax effect in {@link DetailsFragment}.
+     */
+    private void setupParallaxEffect(Context context) {
+        // Add cover image parallax effect:
+        // When frameTop moves from half of the screen to top of the screen,
+        // change vertical offset of Bitmap from 0 to -100
+
+        Parallax parallax = mDetailsParallaxManager.getParallax();
+        ParallaxRecyclerViewSource.ChildPositionProperty frameTop =
+                mDetailsParallaxManager.getFrameTop();
+        ParallaxRecyclerViewSource.ChildPositionProperty frameBottom =
+                mDetailsParallaxManager.getFrameBottom();
+        // The values are from DetailsFragment.setupDetailsOverviewRowPresenter()
+        final int fromValue = context.getResources()
+                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions);
+        final int toValue = context.getResources()
+                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description);
+        parallax.addEffect(frameTop.atAbsolute(fromValue), frameTop.atFraction(toValue))
+                .target(mCoverImageDrawable,
+                    PropertyValuesHolder.ofInt("verticalOffset", 0, mCoverImageMinVerticalOffset));
+
+        // Add solid color parallax effect:
+        // When frameBottom moves from bottom of the screen to top of the screen,
+        // change solid ColorDrawable's top from bottom of screen to top of the screen.
+        // Also we are changing the drawing the bitmap from bottom to top of the screen.
+        parallax.addEffect(frameBottom.atFraction(1f), frameBottom.atFraction(0f))
+                .target(mCompositeDrawable.getChildAt(1),
+                        PropertyValuesHolder.ofFloat(
+                                CompositeDrawable.ChildDrawable.TOP_FRACTION, 1f, 0f))
+                .target(mCompositeDrawable.getChildAt(0),
+                        PropertyValuesHolder.ofFloat(
+                                CompositeDrawable.ChildDrawable.BOTTOM_FRACTION, 1f, 0f));
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index ee733a6..1c7d80c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -13,23 +13,24 @@
  */
 package android.support.v17.leanback.app;
 
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
 import android.support.v17.leanback.widget.ItemAlignmentFacet;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.TitleHelper;
 import android.support.v17.leanback.widget.VerticalGridView;
-import android.os.Bundle;
 import android.util.Log;
-import android.util.TypedValue;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -88,18 +89,20 @@
         }
     }
 
+    BrowseFrameLayout mRootView;
+    Fragment mVideoFragment;
+    DetailsParallaxManager mDetailsParallaxManager;
     RowsFragment mRowsFragment;
-
-    private ObjectAdapter mAdapter;
-    private int mContainerListAlignTop;
+    ObjectAdapter mAdapter;
+    int mContainerListAlignTop;
     BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    private BaseOnItemViewClickedListener mOnItemViewClickedListener;
+    BaseOnItemViewClickedListener mOnItemViewClickedListener;
 
-    private Object mSceneAfterEntranceTransition;
+    Object mSceneAfterEntranceTransition;
 
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
+    final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
-    private final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
+    final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
             new BaseOnItemViewSelectedListener<Object>() {
         @Override
         public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
@@ -170,7 +173,6 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         mContainerListAlignTop =
             getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
     }
@@ -178,9 +180,8 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.lb_details_fragment, container, false);
-        ViewGroup fragment_root = (ViewGroup) view.findViewById(R.id.details_fragment_root);
-        installTitleView(inflater, fragment_root, savedInstanceState);
+        mRootView = (BrowseFrameLayout) inflater.inflate(
+                R.layout.lb_details_fragment, container, false);
         mRowsFragment = (RowsFragment) getChildFragmentManager().findFragmentById(
                 R.id.details_rows_dock);
         if (mRowsFragment == null) {
@@ -192,16 +193,17 @@
         mRowsFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
         mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(
-                (ViewGroup) view, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
             @Override
             public void run() {
                 mRowsFragment.setEntranceTransitionState(true);
             }
         });
-        return view;
+
+        return mRootView;
     }
 
+
     /**
      * @deprecated override {@link #onInflateTitleView(LayoutInflater,ViewGroup,Bundle)} instead.
      */
@@ -227,9 +229,9 @@
     }
 
     /**
-     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.  Note
-     * that setup should only change the Presenter behavior that is meaningful in DetailsFragment.  For
-     * example how a row is aligned in details Fragment.   The default implementation invokes
+     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
+     * that setup should only change the Presenter behavior that is meaningful in DetailsFragment.
+     * For example how a row is aligned in details Fragment.   The default implementation invokes
      * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
      *
      */
@@ -286,15 +288,6 @@
         setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
     }
 
-    private void setupFocusSearchListener() {
-        TitleHelper titleHelper = getTitleHelper();
-        if (titleHelper != null) {
-            BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
-                    R.id.details_fragment_root);
-            browseFrameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
-        }
-    }
-
     /**
      * Sets the selected row position with smooth animation.
      */
@@ -313,10 +306,58 @@
         }
     }
 
+    /**
+     * Creates an instance of {@link VideoFragment}. Subclasses can override this method
+     * and provide their own instance of a {@link Fragment}. When you provide your own instance of
+     * video fragment, you MUST also provide a custom
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
+     */
+    public Fragment onCreateVideoFragment() {
+        return new VideoFragment();
+    }
+
+    /**
+     * Creates an instance of
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}. The implementation
+     * of this host depends on the instance of video fragment {@link #onCreateVideoFragment()}.
+     */
+    public PlaybackGlue.PlaybackGlueHost onCreateVideoFragmentHost(Fragment fragment) {
+        return new VideoFragmentGlueHost((VideoFragment) fragment);
+    }
+
+    /**
+     * This method adds a fragment for rendering video to the layout. In case the
+     * fragment is being restored, it will return the video fragment in there.
+     *
+     * @return Fragment the added or restored fragment responsible for rendering video.
+     */
+    public final Fragment findOrCreateVideoFragment() {
+        Fragment fragment = getFragmentManager().findFragmentById(R.id.video_surface_container);
+        if (fragment == null) {
+            FragmentTransaction ft2 = getFragmentManager().beginTransaction();
+            ft2.add(android.support.v17.leanback.R.id.video_surface_container,
+                    fragment = onCreateVideoFragment());
+            ft2.commit();
+            setupVideoPlayback();
+        }
+        mVideoFragment = fragment;
+        return mVideoFragment;
+    }
+
+    /**
+     * This method initializes a video fragment, create an instance of
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} using that fragment
+     * and return it.
+     */
+    public final PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost() {
+        Fragment fragment = findOrCreateVideoFragment();
+        return onCreateVideoFragmentHost(fragment);
+    }
+
     void onRowSelected(int selectedPosition, int selectedSubPosition) {
         ObjectAdapter adapter = getAdapter();
-        if (adapter == null || adapter.size() == 0 ||
-                (selectedPosition == 0 && selectedSubPosition == 0)) {
+        if (adapter == null || adapter.size() == 0
+                || (selectedPosition == 0 && selectedSubPosition == 0)) {
             showTitle(true);
         } else {
             showTitle(false);
@@ -395,10 +436,10 @@
     public void onStart() {
         super.onStart();
         setupChildFragmentLayout();
-        setupFocusSearchListener();
         if (isEntranceTransitionEnabled()) {
             mRowsFragment.setEntranceTransitionState(false);
         }
+        mRowsFragment.getVerticalGridView().requestFocus();
     }
 
     @Override
@@ -426,4 +467,85 @@
     protected void onEntranceTransitionStart() {
         mRowsFragment.onTransitionStart();
     }
+
+    /**
+     * Returns the {@link DetailsParallaxManager} instance used to configure
+     * {@link android.support.v17.leanback.widget.Parallax} instance.
+     */
+    public DetailsParallaxManager getParallaxManager() {
+        if (mDetailsParallaxManager == null) {
+            mDetailsParallaxManager = new DetailsParallaxManager(
+                    getRowsFragment().getVerticalGridView());
+        }
+        return mDetailsParallaxManager;
+    }
+
+    /**
+     * This method does the following
+     * <ul>
+     * <li>sets up focus search handling logic in the root view to enable transitioning between
+     * half screen/full screen/no video mode.</li>
+     *
+     * <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
+     * transition to appropriate mode like half/full screen video.</li>
+     * </ul>
+     */
+    void setupVideoPlayback() {
+        mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
+            @Override
+            public View onFocusSearch(View focused, int direction) {
+                if (mVideoFragment == null) {
+                    return null;
+                }
+                if (mRowsFragment.getVerticalGridView() != null
+                        && mRowsFragment.getVerticalGridView().hasFocus()) {
+                    if (direction == View.FOCUS_UP) {
+                        slideOutGridView();
+                        return mVideoFragment.getView();
+                    }
+                } else if (mVideoFragment.getView() != null
+                        && mVideoFragment.getView().hasFocus()) {
+                    if (direction == View.FOCUS_DOWN) {
+                        slideInGridView();
+                        return mRowsFragment.getVerticalGridView();
+                    }
+                }
+                return focused;
+            }
+        });
+
+        // If we press BACK or DOWN on remote while in full screen video mode, we should
+        // transition back to half screen video playback mode.
+        mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                // This is used to check if we are in full screen video mode. This is somewhat
+                // hacky and relies on the behavior of the video helper class to update the
+                // focusability of the video surface view.
+                if (mVideoFragment.getView() != null && mVideoFragment.getView().hasFocus()) {
+                    if (keyCode == KeyEvent.KEYCODE_BACK) {
+                        slideInGridView();
+                        getVerticalGridView().requestFocus();
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        });
+    }
+
+    /**
+     * Slides vertical grid view (displaying media item details) out of the screen from below.
+     */
+    void slideOutGridView() {
+        getVerticalGridView().animateOut();
+    }
+
+    /**
+     * Slides in vertical grid view (displaying media item details) from below.
+     */
+    void slideInGridView() {
+        getVerticalGridView().animateIn();
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java
new file mode 100644
index 0000000..d9b8906
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java
@@ -0,0 +1,177 @@
+/*
+ * 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.v17.leanback.app;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.graphics.CompositeDrawable;
+import android.support.v17.leanback.widget.Parallax;
+import android.support.v17.leanback.widget.ParallaxRecyclerViewSource;
+import android.support.v17.leanback.widget.ParallaxTarget;
+
+/**
+ * Helper class responsible for setting up video playback in {@link DetailsFragment}. This
+ * takes {@link DetailsFragment} and {@link PlaybackGlue} as input and configures them. This
+ * class is also responsible for implementing
+ * {@link android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener} and
+ * {@link android.support.v7.widget.RecyclerView.OnScrollListener} in {@link DetailsFragment}.
+ */
+public class DetailsFragmentVideoHelper {
+    private static final long BACKGROUND_CROSS_FADE_DURATION = 500;
+    private static final long CROSSFADE_DELAY = 1000;
+
+    /**
+     * Different states {@link DetailsFragment} can be in.
+     */
+    enum STATE {
+        INITIAL,
+        PLAY_VIDEO,
+        NO_VIDEO
+    }
+
+    private final DetailsParallaxManager mParallaxManager;
+    private STATE mCurrentState = STATE.INITIAL;
+
+    private ValueAnimator mBackgroundAnimator;
+    private Drawable mBackgroundDrawable;
+    private PlaybackGlue mPlaybackGlue;
+
+    /**
+     * Constructor.
+     */
+    public DetailsFragmentVideoHelper(
+            PlaybackGlue playbackGlue,
+            DetailsParallaxManager parallaxManager) {
+        this.mPlaybackGlue = playbackGlue;
+        this.mParallaxManager = parallaxManager;
+        setupParallax();
+    }
+
+    void setupParallax() {
+        Parallax parallax = mParallaxManager.getParallax();
+        ParallaxRecyclerViewSource.ChildPositionProperty frameTop = mParallaxManager.getFrameTop();
+        final float maxFrameTop = 1f;
+        final float minFrameTop = 0f;
+        parallax.addEffect(frameTop.atFraction(maxFrameTop), frameTop.atFraction(minFrameTop))
+                .target(new ParallaxTarget() {
+
+                    float mFraction;
+                    @Override
+                    public void update(float fraction) {
+                        if (fraction == maxFrameTop) {
+                            updateState(STATE.NO_VIDEO);
+                        } else {
+                            updateState(STATE.PLAY_VIDEO);
+                        }
+                        mFraction = fraction;
+                    }
+
+                    @Override
+                    public float getFraction() {
+                        return mFraction;
+                    }
+                });
+    }
+
+    private void updateState(STATE state) {
+        if (state == mCurrentState) {
+            return;
+        }
+        mCurrentState = state;
+        switch (state) {
+            case PLAY_VIDEO:
+                if (mPlaybackGlue.isReadyForPlayback()) {
+                    internalStartPlayback();
+                } else {
+                    mPlaybackGlue.setPlayerCallback(new PlaybackControlStateCallback());
+                }
+                break;
+            case NO_VIDEO:
+                crossFadeBackgroundToVideo(false);
+                mPlaybackGlue.setPlayerCallback(null);
+                mPlaybackGlue.pause();
+                break;
+        }
+    }
+
+    private void internalStartPlayback() {
+        mPlaybackGlue.play();
+        mParallaxManager.getRecyclerView().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                crossFadeBackgroundToVideo(true);
+            }
+        }, CROSSFADE_DELAY);
+    }
+
+    private void crossFadeBackgroundToVideo(final boolean crossFadeToVideo) {
+        if (mBackgroundAnimator != null) {
+            mBackgroundAnimator.cancel();
+        }
+
+        float startAlpha = crossFadeToVideo ? 1f : 0f;
+        float endAlpha = crossFadeToVideo ? 0f : 1f;
+
+        mBackgroundAnimator = ValueAnimator.ofFloat(startAlpha, endAlpha);
+        mBackgroundAnimator.setDuration(BACKGROUND_CROSS_FADE_DURATION);
+        mBackgroundAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mBackgroundDrawable.setAlpha(
+                        (int) ((Float) (valueAnimator.getAnimatedValue()) * 255));
+            }
+        });
+
+        mBackgroundAnimator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animator) {
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                mBackgroundAnimator = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animator) {
+            }
+        });
+
+        mBackgroundAnimator.start();
+    }
+
+    /**
+     * Sets the drawable to be used as background image for {@link DetailsFragment}. If set,
+     * we will cross fade from the background drawable to the video.
+     */
+    public void setBackgroundDrawable(Drawable drawable) {
+        this.mBackgroundDrawable = drawable;
+    }
+
+    private class PlaybackControlStateCallback extends PlaybackGlue.PlayerCallback {
+
+        @Override
+        public void onReadyForPlayback() {
+            internalStartPlayback();
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsParallaxManager.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsParallaxManager.java
new file mode 100644
index 0000000..8aa2e1d
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsParallaxManager.java
@@ -0,0 +1,82 @@
+/*
+ * 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.v17.leanback.app;
+
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.Parallax;
+import android.support.v17.leanback.widget.ParallaxRecyclerViewSource;
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * Class in charge of managing the {@link Parallax} object for {@link DetailsFragment}. This
+ * can be shared for creating both parallax effect and video animations when transitioning to/from
+ * half/full screen.
+ */
+public class DetailsParallaxManager {
+    private final RecyclerView mRecyclerView;
+    private final ParallaxRecyclerViewSource mParallaxSource;
+    private final ParallaxRecyclerViewSource.ChildPositionProperty mFrameTop;
+    private final ParallaxRecyclerViewSource.ChildPositionProperty mFrameBottom;
+    private Parallax mParallax;
+
+    public DetailsParallaxManager(RecyclerView recyclerView) {
+        this.mRecyclerView = recyclerView;
+        mParallaxSource = new ParallaxRecyclerViewSource(mRecyclerView);
+
+        // track the top edge of details_frame of first item of adapter
+        mFrameTop = mParallaxSource
+                .addProperty("frameTop")
+                .adapterPosition(0)
+                .viewId(R.id.details_frame);
+
+        // track the bottom edge of details_frame of first item of adapter
+        mFrameBottom = mParallaxSource
+                .addProperty("frameBottom")
+                .adapterPosition(0)
+                .viewId(R.id.details_frame)
+                .fraction(1.0f);
+
+        mParallax = new Parallax();
+        mParallax.setSource(mParallaxSource);
+    }
+
+    /**
+     * Returns the {@link Parallax} instance.
+     */
+    public Parallax getParallax() {
+        return mParallax;
+    }
+
+    public RecyclerView getRecyclerView() {
+        return mRecyclerView;
+    }
+
+    /**
+     * Returns the top of the details overview row. This is tracked for implementing the
+     * parallax effect.
+     */
+    public ParallaxRecyclerViewSource.ChildPositionProperty getFrameTop() {
+        return mFrameTop;
+    }
+
+    /**
+     * Returns the bottom of the details overview row. This is tracked for implementing the
+     * parallax effect.
+     */
+    public ParallaxRecyclerViewSource.ChildPositionProperty getFrameBottom() {
+        return mFrameBottom;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
index af23da9..f663bbc 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from DetailsFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,23 +16,24 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.os.Bundle;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
 import android.support.v17.leanback.widget.ItemAlignmentFacet;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.TitleHelper;
 import android.support.v17.leanback.widget.VerticalGridView;
-import android.os.Bundle;
 import android.util.Log;
-import android.util.TypedValue;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -90,18 +92,20 @@
         }
     }
 
+    BrowseFrameLayout mRootView;
+    Fragment mVideoSupportFragment;
+    DetailsParallaxManager mDetailsParallaxManager;
     RowsSupportFragment mRowsSupportFragment;
-
-    private ObjectAdapter mAdapter;
-    private int mContainerListAlignTop;
+    ObjectAdapter mAdapter;
+    int mContainerListAlignTop;
     BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    private BaseOnItemViewClickedListener mOnItemViewClickedListener;
+    BaseOnItemViewClickedListener mOnItemViewClickedListener;
 
-    private Object mSceneAfterEntranceTransition;
+    Object mSceneAfterEntranceTransition;
 
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
+    final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
 
-    private final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
+    final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
             new BaseOnItemViewSelectedListener<Object>() {
         @Override
         public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
@@ -172,7 +176,6 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         mContainerListAlignTop =
             getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
     }
@@ -180,9 +183,8 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.lb_details_fragment, container, false);
-        ViewGroup fragment_root = (ViewGroup) view.findViewById(R.id.details_fragment_root);
-        installTitleView(inflater, fragment_root, savedInstanceState);
+        mRootView = (BrowseFrameLayout) inflater.inflate(
+                R.layout.lb_details_fragment, container, false);
         mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
                 R.id.details_rows_dock);
         if (mRowsSupportFragment == null) {
@@ -194,16 +196,17 @@
         mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
         mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
 
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(
-                (ViewGroup) view, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
             @Override
             public void run() {
                 mRowsSupportFragment.setEntranceTransitionState(true);
             }
         });
-        return view;
+
+        return mRootView;
     }
 
+
     /**
      * @deprecated override {@link #onInflateTitleView(LayoutInflater,ViewGroup,Bundle)} instead.
      */
@@ -229,9 +232,9 @@
     }
 
     /**
-     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.  Note
-     * that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.  For
-     * example how a row is aligned in details Fragment.   The default implementation invokes
+     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
+     * that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.
+     * For example how a row is aligned in details Fragment.   The default implementation invokes
      * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
      *
      */
@@ -288,15 +291,6 @@
         setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
     }
 
-    private void setupFocusSearchListener() {
-        TitleHelper titleHelper = getTitleHelper();
-        if (titleHelper != null) {
-            BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
-                    R.id.details_fragment_root);
-            browseFrameLayout.setOnFocusSearchListener(titleHelper.getOnFocusSearchListener());
-        }
-    }
-
     /**
      * Sets the selected row position with smooth animation.
      */
@@ -315,10 +309,58 @@
         }
     }
 
+    /**
+     * Creates an instance of {@link VideoSupportFragment}. Subclasses can override this method
+     * and provide their own instance of a {@link Fragment}. When you provide your own instance of
+     * video fragment, you MUST also provide a custom
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
+     */
+    public Fragment onCreateVideoSupportFragment() {
+        return new VideoSupportFragment();
+    }
+
+    /**
+     * Creates an instance of
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}. The implementation
+     * of this host depends on the instance of video fragment {@link #onCreateVideoSupportFragment()}.
+     */
+    public PlaybackGlue.PlaybackGlueHost onCreateVideoSupportFragmentHost(Fragment fragment) {
+        return new VideoSupportFragmentGlueHost((VideoSupportFragment) fragment);
+    }
+
+    /**
+     * This method adds a fragment for rendering video to the layout. In case the
+     * fragment is being restored, it will return the video fragment in there.
+     *
+     * @return Fragment the added or restored fragment responsible for rendering video.
+     */
+    public final Fragment findOrCreateVideoSupportFragment() {
+        Fragment fragment = getFragmentManager().findFragmentById(R.id.video_surface_container);
+        if (fragment == null) {
+            FragmentTransaction ft2 = getFragmentManager().beginTransaction();
+            ft2.add(android.support.v17.leanback.R.id.video_surface_container,
+                    fragment = onCreateVideoSupportFragment());
+            ft2.commit();
+            setupVideoPlayback();
+        }
+        mVideoSupportFragment = fragment;
+        return mVideoSupportFragment;
+    }
+
+    /**
+     * This method initializes a video fragment, create an instance of
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} using that fragment
+     * and return it.
+     */
+    public final PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost() {
+        Fragment fragment = findOrCreateVideoSupportFragment();
+        return onCreateVideoSupportFragmentHost(fragment);
+    }
+
     void onRowSelected(int selectedPosition, int selectedSubPosition) {
         ObjectAdapter adapter = getAdapter();
-        if (adapter == null || adapter.size() == 0 ||
-                (selectedPosition == 0 && selectedSubPosition == 0)) {
+        if (adapter == null || adapter.size() == 0
+                || (selectedPosition == 0 && selectedSubPosition == 0)) {
             showTitle(true);
         } else {
             showTitle(false);
@@ -397,10 +439,10 @@
     public void onStart() {
         super.onStart();
         setupChildFragmentLayout();
-        setupFocusSearchListener();
         if (isEntranceTransitionEnabled()) {
             mRowsSupportFragment.setEntranceTransitionState(false);
         }
+        mRowsSupportFragment.getVerticalGridView().requestFocus();
     }
 
     @Override
@@ -428,4 +470,85 @@
     protected void onEntranceTransitionStart() {
         mRowsSupportFragment.onTransitionStart();
     }
+
+    /**
+     * Returns the {@link DetailsParallaxManager} instance used to configure
+     * {@link android.support.v17.leanback.widget.Parallax} instance.
+     */
+    public DetailsParallaxManager getParallaxManager() {
+        if (mDetailsParallaxManager == null) {
+            mDetailsParallaxManager = new DetailsParallaxManager(
+                    getRowsSupportFragment().getVerticalGridView());
+        }
+        return mDetailsParallaxManager;
+    }
+
+    /**
+     * This method does the following
+     * <ul>
+     * <li>sets up focus search handling logic in the root view to enable transitioning between
+     * half screen/full screen/no video mode.</li>
+     *
+     * <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
+     * transition to appropriate mode like half/full screen video.</li>
+     * </ul>
+     */
+    void setupVideoPlayback() {
+        mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
+            @Override
+            public View onFocusSearch(View focused, int direction) {
+                if (mVideoSupportFragment == null) {
+                    return null;
+                }
+                if (mRowsSupportFragment.getVerticalGridView() != null
+                        && mRowsSupportFragment.getVerticalGridView().hasFocus()) {
+                    if (direction == View.FOCUS_UP) {
+                        slideOutGridView();
+                        return mVideoSupportFragment.getView();
+                    }
+                } else if (mVideoSupportFragment.getView() != null
+                        && mVideoSupportFragment.getView().hasFocus()) {
+                    if (direction == View.FOCUS_DOWN) {
+                        slideInGridView();
+                        return mRowsSupportFragment.getVerticalGridView();
+                    }
+                }
+                return focused;
+            }
+        });
+
+        // If we press BACK or DOWN on remote while in full screen video mode, we should
+        // transition back to half screen video playback mode.
+        mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
+            @Override
+            public boolean onKey(View v, int keyCode, KeyEvent event) {
+                // This is used to check if we are in full screen video mode. This is somewhat
+                // hacky and relies on the behavior of the video helper class to update the
+                // focusability of the video surface view.
+                if (mVideoSupportFragment.getView() != null && mVideoSupportFragment.getView().hasFocus()) {
+                    if (keyCode == KeyEvent.KEYCODE_BACK) {
+                        slideInGridView();
+                        getVerticalGridView().requestFocus();
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        });
+    }
+
+    /**
+     * Slides vertical grid view (displaying media item details) out of the screen from below.
+     */
+    void slideOutGridView() {
+        getVerticalGridView().animateOut();
+    }
+
+    /**
+     * Slides in vertical grid view (displaying media item details) from below.
+     */
+    void slideInGridView() {
+        getVerticalGridView().animateIn();
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/ErrorFragment.java b/v17/leanback/src/android/support/v17/leanback/app/ErrorFragment.java
index ac7b933..c35fcdc 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/ErrorFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/ErrorFragment.java
@@ -13,23 +13,19 @@
  */
 package android.support.v17.leanback.app;
 
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.TitleView;
 import android.text.TextUtils;
-import android.util.Log;
-import android.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.drawable.Drawable;
 
 /**
  * A fragment for displaying an error indication.
@@ -75,8 +71,8 @@
         mBackgroundDrawable = drawable;
         if (drawable != null) {
             final int opacity = drawable.getOpacity();
-            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT ||
-                    opacity == PixelFormat.TRANSPARENT);
+            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT
+                    || opacity == PixelFormat.TRANSPARENT);
         }
         updateBackground();
         updateMessage();
@@ -194,9 +190,9 @@
                 mErrorFrame.setBackground(mBackgroundDrawable);
             } else {
                 mErrorFrame.setBackgroundColor(mErrorFrame.getResources().getColor(
-                        mIsBackgroundTranslucent ?
-                        R.color.lb_error_background_color_translucent :
-                        R.color.lb_error_background_color_opaque));
+                        mIsBackgroundTranslucent
+                                ? R.color.lb_error_background_color_translucent
+                                : R.color.lb_error_background_color_opaque));
             }
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/ErrorSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/ErrorSupportFragment.java
index 1cc5e21..179e2e9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/ErrorSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/ErrorSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from ErrorFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,23 +16,19 @@
  */
 package android.support.v17.leanback.app;
 
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.TitleView;
 import android.text.TextUtils;
-import android.util.Log;
-import android.support.v4.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.drawable.Drawable;
 
 /**
  * A fragment for displaying an error indication.
@@ -77,8 +74,8 @@
         mBackgroundDrawable = drawable;
         if (drawable != null) {
             final int opacity = drawable.getOpacity();
-            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT ||
-                    opacity == PixelFormat.TRANSPARENT);
+            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT
+                    || opacity == PixelFormat.TRANSPARENT);
         }
         updateBackground();
         updateMessage();
@@ -196,9 +193,9 @@
                 mErrorFrame.setBackground(mBackgroundDrawable);
             } else {
                 mErrorFrame.setBackgroundColor(mErrorFrame.getResources().getColor(
-                        mIsBackgroundTranslucent ?
-                        R.color.lb_error_background_color_translucent :
-                        R.color.lb_error_background_color_opaque));
+                        mIsBackgroundTranslucent
+                                ? R.color.lb_error_background_color_translucent
+                                : R.color.lb_error_background_color_opaque));
             }
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 73c9700..db1bed5 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -13,6 +13,8 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.app.Activity;
@@ -33,7 +35,6 @@
 import android.support.v17.leanback.widget.GuidedActionAdapter;
 import android.support.v17.leanback.widget.GuidedActionAdapterGroup;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
-import android.support.v17.leanback.widget.ViewHolderTask;
 import android.support.v4.app.ActivityCompat;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -49,8 +50,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * A GuidedStepFragment is used to guide the user through a decision or series of decisions.
  * It is composed of a guidance view on the left and a view on the right containing a list of
@@ -141,7 +140,6 @@
 public class GuidedStepFragment extends Fragment implements GuidedActionAdapter.FocusListener {
 
     private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepFragment";
-    private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex";
     private static final String EXTRA_ACTION_PREFIX = "action_";
     private static final String EXTRA_BUTTON_ACTION_PREFIX = "buttonaction_";
 
@@ -262,8 +260,6 @@
     private GuidedActionAdapterGroup mAdapterGroup;
     private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
     private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>();
-    private int mSelectedIndex = -1;
-    private int mButtonSelectedIndex = -1;
     private int entranceTransitionType = SLIDE_FROM_SIDE;
 
     public GuidedStepFragment() {
@@ -363,6 +359,14 @@
     }
 
     /**
+     * @return True if is current expanded including subactions list or
+     * action with {@link GuidedAction#hasEditableActivatorView()} is true.
+     */
+    public boolean isExpanded() {
+        return mActionsStylist.isExpanded();
+    }
+
+    /**
      * @return True if the sub actions list is expanded, false otherwise.
      */
     public boolean isSubActionsExpanded() {
@@ -372,21 +376,25 @@
     /**
      * Expand a given action's sub actions list.
      * @param action GuidedAction to expand.
-     * @see GuidedAction#getSubActions()
+     * @see #expandAction(GuidedAction, boolean)
      */
     public void expandSubActions(GuidedAction action) {
-        final int actionPosition = mActions.indexOf(action);
-        if (actionPosition < 0) {
+        if (!action.hasSubActions()) {
             return;
         }
-        mActionsStylist.getActionsGridView().setSelectedPositionSmooth(actionPosition,
-                new ViewHolderTask() {
-            @Override
-            public void run(RecyclerView.ViewHolder vh) {
-                GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder) vh;
-                mActionsStylist.setExpandedViewHolder(avh);
-            }
-        });
+        expandAction(action, true);
+    }
+
+    /**
+     * Expand a given action with sub actions list or
+     * {@link GuidedAction#hasEditableActivatorView()} is true. The method must be called after
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} creates fragment view.
+     *
+     * @param action GuidedAction to expand.
+     * @param withTransition True to run transition animation, false otherwise.
+     */
+    public void expandAction(GuidedAction action, boolean withTransition) {
+        mActionsStylist.expandAction(action, withTransition);
     }
 
     /**
@@ -394,7 +402,19 @@
      * @see GuidedAction#getSubActions()
      */
     public void collapseSubActions() {
-        mActionsStylist.setExpandedViewHolder(null);
+        collapseAction(true);
+    }
+
+    /**
+     * Collapse action which either has a sub actions list or action with
+     * {@link GuidedAction#hasEditableActivatorView()} is true.
+     *
+     * @param withTransition True to run transition animation, false otherwise.
+     */
+    public void collapseAction(boolean withTransition) {
+        if (mActionsStylist != null && mActionsStylist.getActionsGridView() != null) {
+            mActionsStylist.collapseAction(withTransition);
+        }
     }
 
     /**
@@ -543,7 +563,7 @@
      * @return BackStackEntry name for the GuidedStepFragment or empty String if no entry is
      * associated.
      */
-    String generateStackEntryName() {
+    final String generateStackEntryName() {
         return generateStackEntryName(getUiStyle(), getClass());
     }
 
@@ -555,9 +575,6 @@
      * associated.
      */
     static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) {
-        if (!GuidedStepFragment.class.isAssignableFrom(guidedStepFragmentClass)) {
-            return "";
-        }
         switch (uiStyle) {
         case UI_STYLE_REPLACE:
             return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName();
@@ -618,8 +635,8 @@
         activity.getWindow().getDecorView();
         FragmentManager fragmentManager = activity.getFragmentManager();
         if (fragmentManager.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT) != null) {
-            Log.w(TAG, "Fragment is already exists, likely calling " +
-                    "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
+            Log.w(TAG, "Fragment is already exists, likely calling "
+                    + "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
             return -1;
         }
         FragmentTransaction ft = fragmentManager.beginTransaction();
@@ -865,8 +882,8 @@
                         true);
                 TransitionHelper.setEnterTransition(this, enterTransition);
 
-                Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
-                        TransitionHelper.FADE_OUT);
+                Object fade = TransitionHelper.createFadeTransition(
+                        TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
                 TransitionHelper.include(fade, R.id.guidedactions_sub_list_background);
                 Object changeBounds = TransitionHelper.createChangeBounds(false);
                 Object sharedElementTransition = TransitionHelper.createTransitionSet(false);
@@ -875,11 +892,11 @@
                 TransitionHelper.setSharedElementEnterTransition(this, sharedElementTransition);
             } else if (uiStyle == UI_STYLE_ENTRANCE) {
                 if (entranceTransitionType == SLIDE_FROM_SIDE) {
-                    Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
-                            TransitionHelper.FADE_OUT);
+                    Object fade = TransitionHelper.createFadeTransition(
+                            TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
                     TransitionHelper.include(fade, R.id.guidedstep_background);
-                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(Gravity.END |
-                            Gravity.START);
+                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(
+                            Gravity.END | Gravity.START);
                     TransitionHelper.include(slideFromSide, R.id.content_fragment);
                     TransitionHelper.include(slideFromSide, R.id.action_fragment_root);
                     Object enterTransition = TransitionHelper.createTransitionSet(false);
@@ -979,12 +996,7 @@
         if (DEBUG) Log.v(TAG, "onCreate");
         // Set correct transition from saved arguments.
         onProvideFragmentTransitions();
-        Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments();
-        if (state != null) {
-            if (mSelectedIndex == -1) {
-                mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1);
-            }
-        }
+
         ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>();
         onCreateActions(actions, savedInstanceState);
         if (savedInstanceState != null) {
@@ -1071,10 +1083,10 @@
             @Override
             public void onGuidedActionClicked(GuidedAction action) {
                 GuidedStepFragment.this.onGuidedActionClicked(action);
-                if (isSubActionsExpanded()) {
-                    collapseSubActions();
-                } else if (action.hasSubActions()) {
-                    expandSubActions(action);
+                if (isExpanded()) {
+                    collapseAction(true);
+                } else if (action.hasSubActions() || action.hasEditableActivatorView()) {
+                    expandAction(action, true);
                 }
             }
         }, this, mActionsStylist, false);
@@ -1130,12 +1142,6 @@
             }
         }
 
-        int pos = (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ?
-                mSelectedIndex : getFirstCheckedAction();
-        setSelectedActionPosition(pos);
-
-        setSelectedButtonActionPosition(0);
-
         // Add the background view.
         View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
         if (backgroundView != null) {
@@ -1143,6 +1149,7 @@
                 R.id.guidedstep_background_view_root);
             backgroundViewRoot.addView(backgroundView, 0);
         }
+
         return root;
     }
 
@@ -1218,9 +1225,6 @@
         super.onSaveInstanceState(outState);
         onSaveActions(mActions, outState);
         onSaveButtonActions(mButtonActions, outState);
-        outState.putInt(EXTRA_ACTION_SELECTED_INDEX,
-                (mActionsStylist.getActionsGridView() != null) ?
-                        getSelectedActionPosition() : mSelectedIndex);
     }
 
     private static boolean isGuidedStepTheme(Context context) {
@@ -1249,7 +1253,7 @@
                     if (top != null) {
                         top.setUiStyle(UI_STYLE_ENTRANCE);
                     }
-                    fragmentManager.popBackStack(entry.getId(),
+                    fragmentManager.popBackStackImmediate(entry.getId(),
                             FragmentManager.POP_BACK_STACK_INCLUSIVE);
                     return;
                 }
@@ -1275,7 +1279,7 @@
                 BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
                 String entryClassName = getGuidedStepFragmentClassName(entry.getName());
                 if (className.equals(entryClassName)) {
-                    fragmentManager.popBackStack(entry.getId(), flags);
+                    fragmentManager.popBackStackImmediate(entry.getId(), flags);
                     return;
                 }
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
index 6d05fd1..ec34bba 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
@@ -51,8 +51,8 @@
             if (Util.isDescendant(this, newFocus)) {
                 return newFocus;
             }
-            if (getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_LTR ?
-                    direction == FOCUS_LEFT : direction == FOCUS_RIGHT) {
+            if (getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_LTR
+                    ? direction == FOCUS_LEFT : direction == FOCUS_RIGHT) {
                 if (!mFocusOutStart) {
                     return focused;
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index f5b27df..307c94b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from GuidedStepFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,9 +16,10 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.support.annotation.RestrictTo;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
@@ -27,6 +29,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.GuidanceStylist;
@@ -35,7 +38,6 @@
 import android.support.v17.leanback.widget.GuidedActionAdapter;
 import android.support.v17.leanback.widget.GuidedActionAdapterGroup;
 import android.support.v17.leanback.widget.GuidedActionsStylist;
-import android.support.v17.leanback.widget.ViewHolderTask;
 import android.support.v4.app.ActivityCompat;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
@@ -51,8 +53,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * A GuidedStepSupportFragment is used to guide the user through a decision or series of decisions.
  * It is composed of a guidance view on the left and a view on the right containing a list of
@@ -143,7 +143,6 @@
 public class GuidedStepSupportFragment extends Fragment implements GuidedActionAdapter.FocusListener {
 
     private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepSupportFragment";
-    private static final String EXTRA_ACTION_SELECTED_INDEX = "selectedIndex";
     private static final String EXTRA_ACTION_PREFIX = "action_";
     private static final String EXTRA_BUTTON_ACTION_PREFIX = "buttonaction_";
 
@@ -264,8 +263,6 @@
     private GuidedActionAdapterGroup mAdapterGroup;
     private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
     private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>();
-    private int mSelectedIndex = -1;
-    private int mButtonSelectedIndex = -1;
     private int entranceTransitionType = SLIDE_FROM_SIDE;
 
     public GuidedStepSupportFragment() {
@@ -365,6 +362,14 @@
     }
 
     /**
+     * @return True if is current expanded including subactions list or
+     * action with {@link GuidedAction#hasEditableActivatorView()} is true.
+     */
+    public boolean isExpanded() {
+        return mActionsStylist.isExpanded();
+    }
+
+    /**
      * @return True if the sub actions list is expanded, false otherwise.
      */
     public boolean isSubActionsExpanded() {
@@ -374,21 +379,25 @@
     /**
      * Expand a given action's sub actions list.
      * @param action GuidedAction to expand.
-     * @see GuidedAction#getSubActions()
+     * @see #expandAction(GuidedAction, boolean)
      */
     public void expandSubActions(GuidedAction action) {
-        final int actionPosition = mActions.indexOf(action);
-        if (actionPosition < 0) {
+        if (!action.hasSubActions()) {
             return;
         }
-        mActionsStylist.getActionsGridView().setSelectedPositionSmooth(actionPosition,
-                new ViewHolderTask() {
-            @Override
-            public void run(RecyclerView.ViewHolder vh) {
-                GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder) vh;
-                mActionsStylist.setExpandedViewHolder(avh);
-            }
-        });
+        expandAction(action, true);
+    }
+
+    /**
+     * Expand a given action with sub actions list or
+     * {@link GuidedAction#hasEditableActivatorView()} is true. The method must be called after
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} creates fragment view.
+     *
+     * @param action GuidedAction to expand.
+     * @param withTransition True to run transition animation, false otherwise.
+     */
+    public void expandAction(GuidedAction action, boolean withTransition) {
+        mActionsStylist.expandAction(action, withTransition);
     }
 
     /**
@@ -396,7 +405,19 @@
      * @see GuidedAction#getSubActions()
      */
     public void collapseSubActions() {
-        mActionsStylist.setExpandedViewHolder(null);
+        collapseAction(true);
+    }
+
+    /**
+     * Collapse action which either has a sub actions list or action with
+     * {@link GuidedAction#hasEditableActivatorView()} is true.
+     *
+     * @param withTransition True to run transition animation, false otherwise.
+     */
+    public void collapseAction(boolean withTransition) {
+        if (mActionsStylist != null && mActionsStylist.getActionsGridView() != null) {
+            mActionsStylist.collapseAction(withTransition);
+        }
     }
 
     /**
@@ -545,7 +566,7 @@
      * @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
      * associated.
      */
-    String generateStackEntryName() {
+    final String generateStackEntryName() {
         return generateStackEntryName(getUiStyle(), getClass());
     }
 
@@ -557,9 +578,6 @@
      * associated.
      */
     static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) {
-        if (!GuidedStepSupportFragment.class.isAssignableFrom(guidedStepFragmentClass)) {
-            return "";
-        }
         switch (uiStyle) {
         case UI_STYLE_REPLACE:
             return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName();
@@ -620,8 +638,8 @@
         activity.getWindow().getDecorView();
         FragmentManager fragmentManager = activity.getSupportFragmentManager();
         if (fragmentManager.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT) != null) {
-            Log.w(TAG, "Fragment is already exists, likely calling " +
-                    "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
+            Log.w(TAG, "Fragment is already exists, likely calling "
+                    + "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
             return -1;
         }
         FragmentTransaction ft = fragmentManager.beginTransaction();
@@ -867,8 +885,8 @@
                         true);
                 TransitionHelper.setEnterTransition(this, enterTransition);
 
-                Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
-                        TransitionHelper.FADE_OUT);
+                Object fade = TransitionHelper.createFadeTransition(
+                        TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
                 TransitionHelper.include(fade, R.id.guidedactions_sub_list_background);
                 Object changeBounds = TransitionHelper.createChangeBounds(false);
                 Object sharedElementTransition = TransitionHelper.createTransitionSet(false);
@@ -877,11 +895,11 @@
                 TransitionHelper.setSharedElementEnterTransition(this, sharedElementTransition);
             } else if (uiStyle == UI_STYLE_ENTRANCE) {
                 if (entranceTransitionType == SLIDE_FROM_SIDE) {
-                    Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
-                            TransitionHelper.FADE_OUT);
+                    Object fade = TransitionHelper.createFadeTransition(
+                            TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
                     TransitionHelper.include(fade, R.id.guidedstep_background);
-                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(Gravity.END |
-                            Gravity.START);
+                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(
+                            Gravity.END | Gravity.START);
                     TransitionHelper.include(slideFromSide, R.id.content_fragment);
                     TransitionHelper.include(slideFromSide, R.id.action_fragment_root);
                     Object enterTransition = TransitionHelper.createTransitionSet(false);
@@ -981,12 +999,7 @@
         if (DEBUG) Log.v(TAG, "onCreate");
         // Set correct transition from saved arguments.
         onProvideFragmentTransitions();
-        Bundle state = (savedInstanceState != null) ? savedInstanceState : getArguments();
-        if (state != null) {
-            if (mSelectedIndex == -1) {
-                mSelectedIndex = state.getInt(EXTRA_ACTION_SELECTED_INDEX, -1);
-            }
-        }
+
         ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>();
         onCreateActions(actions, savedInstanceState);
         if (savedInstanceState != null) {
@@ -1073,10 +1086,10 @@
             @Override
             public void onGuidedActionClicked(GuidedAction action) {
                 GuidedStepSupportFragment.this.onGuidedActionClicked(action);
-                if (isSubActionsExpanded()) {
-                    collapseSubActions();
-                } else if (action.hasSubActions()) {
-                    expandSubActions(action);
+                if (isExpanded()) {
+                    collapseAction(true);
+                } else if (action.hasSubActions() || action.hasEditableActivatorView()) {
+                    expandAction(action, true);
                 }
             }
         }, this, mActionsStylist, false);
@@ -1132,12 +1145,6 @@
             }
         }
 
-        int pos = (mSelectedIndex >= 0 && mSelectedIndex < mActions.size()) ?
-                mSelectedIndex : getFirstCheckedAction();
-        setSelectedActionPosition(pos);
-
-        setSelectedButtonActionPosition(0);
-
         // Add the background view.
         View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
         if (backgroundView != null) {
@@ -1145,6 +1152,7 @@
                 R.id.guidedstep_background_view_root);
             backgroundViewRoot.addView(backgroundView, 0);
         }
+
         return root;
     }
 
@@ -1220,9 +1228,6 @@
         super.onSaveInstanceState(outState);
         onSaveActions(mActions, outState);
         onSaveButtonActions(mButtonActions, outState);
-        outState.putInt(EXTRA_ACTION_SELECTED_INDEX,
-                (mActionsStylist.getActionsGridView() != null) ?
-                        getSelectedActionPosition() : mSelectedIndex);
     }
 
     private static boolean isGuidedStepTheme(Context context) {
@@ -1251,7 +1256,7 @@
                     if (top != null) {
                         top.setUiStyle(UI_STYLE_ENTRANCE);
                     }
-                    fragmentManager.popBackStack(entry.getId(),
+                    fragmentManager.popBackStackImmediate(entry.getId(),
                             FragmentManager.POP_BACK_STACK_INCLUSIVE);
                     return;
                 }
@@ -1277,7 +1282,7 @@
                 BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
                 String entryClassName = getGuidedStepSupportFragmentClassName(entry.getName());
                 if (className.equals(entryClassName)) {
-                    fragmentManager.popBackStack(entry.getId(), flags);
+                    fragmentManager.popBackStackImmediate(entry.getId(), flags);
                     return;
                 }
             }
@@ -1317,6 +1322,7 @@
      * For now clients(subclasses) can call this method inside the constructor.
      * @hide
      */
+    @RestrictTo(GROUP_ID)
     public void setEntranceTransitionType(int transitionType) {
       this.entranceTransitionType = transitionType;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
index 8937e08..8989edf 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from HeadersFragment.java.  DO NOT MODIFY. */
 
 /*
diff --git a/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java b/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
index 27ccd06..5c75506 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
@@ -48,14 +48,17 @@
      *
      * <p>The {@link PlaybackOverlayFragment} must be passed in.
      * A {@link android.support.v17.leanback.widget.OnItemViewClickedListener} and
-     * {@link PlaybackOverlayFragment.InputEventHandler}
+     * {@link android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler}
      * will be set on the fragment.
      * </p>
      *
      * @param context
      * @param fragment
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
+     * @deprecated Use
+     * {@link #MediaControllerGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
+    @Deprecated
     public MediaControllerGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] seekSpeeds) {
@@ -67,7 +70,7 @@
      *
      * <p>The {@link PlaybackOverlayFragment} must be passed in.
      * A {@link android.support.v17.leanback.widget.OnItemViewClickedListener} and
-     * {@link PlaybackOverlayFragment.InputEventHandler}
+     * {@link android.support.v17.leanback.app.PlaybackControlGlue.InputEventHandler}
      * will be set on the fragment.
      * </p>
      *
@@ -75,7 +78,10 @@
      * @param fragment
      * @param fastForwardSpeeds Array of seek speeds for fast forward.
      * @param rewindSpeeds Array of seek speeds for rewind.
+     * @deprecated Use
+     * {@link #MediaControllerGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
+    @Deprecated
     public MediaControllerGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] fastForwardSpeeds,
@@ -84,6 +90,21 @@
     }
 
     /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param host Optional; if using a {@link PlaybackGlue.PlaybackGlueHost}, pass it in.
+     * @param fastForwardSpeeds Array of seek speeds for fast forward.
+     * @param rewindSpeeds Array of seek speeds for rewind.
+     */
+    public MediaControllerGlue(Context context,
+            PlaybackGlueHost host,
+            int[] fastForwardSpeeds,
+            int[] rewindSpeeds) {
+        super(context, host, fastForwardSpeeds, rewindSpeeds);
+    }
+
+    /**
      * Attaches to the given media controller.
      */
     public void attachToMediaController(MediaControllerCompat mediaController) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java b/v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java
new file mode 100644
index 0000000..07773e7
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java
@@ -0,0 +1,524 @@
+/*
+ * 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.v17.leanback.app;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Handler;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import java.io.IOException;
+
+/**
+ * This glue extends the {@link PlaybackControlGlue} with a {@link MediaPlayer} synchronization. It
+ * supports 7 actions:
+ *
+ * <ul>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction}</li>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}</li>
+ * <li>{@link  android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction}</li>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction}</li>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction}</li>
+ * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction}</li>
+ * </ul>
+ *
+ * @hide
+ */
+public class MediaPlayerGlue extends PlaybackControlGlue implements
+        OnItemViewSelectedListener {
+
+    public static final int NO_REPEAT = 0;
+    public static final int REPEAT_ONE = 1;
+    public static final int REPEAT_ALL = 2;
+
+    public static final int FAST_FORWARD_REWIND_STEP = 10 * 1000; // in milliseconds
+    public static final int FAST_FORWARD_REWIND_REPEAT_DELAY = 200; // in milliseconds
+    private static final String TAG = "MediaPlayerGlue";
+    protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
+    protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
+    private final Context mContext;
+    MediaPlayer mPlayer = new MediaPlayer();
+    private final PlaybackControlsRow.RepeatAction mRepeatAction;
+    private PlaybackControlsRow mControlsRow;
+    private Runnable mRunnable;
+    private Handler mHandler = new Handler();
+    private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
+    private Action mSelectedAction; // the action which is currently selected by the user
+    private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
+    private Uri mMediaSourceUri = null;
+    private String mMediaSourcePath = null;
+    private PlayerCallback mPlayerCallback;
+    private MediaPlayer.OnCompletionListener mOnCompletionListener;
+    private String mArtist;
+    private String mTitle;
+    private Drawable mCover;
+
+    /**
+     * Sets the drawable representing cover image.
+     */
+    public void setCover(Drawable cover) {
+        this.mCover = cover;
+    }
+
+    /**
+     * Sets the artist name.
+     */
+    public void setArtist(String artist) {
+        this.mArtist = artist;
+    }
+
+    /**
+     * Sets the media title.
+     */
+    public void setTitle(String title) {
+        this.mTitle = title;
+    }
+
+    /**
+     * Sets the url for the video.
+     */
+    public void setVideoUrl(String videoUrl) {
+        setMediaSource(videoUrl);
+        onMetadataChanged();
+    }
+
+    /**
+     * Constructor.
+     */
+    public MediaPlayerGlue(Context context, PlaybackGlueHost host) {
+        this(context, host, new int[]{1}, new int[]{1});
+    }
+
+    /**
+     * Constructor.
+     */
+    public MediaPlayerGlue(
+            Context context, PlaybackGlueHost host, int[] fastForwardSpeeds, int[] rewindSpeeds) {
+        super(context, host, fastForwardSpeeds, rewindSpeeds);
+        mContext = context;
+
+        // Instantiate secondary actions
+        mRepeatAction = new PlaybackControlsRow.RepeatAction(mContext);
+        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(mContext);
+        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(mContext);
+        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+    }
+
+    @Override
+    public void setHost(PlaybackGlueHost host) {
+        super.setHost(host);
+        if (getHost() instanceof VideoFragmentGlueHost) {
+            ((VideoFragmentGlueHost) getHost()).setSurfaceHolderCallback(
+                    new VideoFragmentSurfaceHolderCallback());
+        }
+    }
+
+    /**
+     * Sets the callback, which would tell the listener that video is ready to be played.
+     */
+    public void setPlayerCallback(PlayerCallback callback) {
+        this.mPlayerCallback = callback;
+    }
+
+    /**
+     * Will reset the {@link MediaPlayer} and the glue such that a new file can be played. You are
+     * not required to call this method before playing the first file. However you have to call it
+     * before playing a second one.
+     */
+    void reset() {
+        mInitialized = false;
+        mPlayer.reset();
+    }
+
+    /**
+     * Override this method in case you need to add different secondary actions.
+     *
+     * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
+     */
+    protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+        secondaryActionsAdapter.add(mRepeatAction);
+        secondaryActionsAdapter.add(mThumbsDownAction);
+        secondaryActionsAdapter.add(mThumbsUpAction);
+    }
+
+    /**
+     * @see MediaPlayer#setDisplay(SurfaceHolder)
+     */
+    public void setDisplay(SurfaceHolder surfaceHolder) {
+        mPlayer.setDisplay(surfaceHolder);
+    }
+
+    /**
+     * Creates a presenter ({@link PlaybackControlsRowPresenter}) for rendering playback controls.
+     * @return
+     */
+    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
+        PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
+        mControlsRow = getControlsRow();
+
+        // Add secondary actions and change the control row color.
+        ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
+                new ControlButtonPresenterSelector());
+        mControlsRow.setSecondaryActionsAdapter(secondaryActions);
+        addSecondaryActions(secondaryActions);
+        return presenter;
+    }
+
+    @Override
+    public void enableProgressUpdating(final boolean enabled) {
+        if (!enabled) {
+            if (mRunnable != null) mHandler.removeCallbacks(mRunnable);
+            return;
+        }
+        mRunnable = new Runnable() {
+            @Override
+            public void run() {
+                updateProgress();
+                Log.d(TAG, "enableProgressUpdating(boolean)");
+                mHandler.postDelayed(this, getUpdatePeriod());
+            }
+        };
+        mHandler.postDelayed(mRunnable, getUpdatePeriod());
+    }
+
+    @Override
+    public void onActionClicked(Action action) {
+        // If either 'Shuffle' or 'Repeat' has been clicked we need to make sure the actions index
+        // is incremented and the UI updated such that we can display the new state.
+        super.onActionClicked(action);
+        if (action instanceof PlaybackControlsRow.RepeatAction) {
+            mRepeatAction.nextIndex();
+        } else if (action instanceof PlaybackControlsRow.ThumbsUpAction) {
+            if (mThumbsUpAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+            } else {
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+            }
+        } else if (action instanceof PlaybackControlsRow.ThumbsDownAction) {
+            if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.SOLID) {
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+            } else {
+                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.SOLID);
+                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
+            }
+        }
+        onMetadataChanged();
+    }
+
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        // This method is overridden in order to make implement fast forwarding and rewinding when
+        // the user keeps the corresponding action pressed.
+        // We only consume DPAD_CENTER Action_DOWN events on the Fast-Forward and Rewind action and
+        // only if it has not been pressed in the last X milliseconds.
+        boolean consume = mSelectedAction instanceof PlaybackControlsRow.RewindAction;
+        consume = consume || mSelectedAction instanceof PlaybackControlsRow.FastForwardAction;
+        consume = consume && mInitialized;
+        consume = consume && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER;
+        consume = consume && event.getAction() == KeyEvent.ACTION_DOWN;
+        consume = consume && System
+                .currentTimeMillis() - mLastKeyDownEvent > FAST_FORWARD_REWIND_REPEAT_DELAY;
+
+        if (consume) {
+            mLastKeyDownEvent = System.currentTimeMillis();
+            int newPosition = getCurrentPosition() + FAST_FORWARD_REWIND_STEP;
+            if (mSelectedAction instanceof PlaybackControlsRow.RewindAction) {
+                newPosition = getCurrentPosition() - FAST_FORWARD_REWIND_STEP;
+            }
+            // Make sure the new calculated duration is in the range 0 >= X >= MediaDuration
+            if (newPosition < 0) newPosition = 0;
+            if (newPosition > getMediaDuration()) newPosition = getMediaDuration();
+            seekTo(newPosition);
+            return true;
+        }
+
+        return super.onKey(v, keyCode, event);
+    }
+
+    @Override
+    public boolean hasValidMedia() {
+        return mTitle != null && mMediaSourcePath != null;
+    }
+
+    @Override
+    public boolean isMediaPlaying() {
+        return mPlayer.isPlaying();
+    }
+
+    @Override
+    public CharSequence getMediaTitle() {
+        return mTitle != null ? mTitle : "N/a";
+    }
+
+    @Override
+    public CharSequence getMediaSubtitle() {
+        return mArtist != null ? mArtist : "N/a";
+    }
+
+    @Override
+    public int getMediaDuration() {
+        return mInitialized ? mPlayer.getDuration() : 0;
+    }
+
+    @Override
+    public Drawable getMediaArt() {
+        return mCover;
+    }
+
+    @Override
+    public long getSupportedActions() {
+        return PlaybackControlGlue.ACTION_PLAY_PAUSE
+                | PlaybackControlGlue.ACTION_FAST_FORWARD
+                | PlaybackControlGlue.ACTION_REWIND;
+    }
+
+    @Override
+    public int getCurrentSpeedId() {
+        // 0 = Pause, 1 = Normal Playback Speed
+        return mPlayer.isPlaying() ? 1 : 0;
+    }
+
+    @Override
+    public int getCurrentPosition() {
+        return mInitialized ? mPlayer.getCurrentPosition() : 0;
+    }
+
+    @Override
+    protected void startPlayback(int speed) throws IllegalStateException {
+        play();
+    }
+
+    @Override
+    protected void pausePlayback() {
+        pause();
+    }
+
+    @Override
+    protected void skipToNext() {
+        // Not supported.
+    }
+
+    @Override
+    protected void skipToPrevious() {
+        // Not supported.
+    }
+
+    @Override
+    public void play() {
+        mPlayer.start();
+        onMetadataChanged();
+        onStateChanged();
+        updateProgress();
+    }
+
+    @Override
+    public void pause() {
+        if (mPlayer.isPlaying()) {
+            mPlayer.pause();
+        }
+    }
+
+    /**
+     * Sets the playback mode. It currently support no repeat, repeat once and infinite
+     * loop mode.
+     */
+    public void setMode(int mode) {
+        switch(mode) {
+            case NO_REPEAT:
+                mOnCompletionListener = null;
+                break;
+            case REPEAT_ONE:
+                mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
+                    public boolean mFirstRepeat;
+
+                    @Override
+                    public void onCompletion(MediaPlayer mediaPlayer) {
+                        if (!mFirstRepeat) {
+                            mFirstRepeat = true;
+                            mediaPlayer.setOnCompletionListener(null);
+                        }
+                        play();
+                    }
+                };
+                break;
+            case REPEAT_ALL:
+                mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
+                    @Override
+                    public void onCompletion(MediaPlayer mediaPlayer) {
+                        play();
+                    }
+                };
+                break;
+        }
+    }
+
+    /**
+     * Called whenever the user presses fast-forward/rewind or when the user keeps the
+     * corresponding action pressed.
+     *
+     * @param newPosition The new position of the media track in milliseconds.
+     */
+    protected void seekTo(int newPosition) {
+        mPlayer.seekTo(newPosition);
+    }
+
+    /**
+     * Sets the media source of the player witha given URI.
+     *
+     * @return Returns <code>true</code> if uri represents a new media; <code>false</code>
+     * otherwise.
+     * @see MediaPlayer#setDataSource(String)
+     */
+    public boolean setMediaSource(Uri uri) {
+        if (mMediaSourceUri != null && mMediaSourceUri.equals(uri)) {
+            return false;
+        }
+        mMediaSourceUri = uri;
+        return true;
+    }
+
+    /**
+     * Sets the media source of the player with a String path URL.
+     *
+     * @return Returns <code>true</code> if path represents a new media; <code>false</code>
+     * otherwise.
+     * @see MediaPlayer#setDataSource(String)
+     */
+    public boolean setMediaSource(String path) {
+        if (mMediaSourcePath != null && mMediaSourcePath.equals(mMediaSourcePath)) {
+            return false;
+        }
+        mMediaSourcePath = path;
+        return true;
+    }
+
+    private void prepareMediaForPlaying() {
+        reset();
+        try {
+            if (mMediaSourceUri != null) mPlayer.setDataSource(getContext(), mMediaSourceUri);
+            else mPlayer.setDataSource(mMediaSourcePath);
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+        mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+            @Override
+            public void onPrepared(MediaPlayer mp) {
+                mInitialized = true;
+                if (mPlayerCallback != null) {
+                    mPlayerCallback.onReadyForPlayback();
+                }
+            }
+        });
+
+        if (mOnCompletionListener != null) {
+            mPlayer.setOnCompletionListener(mOnCompletionListener);
+        }
+
+        mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
+            @Override
+            public void onBufferingUpdate(MediaPlayer mp, int percent) {
+                mControlsRow.setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
+            }
+        });
+        mPlayer.prepareAsync();
+        onStateChanged();
+    }
+
+    /**
+     * Call to <code>startPlayback(1)</code>.
+     *
+     * @throws IllegalStateException See {@link MediaPlayer} for further information about it's
+     *                               different states when setting a data source and preparing it
+     *                               to be played.
+     */
+    public void startPlayback() throws IllegalStateException {
+        startPlayback(1);
+    }
+
+    /**
+     * This is a listener implementation for the {@link OnItemViewSelectedListener} of the {@link
+     * PlaybackFragment}. This implementation is required in order to detect KEY_DOWN events
+     * on the {@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction} and
+     * {@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}. Thus you
+     * should <u>NOT</u> set another {@link OnItemViewSelectedListener} on your
+     * {@link PlaybackFragment}. Instead, override this method and call its super (this)
+     * implementation.
+     *
+     * @see OnItemViewSelectedListener#onItemSelected(
+     *Presenter.ViewHolder, Object, RowPresenter.ViewHolder, Object)
+     */
+    @Override
+    public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                               RowPresenter.ViewHolder rowViewHolder, Row row) {
+        if (item instanceof Action) {
+            mSelectedAction = (Action) item;
+        } else {
+            mSelectedAction = null;
+        }
+    }
+
+    @Override
+    public boolean isReadyForPlayback() {
+        return mInitialized;
+    }
+
+    /**
+     * Implements {@link SurfaceHolder.Callback} that can then be set on the \
+     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}
+     */
+    private class VideoFragmentSurfaceHolderCallback implements SurfaceHolder.Callback {
+        private boolean mMediaPlayerReset = true;
+
+        @Override
+        public void surfaceCreated(SurfaceHolder surfaceHolder) {
+            if (mMediaPlayerReset) {
+                mMediaPlayerReset = false;
+                setDisplay(surfaceHolder);
+                prepareMediaForPlaying();
+            }
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+            reset();
+            setDisplay(null);
+            mMediaPlayerReset = true;
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
index 16d3ed1..0459ab6 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingFragment.java
@@ -160,8 +160,8 @@
     private static int sSlideDistance;
 
     private static final TimeInterpolator HEADER_APPEAR_INTERPOLATOR = new DecelerateInterpolator();
-    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR
-            = new AccelerateInterpolator();
+    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR =
+            new AccelerateInterpolator();
 
     // Keys used to save and restore the states.
     private static final String KEY_CURRENT_PAGE_INDEX = "leanback.onboarding.current_page_index";
diff --git a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
index 076b61e..32163b0 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/OnboardingSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from OnboardingFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -162,8 +163,8 @@
     private static int sSlideDistance;
 
     private static final TimeInterpolator HEADER_APPEAR_INTERPOLATOR = new DecelerateInterpolator();
-    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR
-            = new AccelerateInterpolator();
+    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR =
+            new AccelerateInterpolator();
 
     // Keys used to save and restore the states.
     private static final String KEY_CURRENT_PAGE_INDEX = "leanback.onboarding.current_page_index";
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
index 2cf897f..9cae90e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
@@ -1,3 +1,16 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
 package android.support.v17.leanback.app;
 
 import android.content.Context;
@@ -21,24 +34,23 @@
 import android.view.KeyEvent;
 import android.view.View;
 
-
 /**
- * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow} and
- * {@link PlaybackOverlayFragment} that implements a recommended approach to handling standard
- * playback control actions such as play/pause, fast forward/rewind at progressive speed levels,
- * and skip to next/previous.  This helper class is a glue layer in that it manages the
- * configuration of and interaction between the leanback UI components by defining a functional
- * interface to the media player.
+ * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow}
+ * and {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} that implements a
+ * recommended approach to handling standard playback control actions such as play/pause,
+ * fast forward/rewind at progressive speed levels, and skip to next/previous. This helper class
+ * is a glue layer in that manages the configuration of and interaction between the
+ * leanback UI components by defining a functional interface to the media player.
  *
- * <p>You can instantiate a concrete subclass such as {@link MediaControllerGlue} or you must
+ * <p>You can instantiate a concrete subclass such as MediaPlayerGlue or you must
  * subclass this abstract helper.  To create a subclass you must implement all of the
  * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
  * {@link #onStateChanged()} appropriately.
  * </p>
  *
  * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
- * inform the glue what speed levels are supported for fast forward/rewind.  Providing a
- * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} is optional.
+ * inform the glue what speed levels are supported for fast forward/rewind. Providing a
+ * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} is optional.
  * </p>
  *
  * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
@@ -48,21 +60,20 @@
  * </p>
  *
  * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter}
- * on the controls row as the primary actions adapter, and adds actions to it.  You can provide
- * additional actions by overriding {@link #createPrimaryActionsAdapter}.  This helper does not
+ * on the controls row as the primary actions adapter, and adds actions to it. You can provide
+ * additional actions by overriding {@link #createPrimaryActionsAdapter}. This helper does not
  * deal in secondary actions so those you may add separately.
  * </p>
  *
  * <p>Provide a click listener on your fragment and if an action is clicked, call
- * {@link #onActionClicked}.  There is no need to call {@link #setOnItemViewClickedListener}
- * but if you do a click listener will be installed on the fragment and recognized action clicks
- * will be handled.  Your listener will be called only for unhandled actions.
+ * {@link #onActionClicked}. If you set a listener by calling {@link #setOnItemViewClickedListener},
+ * your listener will be called for all unhandled actions.
  * </p>
  *
- * <p>The helper implements a key event handler.  If you pass a
- * {@link android.support.v17.leanback.app.PlaybackOverlayFragment} the fragment's input event
- * handler will be set.  Otherwise, you should set the glue object as key event handler to the
- * ViewHolder when bound by your row presenter; see
+ * <p>This helper implements a key event handler. If you pass a
+ * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}, it will configure it's
+ * fragment to intercept all key events.  Otherwise, you should set the glue object as key event
+ * handler to the ViewHolder when bound by your row presenter; see
  * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
  * </p>
  *
@@ -72,7 +83,8 @@
  * </p>
  *
  */
-public abstract class PlaybackControlGlue implements OnActionClickedListener, View.OnKeyListener {
+public abstract class PlaybackControlGlue extends PlaybackGlue
+        implements OnActionClickedListener, View.OnKeyListener {
     /**
      * The adapter key for the first custom control on the left side
      * of the predefined primary controls.
@@ -160,11 +172,9 @@
 
     static final int MSG_UPDATE_PLAYBACK_STATE = 100;
     private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
-    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4 -
-            PLAYBACK_SPEED_FAST_L0 + 1;
+    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4
+            - PLAYBACK_SPEED_FAST_L0 + 1;
 
-    private final PlaybackOverlayFragment mFragment;
-    private final Context mContext;
     private final int[] mFastForwardSpeeds;
     private final int[] mRewindSpeeds;
     private PlaybackControlsRow mControlsRow;
@@ -187,22 +197,21 @@
         }
     };
 
-    private final OnItemViewClickedListener mOnItemViewClickedListener =
-            new OnItemViewClickedListener() {
-        @Override
-        public void onItemClicked(Presenter.ViewHolder viewHolder, Object object,
-                                  RowPresenter.ViewHolder viewHolder2, Row row) {
-            if (DEBUG) Log.v(TAG, "onItemClicked " + object);
-            boolean handled = false;
-            if (object instanceof Action) {
-                handled = dispatchAction((Action) object, null);
-            }
-            if (!handled && mExternalOnItemViewClickedListener != null) {
-                mExternalOnItemViewClickedListener.onItemClicked(viewHolder, object,
-                        viewHolder2, row);
-            }
-        }
-    };
+    /**
+     * Interface allowing the application to handle input events.
+     * @deprecated Use
+     * {@link PlaybackGlue.PlaybackGlueHost#setOnKeyInterceptListener(View.OnKeyListener)}.
+     */
+    @Deprecated
+    public interface InputEventHandler {
+        /**
+         * Called when an {@link InputEvent} is received.
+         *
+         * @return If the event should be consumed, return true. To allow the event to
+         * continue on to the next handler, return false.
+         */
+        boolean handleInputEvent(InputEvent event);
+    }
 
     /**
      * Constructor for the glue.
@@ -211,7 +220,7 @@
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
      */
     public PlaybackControlGlue(Context context, int[] seekSpeeds) {
-        this(context, null, seekSpeeds, seekSpeeds);
+        this(context, (PlaybackGlueHost) null, seekSpeeds, seekSpeeds);
     }
 
     /**
@@ -224,7 +233,7 @@
     public PlaybackControlGlue(Context context,
                                int[] fastForwardSpeeds,
                                int[] rewindSpeeds) {
-        this(context, null, fastForwardSpeeds, rewindSpeeds);
+        this(context, (PlaybackGlueHost) null, fastForwardSpeeds, rewindSpeeds);
     }
 
     /**
@@ -233,7 +242,10 @@
      * @param context
      * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
+     * @deprecated Use
+     * {@link #PlaybackControlGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
+    @Deprecated
     public PlaybackControlGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] seekSpeeds) {
@@ -247,16 +259,31 @@
      * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param fastForwardSpeeds Array of seek speeds for fast forward.
      * @param rewindSpeeds Array of seek speeds for rewind.
+     * @deprecated Use
+     * {@link #PlaybackControlGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
+    @Deprecated
     public PlaybackControlGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] fastForwardSpeeds,
                                int[] rewindSpeeds) {
-        mContext = context;
-        mFragment = fragment;
-        if (fragment != null) {
-            attachToFragment();
-        }
+        this(context, fragment == null ? (PlaybackGlueHost) null:
+                new PlaybackGlueHostOld(fragment), fastForwardSpeeds, rewindSpeeds);
+    }
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param host Optional; if using a {@link PlaybackGlue.PlaybackGlueHost}, pass it in.
+     * @param fastForwardSpeeds Array of seek speeds for fast forward.
+     * @param rewindSpeeds Array of seek speeds for rewind.
+     */
+    public PlaybackControlGlue(Context context,
+                               PlaybackGlueHost host,
+                               int[] fastForwardSpeeds,
+                               int[] rewindSpeeds) {
+        super(context);
         if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
             throw new IllegalStateException("invalid fastForwardSpeeds array size");
         }
@@ -265,34 +292,43 @@
             throw new IllegalStateException("invalid rewindSpeeds array size");
         }
         mRewindSpeeds = rewindSpeeds;
+        setHost(host);
     }
 
-    private final PlaybackOverlayFragment.InputEventHandler mOnInputEventHandler =
-            new PlaybackOverlayFragment.InputEventHandler() {
-        @Override
-        public boolean handleInputEvent(InputEvent event) {
-            if (event instanceof KeyEvent) {
-                KeyEvent keyEvent = (KeyEvent) event;
-                return onKey(null, keyEvent.getKeyCode(), keyEvent);
+    @Override
+    public void setHost(PlaybackGlueHost host) {
+        super.setHost(host);
+        if (getHost() != null) {
+            if (getHost() instanceof PlaybackGlueHostOld) {
+                ((PlaybackGlueHostOld) getHost()).mGlue = this;
             }
-            return false;
-        }
-    };
+            getHost().setOnKeyInterceptListener(this);
+            getHost().setOnActionClickedListener(this);
+            getHost().setPlaybackRowPresenter(createControlsRowAndPresenter());
+            getHost().setPlaybackRow(getControlsRow());
+            getHost().setHostLifeCycleCallback(new HostLifecycleCallback() {
+                @Override
+                public void onHostStart() {
+                    enableProgressUpdating(true);
+                }
 
-    private void attachToFragment() {
-        mFragment.setInputEventHandler(mOnInputEventHandler);
+                @Override
+                public void onHostStop() {
+                    enableProgressUpdating(false);
+                }
+            });
+        }
     }
 
     /**
-     * Helper method for instantiating a
-     * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
-     * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}.
+     * Helper method for instantiating a {@link PlaybackControlsRow} and corresponding
+     * {@link PlaybackControlsRowPresenter}.
      */
     public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
         PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
         setControlsRow(controlsRow);
 
-        AbstractDetailsDescriptionPresenter detailsPresenter =
+        final AbstractDetailsDescriptionPresenter detailsPresenter =
                 new AbstractDetailsDescriptionPresenter() {
             @Override
             protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder
@@ -307,6 +343,7 @@
                 }
             }
         };
+
         return new PlaybackControlsRowPresenter(detailsPresenter) {
             @Override
             protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
@@ -323,16 +360,14 @@
 
     /**
      * Returns the fragment.
+     * @deprecated The glue is no longer associated with a fragment, use {@link #getHost()}.
      */
+    @Deprecated
     public PlaybackOverlayFragment getFragment() {
-        return mFragment;
-    }
-
-    /**
-     * Returns the context.
-     */
-    public Context getContext() {
-        return mContext;
+        if (getHost() instanceof PlaybackGlueHostOld) {
+            return ((PlaybackGlueHostOld)getHost()).mFragment;
+        }
+        return null;
     }
 
     /**
@@ -354,8 +389,8 @@
      */
     public void setFadingEnabled(boolean enable) {
         mFadeWhenPlaying = enable;
-        if (!mFadeWhenPlaying && mFragment != null) {
-            mFragment.setFadingEnabled(false);
+        if (!mFadeWhenPlaying && getHost() != null) {
+            getHost().setFadingEnabled(false);
         }
     }
 
@@ -370,20 +405,18 @@
      * Set the {@link OnItemViewClickedListener} to be called if the click event
      * is not handled internally.
      * @param listener
-     * @deprecated Don't call this.  Instead set the listener on the fragment yourself,
-     * and call {@link #onActionClicked} to handle clicks.
+     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
      */
     @Deprecated
     public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
         mExternalOnItemViewClickedListener = listener;
-        if (mFragment != null) {
-            mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
     }
 
     /**
      * Returns the {@link OnItemViewClickedListener}.
+     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
      */
+    @Deprecated
     public OnItemViewClickedListener getOnItemViewClickedListener() {
         return mExternalOnItemViewClickedListener;
     }
@@ -454,8 +487,8 @@
             case KeyEvent.KEYCODE_DPAD_LEFT:
             case KeyEvent.KEYCODE_BACK:
             case KeyEvent.KEYCODE_ESCAPE:
-                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 ||
-                        mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
+                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0
+                        || mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
                 if (abortSeek) {
                     mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
                     startPlayback(mPlaybackSpeed);
@@ -465,14 +498,15 @@
                 return false;
         }
         Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode);
+
         if (action != null) {
-            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_REWIND) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
-                if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
-                    dispatchAction(action, (KeyEvent) event);
+            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE)
+                    || action == mPrimaryActionsAdapter.lookup(ACTION_REWIND)
+                    || action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD)
+                    || action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS)
+                    || action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
+                if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                    dispatchAction(action, event);
                 }
                 return true;
             }
@@ -486,12 +520,12 @@
     boolean dispatchAction(Action action, KeyEvent keyEvent) {
         boolean handled = false;
         if (action == mPlayPauseAction) {
-            boolean canPlay = keyEvent == null ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
-            boolean canPause = keyEvent == null ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
+            boolean canPlay = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
+            boolean canPause = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
             //            PLAY_PAUSE    PLAY      PAUSE
             // playing    paused                  paused
             // paused     playing       playing
@@ -578,7 +612,7 @@
             return;
         }
 
-        if (DEBUG) Log.v(TAG, "updateRowMetadata hasValidMedia " + hasValidMedia());
+        if (DEBUG) Log.v(TAG, "updateRowMetadata");
 
         if (!hasValidMedia()) {
             mControlsRow.setImageDrawable(null);
@@ -590,7 +624,9 @@
             mControlsRow.setCurrentTime(getCurrentPosition());
         }
 
-        onRowChanged(mControlsRow);
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
     }
 
     void updatePlaybackState() {
@@ -608,7 +644,7 @@
         final long actions = getSupportedActions();
         if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
             if (mSkipPreviousAction == null) {
-                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(mContext);
+                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getContext());
             }
             mPrimaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
         } else {
@@ -617,7 +653,8 @@
         }
         if ((actions & ACTION_REWIND) != 0) {
             if (mRewindAction == null) {
-                mRewindAction = new PlaybackControlsRow.RewindAction(mContext,
+                mRewindAction = new PlaybackControlsRow.RewindAction(
+                        getContext(),
                         mRewindSpeeds.length);
             }
             mPrimaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
@@ -627,7 +664,7 @@
         }
         if ((actions & ACTION_PLAY_PAUSE) != 0) {
             if (mPlayPauseAction == null) {
-                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(mContext);
+                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
             }
             mPrimaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
         } else {
@@ -636,7 +673,8 @@
         }
         if ((actions & ACTION_FAST_FORWARD) != 0) {
             if (mFastForwardAction == null) {
-                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(mContext,
+                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(
+                        getContext(),
                         mFastForwardSpeeds.length);
             }
             mPrimaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
@@ -646,7 +684,7 @@
         }
         if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
             if (mSkipNextAction == null) {
-                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(mContext);
+                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(getContext());
             }
             mPrimaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
         } else {
@@ -682,14 +720,14 @@
             enableProgressUpdating(true);
         }
 
-        if (mFadeWhenPlaying && mFragment != null) {
-            mFragment.setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
+        if (mFadeWhenPlaying && getHost() != null) {
+            getHost().setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
         }
 
         if (mPlayPauseAction != null) {
-            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED ?
-                    PlaybackControlsRow.PlayPauseAction.PLAY :
-                    PlaybackControlsRow.PlayPauseAction.PAUSE;
+            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED
+                    ? PlaybackControlsRow.PlayPauseAction.PLAY
+                    : PlaybackControlsRow.PlayPauseAction.PAUSE;
             if (mPlayPauseAction.getIndex() != index) {
                 mPlayPauseAction.setIndex(index);
                 notifyItemChanged(mPrimaryActionsAdapter, mPlayPauseAction);
@@ -784,32 +822,48 @@
 
     /**
      * Start playback at the given speed.
+     * @deprecated use {@link #play()} instead.
+     *
      * @param speed The desired playback speed.  For normal playback this will be
      *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
      *              and negative values for rewind.
      */
-    protected abstract void startPlayback(int speed);
+    @Deprecated
+    protected void startPlayback(int speed) {}
 
     /**
      * Pause playback.
+     * @deprecated use {@link #pause()} instead.
      */
-    protected abstract void pausePlayback();
+    @Deprecated
+    protected void pausePlayback() {}
 
     /**
      * Skip to the next track.
+     * @deprecated use {@link #next()} instead.
      */
-    protected abstract void skipToNext();
+    @Deprecated
+    protected void skipToNext() {}
 
     /**
      * Skip to the previous track.
+     * @deprecated use {@link #previous()} instead.
      */
-    protected abstract void skipToPrevious();
+    @Deprecated
+    protected void skipToPrevious() {}
 
     /**
-     * Invoked when the playback controls row has changed.  The adapter containing this row
-     * should be notified.
+     * This method invoked when the playback controls row has changed. The adapter
+     * containing this row should be notified. This method would be delegated to
+     * {@see android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost#notifyPlaybackRowChanged}.
+     * @deprecated see {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
      */
-    protected abstract void onRowChanged(PlaybackControlsRow row);
+    @Deprecated
+    protected void onRowChanged(PlaybackControlsRow row) {
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
 
     /**
      * Creates the primary action adapter.  May be overridden to add additional primary
@@ -853,4 +907,49 @@
         if (DEBUG) Log.v(TAG, "onMetadataChanged");
         updateRowMetadata();
     }
+
+    static final class PlaybackGlueHostOld extends PlaybackGlueHost {
+        final PlaybackOverlayFragment mFragment;
+        PlaybackControlGlue mGlue;
+
+        public PlaybackGlueHostOld(PlaybackOverlayFragment fragment) {
+            mFragment = fragment;
+        }
+
+        @Override
+        public void setFadingEnabled(boolean enable) {
+            mFragment.setFadingEnabled(enable);
+        }
+
+        @Override
+        public void setOnKeyInterceptListener(final View.OnKeyListener onKeyListenerr) {
+            mFragment.setEventHandler( new InputEventHandler() {
+                @Override
+                public boolean handleInputEvent(InputEvent event) {
+                    if (event instanceof KeyEvent) {
+                        KeyEvent keyEvent = (KeyEvent) event;
+                        return onKeyListenerr.onKey(null, keyEvent.getKeyCode(), keyEvent);
+                    }
+                    return false;
+                }
+            });
+        }
+
+        @Override
+        public void setOnActionClickedListener(final OnActionClickedListener listener) {
+            mFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() {
+                @Override
+                public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                                          RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    if (item instanceof Action) {
+                        listener.onActionClicked((Action)item);
+                        if (mGlue.mExternalOnItemViewClickedListener != null) {
+                            mGlue.mExternalOnItemViewClickedListener.onItemClicked(itemViewHolder,
+                                    item, rowViewHolder, row);
+                        }
+                    }
+                }
+            });
+        }
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
index 1e1eab5..ec3cc1e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
@@ -3,852 +3,103 @@
 package android.support.v17.leanback.app;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
 import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.util.Log;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.View;
 
-
 /**
- * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow} and
- * {@link PlaybackOverlaySupportFragment} that implements a recommended approach to handling standard
- * playback control actions such as play/pause, fast forward/rewind at progressive speed levels,
- * and skip to next/previous.  This helper class is a glue layer in that it manages the
- * configuration of and interaction between the leanback UI components by defining a functional
- * interface to the media player.
- *
- * <p>You can instantiate a concrete subclass such as {@link MediaControllerGlue} or you must
- * subclass this abstract helper.  To create a subclass you must implement all of the
- * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
- * {@link #onStateChanged()} appropriately.
- * </p>
- *
- * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
- * inform the glue what speed levels are supported for fast forward/rewind.  Providing a
- * {@link android.support.v17.leanback.app.PlaybackOverlaySupportFragment} is optional.
- * </p>
- *
- * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
- * The row will be updated by the glue layer based on the media metadata and playback state.
- * Alternatively, you may call {@link #createControlsRowAndPresenter()} which will set a controls
- * row and return a row presenter you can use to present the row.
- * </p>
- *
- * <p>The helper sets a {@link android.support.v17.leanback.widget.SparseArrayObjectAdapter}
- * on the controls row as the primary actions adapter, and adds actions to it.  You can provide
- * additional actions by overriding {@link #createPrimaryActionsAdapter}.  This helper does not
- * deal in secondary actions so those you may add separately.
- * </p>
- *
- * <p>Provide a click listener on your fragment and if an action is clicked, call
- * {@link #onActionClicked}.  There is no need to call {@link #setOnItemViewClickedListener}
- * but if you do a click listener will be installed on the fragment and recognized action clicks
- * will be handled.  Your listener will be called only for unhandled actions.
- * </p>
- *
- * <p>The helper implements a key event handler.  If you pass a
- * {@link android.support.v17.leanback.app.PlaybackOverlaySupportFragment} the fragment's input event
- * handler will be set.  Otherwise, you should set the glue object as key event handler to the
- * ViewHolder when bound by your row presenter; see
- * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
- * </p>
- *
- * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
- * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
- * {@link #getUpdatePeriod()} provides a recommended update period.
- * </p>
- *
+ * @deprecated Use {@link PlaybackControlGlue} and {@link PlaybackSupportFragmentGlueHost} for
+ * {@link PlaybackSupportFragment}.
  */
-public abstract class PlaybackControlSupportGlue implements OnActionClickedListener, View.OnKeyListener {
-    /**
-     * The adapter key for the first custom control on the left side
-     * of the predefined primary controls.
-     */
-    public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1;
+@Deprecated
+public abstract class PlaybackControlSupportGlue extends PlaybackControlGlue {
 
-    /**
-     * The adapter key for the skip to previous control.
-     */
-    public static final int ACTION_SKIP_TO_PREVIOUS = 0x10;
-
-    /**
-     * The adapter key for the rewind control.
-     */
-    public static final int ACTION_REWIND = 0x20;
-
-    /**
-     * The adapter key for the play/pause control.
-     */
-    public static final int ACTION_PLAY_PAUSE = 0x40;
-
-    /**
-     * The adapter key for the fast forward control.
-     */
-    public static final int ACTION_FAST_FORWARD = 0x80;
-
-    /**
-     * The adapter key for the skip to next control.
-     */
-    public static final int ACTION_SKIP_TO_NEXT = 0x100;
-
-    /**
-     * The adapter key for the first custom control on the right side
-     * of the predefined primary controls.
-     */
-    public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000;
-
-    /**
-     * Invalid playback speed.
-     */
-    public static final int PLAYBACK_SPEED_INVALID = -1;
-
-    /**
-     * Speed representing playback state that is paused.
-     */
-    public static final int PLAYBACK_SPEED_PAUSED = 0;
-
-    /**
-     * Speed representing playback state that is playing normally.
-     */
-    public static final int PLAYBACK_SPEED_NORMAL = 1;
-
-    /**
-     * The initial (level 0) fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L0 = 10;
-
-    /**
-     * The level 1 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L1 = 11;
-
-    /**
-     * The level 2 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L2 = 12;
-
-    /**
-     * The level 3 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L3 = 13;
-
-    /**
-     * The level 4 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L4 = 14;
-
-    static final String TAG = "PlaybackControlSupportGlue";
-    static final boolean DEBUG = false;
-
-    static final int MSG_UPDATE_PLAYBACK_STATE = 100;
-    private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
-    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4 -
-            PLAYBACK_SPEED_FAST_L0 + 1;
-
-    private final PlaybackOverlaySupportFragment mFragment;
-    private final Context mContext;
-    private final int[] mFastForwardSpeeds;
-    private final int[] mRewindSpeeds;
-    private PlaybackControlsRow mControlsRow;
-    private SparseArrayObjectAdapter mPrimaryActionsAdapter;
-    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
-    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
-    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
-    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
-    private PlaybackControlsRow.RewindAction mRewindAction;
-    OnItemViewClickedListener mExternalOnItemViewClickedListener;
-    private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-    private boolean mFadeWhenPlaying = true;
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
-                updatePlaybackState();
-            }
-        }
-    };
-
-    private final OnItemViewClickedListener mOnItemViewClickedListener =
-            new OnItemViewClickedListener() {
-        @Override
-        public void onItemClicked(Presenter.ViewHolder viewHolder, Object object,
-                                  RowPresenter.ViewHolder viewHolder2, Row row) {
-            if (DEBUG) Log.v(TAG, "onItemClicked " + object);
-            boolean handled = false;
-            if (object instanceof Action) {
-                handled = dispatchAction((Action) object, null);
-            }
-            if (!handled && mExternalOnItemViewClickedListener != null) {
-                mExternalOnItemViewClickedListener.onItemClicked(viewHolder, object,
-                        viewHolder2, row);
-            }
-        }
-    };
-
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
-     */
     public PlaybackControlSupportGlue(Context context, int[] seekSpeeds) {
-        this(context, null, seekSpeeds, seekSpeeds);
+        super(context, seekSpeeds);
     }
 
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public PlaybackControlSupportGlue(Context context,
-                               int[] fastForwardSpeeds,
-                               int[] rewindSpeeds) {
-        this(context, null, fastForwardSpeeds, rewindSpeeds);
+    public PlaybackControlSupportGlue(
+            Context context, int[] fastForwardSpeeds, int[] rewindSpeeds) {
+        super(context, fastForwardSpeeds, rewindSpeeds);
     }
 
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param fragment Optional; if using a {@link PlaybackOverlaySupportFragment}, pass it in.
-     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
-     */
-    public PlaybackControlSupportGlue(Context context,
-                               PlaybackOverlaySupportFragment fragment,
-                               int[] seekSpeeds) {
-        this(context, fragment, seekSpeeds, seekSpeeds);
+    public PlaybackControlSupportGlue(
+            Context context,
+            PlaybackOverlaySupportFragment fragment,
+            int[] seekSpeeds) {
+        super(context,
+                fragment == null ? null: new PlaybackSupportGlueHostOld(fragment),
+                seekSpeeds,
+                seekSpeeds);
     }
 
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param fragment Optional; if using a {@link PlaybackOverlaySupportFragment}, pass it in.
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public PlaybackControlSupportGlue(Context context,
-                               PlaybackOverlaySupportFragment fragment,
-                               int[] fastForwardSpeeds,
-                               int[] rewindSpeeds) {
-        mContext = context;
-        mFragment = fragment;
-        if (fragment != null) {
-            attachToFragment();
+    public PlaybackControlSupportGlue(
+            Context context,
+            PlaybackOverlaySupportFragment fragment,
+            int[] fastForwardSpeeds,
+            int[] rewindSpeeds) {
+        super(context,
+                fragment == null ? null: new PlaybackSupportGlueHostOld(fragment),
+                fastForwardSpeeds,
+                rewindSpeeds);
+    }
+
+    @Override
+    public void setHost(PlaybackGlueHost host) {
+        super.setHost(host);
+        if (host instanceof PlaybackSupportGlueHostOld) {
+            ((PlaybackSupportGlueHostOld) host).mGlue = this;
         }
-        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid fastForwardSpeeds array size");
-        }
-        mFastForwardSpeeds = fastForwardSpeeds;
-        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid rewindSpeeds array size");
-        }
-        mRewindSpeeds = rewindSpeeds;
     }
 
-    private final PlaybackOverlaySupportFragment.InputEventHandler mOnInputEventHandler =
-            new PlaybackOverlaySupportFragment.InputEventHandler() {
+    static final class PlaybackSupportGlueHostOld extends PlaybackGlueHost {
+        final PlaybackOverlaySupportFragment mFragment;
+        PlaybackControlGlue mGlue;
+
+        public PlaybackSupportGlueHostOld(PlaybackOverlaySupportFragment fragment) {
+            mFragment = fragment;
+        }
+
         @Override
-        public boolean handleInputEvent(InputEvent event) {
-            if (event instanceof KeyEvent) {
-                KeyEvent keyEvent = (KeyEvent) event;
-                return onKey(null, keyEvent.getKeyCode(), keyEvent);
-            }
-            return false;
+        public void setFadingEnabled(boolean enable) {
+            mFragment.setFadingEnabled(enable);
         }
-    };
 
-    private void attachToFragment() {
-        mFragment.setInputEventHandler(mOnInputEventHandler);
-    }
-
-    /**
-     * Helper method for instantiating a
-     * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
-     * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}.
-     */
-    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
-        PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
-        setControlsRow(controlsRow);
-
-        AbstractDetailsDescriptionPresenter detailsPresenter =
-                new AbstractDetailsDescriptionPresenter() {
-            @Override
-            protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder
-                                                     viewHolder, Object object) {
-                PlaybackControlSupportGlue glue = (PlaybackControlSupportGlue) object;
-                if (glue.hasValidMedia()) {
-                    viewHolder.getTitle().setText(glue.getMediaTitle());
-                    viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
-                } else {
-                    viewHolder.getTitle().setText("");
-                    viewHolder.getSubtitle().setText("");
+        @Override
+        public void setOnKeyInterceptListener(final View.OnKeyListener onKeyListenerr) {
+            mFragment.setEventHandler( new InputEventHandler() {
+                @Override
+                public boolean handleInputEvent(InputEvent event) {
+                    if (event instanceof KeyEvent) {
+                        KeyEvent keyEvent = (KeyEvent) event;
+                        return onKeyListenerr.onKey(null, keyEvent.getKeyCode(), keyEvent);
+                    }
+                    return false;
                 }
-            }
-        };
-        return new PlaybackControlsRowPresenter(detailsPresenter) {
-            @Override
-            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
-                super.onBindRowViewHolder(vh, item);
-                vh.setOnKeyListener(PlaybackControlSupportGlue.this);
-            }
-            @Override
-            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
-                super.onUnbindRowViewHolder(vh);
-                vh.setOnKeyListener(null);
-            }
-        };
-    }
-
-    /**
-     * Returns the fragment.
-     */
-    public PlaybackOverlaySupportFragment getFragment() {
-        return mFragment;
-    }
-
-    /**
-     * Returns the context.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Returns the fast forward speeds.
-     */
-    public int[] getFastForwardSpeeds() {
-        return mFastForwardSpeeds;
-    }
-
-    /**
-     * Returns the rewind speeds.
-     */
-    public int[] getRewindSpeeds() {
-        return mRewindSpeeds;
-    }
-
-    /**
-     * Sets the controls to fade after a timeout when media is playing.
-     */
-    public void setFadingEnabled(boolean enable) {
-        mFadeWhenPlaying = enable;
-        if (!mFadeWhenPlaying && mFragment != null) {
-            mFragment.setFadingEnabled(false);
+            });
         }
-    }
 
-    /**
-     * Returns true if controls are set to fade when media is playing.
-     */
-    public boolean isFadingEnabled() {
-        return mFadeWhenPlaying;
-    }
-
-    /**
-     * Set the {@link OnItemViewClickedListener} to be called if the click event
-     * is not handled internally.
-     * @param listener
-     * @deprecated Don't call this.  Instead set the listener on the fragment yourself,
-     * and call {@link #onActionClicked} to handle clicks.
-     */
-    @Deprecated
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mExternalOnItemViewClickedListener = listener;
-        if (mFragment != null) {
-            mFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-    }
-
-    /**
-     * Returns the {@link OnItemViewClickedListener}.
-     */
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mExternalOnItemViewClickedListener;
-    }
-
-    /**
-     * Sets the controls row to be managed by the glue layer.
-     * The primary actions and playback state related aspects of the row
-     * are updated by the glue.
-     */
-    public void setControlsRow(PlaybackControlsRow controlsRow) {
-        mControlsRow = controlsRow;
-        mPrimaryActionsAdapter = createPrimaryActionsAdapter(
-                new ControlButtonPresenterSelector());
-        mControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
-        updateControlsRow();
-    }
-
-    /**
-     * Returns the playback controls row managed by the glue layer.
-     */
-    public PlaybackControlsRow getControlsRow() {
-        return mControlsRow;
-    }
-
-    /**
-     * Override this to start/stop a runnable to call {@link #updateProgress} at
-     * an interval such as {@link #getUpdatePeriod}.
-     */
-    public void enableProgressUpdating(boolean enable) {
-    }
-
-    /**
-     * Returns the time period in milliseconds that should be used
-     * to update the progress.  See {@link #updateProgress()}.
-     */
-    public int getUpdatePeriod() {
-        // TODO: calculate a better update period based on total duration and screen size
-        return 500;
-    }
-
-    /**
-     * Updates the progress bar based on the current media playback position.
-     */
-    public void updateProgress() {
-        int position = getCurrentPosition();
-        if (DEBUG) Log.v(TAG, "updateProgress " + position);
-        mControlsRow.setCurrentTime(position);
-    }
-
-    /**
-     * Handles action clicks.  A subclass may override this add support for additional actions.
-     */
-    @Override
-    public void onActionClicked(Action action) {
-        dispatchAction(action, null);
-    }
-
-    /**
-     * Handles key events and returns true if handled.  A subclass may override this to provide
-     * additional support.
-     */
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_BACK:
-            case KeyEvent.KEYCODE_ESCAPE:
-                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0 ||
-                        mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
-                if (abortSeek) {
-                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                    startPlayback(mPlaybackSpeed);
-                    updatePlaybackStatusAfterUserAction();
-                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
+        @Override
+        public void setOnActionClickedListener(final OnActionClickedListener listener) {
+            mFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() {
+                @Override
+                public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                                          RowPresenter.ViewHolder rowViewHolder, Row row) {
+                    if (item instanceof Action) {
+                        listener.onActionClicked((Action)item);
+                        if (mGlue.mExternalOnItemViewClickedListener != null) {
+                            mGlue.mExternalOnItemViewClickedListener.onItemClicked(itemViewHolder,
+                                    item, rowViewHolder, row);
+                        }
+                    }
                 }
-                return false;
+            });
         }
-        Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode);
-        if (action != null) {
-            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_REWIND) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS) ||
-                    action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
-                if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
-                    dispatchAction(action, (KeyEvent) event);
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called when the given action is invoked, either by click or keyevent.
-     */
-    boolean dispatchAction(Action action, KeyEvent keyEvent) {
-        boolean handled = false;
-        if (action == mPlayPauseAction) {
-            boolean canPlay = keyEvent == null ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
-            boolean canPause = keyEvent == null ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                    keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
-            if (mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
-                if (canPlay) {
-                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                    startPlayback(mPlaybackSpeed);
-                }
-            } else if (canPause) {
-                mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
-                pausePlayback();
-            }
-            updatePlaybackStatusAfterUserAction();
-            handled = true;
-        } else if (action == mSkipNextAction) {
-            skipToNext();
-            handled = true;
-        } else if (action == mSkipPreviousAction) {
-            skipToPrevious();
-            handled = true;
-        } else if (action == mFastForwardAction) {
-            if (mPlaybackSpeed < getMaxForwardSpeedId()) {
-                switch (mPlaybackSpeed) {
-                    case PLAYBACK_SPEED_FAST_L0:
-                    case PLAYBACK_SPEED_FAST_L1:
-                    case PLAYBACK_SPEED_FAST_L2:
-                    case PLAYBACK_SPEED_FAST_L3:
-                        mPlaybackSpeed++;
-                        break;
-                    default:
-                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
-                        break;
-                }
-                startPlayback(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        } else if (action == mRewindAction) {
-            if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
-                switch (mPlaybackSpeed) {
-                    case -PLAYBACK_SPEED_FAST_L0:
-                    case -PLAYBACK_SPEED_FAST_L1:
-                    case -PLAYBACK_SPEED_FAST_L2:
-                    case -PLAYBACK_SPEED_FAST_L3:
-                        mPlaybackSpeed--;
-                        break;
-                    default:
-                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
-                        break;
-                }
-                startPlayback(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        }
-        return handled;
-    }
-
-    private int getMaxForwardSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
-    }
-
-    private int getMaxRewindSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
-    }
-
-    private void updateControlsRow() {
-        updateRowMetadata();
-        mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
-        updatePlaybackState();
-    }
-
-    private void updatePlaybackStatusAfterUserAction() {
-        updatePlaybackState(mPlaybackSpeed);
-        // Sync playback state after a delay
-        mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
-        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE,
-                UPDATE_PLAYBACK_STATE_DELAY_MS);
-    }
-
-    private void updateRowMetadata() {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        if (DEBUG) Log.v(TAG, "updateRowMetadata hasValidMedia " + hasValidMedia());
-
-        if (!hasValidMedia()) {
-            mControlsRow.setImageDrawable(null);
-            mControlsRow.setTotalTime(0);
-            mControlsRow.setCurrentTime(0);
-        } else {
-            mControlsRow.setImageDrawable(getMediaArt());
-            mControlsRow.setTotalTime(getMediaDuration());
-            mControlsRow.setCurrentTime(getCurrentPosition());
-        }
-
-        onRowChanged(mControlsRow);
-    }
-
-    void updatePlaybackState() {
-        if (hasValidMedia()) {
-            mPlaybackSpeed = getCurrentSpeedId();
-            updatePlaybackState(mPlaybackSpeed);
-        }
-    }
-
-    private void updatePlaybackState(int playbackSpeed) {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        final long actions = getSupportedActions();
-        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
-            if (mSkipPreviousAction == null) {
-                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(mContext);
-            }
-            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS);
-            mSkipPreviousAction = null;
-        }
-        if ((actions & ACTION_REWIND) != 0) {
-            if (mRewindAction == null) {
-                mRewindAction = new PlaybackControlsRow.RewindAction(mContext,
-                        mRewindSpeeds.length);
-            }
-            mPrimaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_REWIND);
-            mRewindAction = null;
-        }
-        if ((actions & ACTION_PLAY_PAUSE) != 0) {
-            if (mPlayPauseAction == null) {
-                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(mContext);
-            }
-            mPrimaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_PLAY_PAUSE);
-            mPlayPauseAction = null;
-        }
-        if ((actions & ACTION_FAST_FORWARD) != 0) {
-            if (mFastForwardAction == null) {
-                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(mContext,
-                        mFastForwardSpeeds.length);
-            }
-            mPrimaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_FAST_FORWARD);
-            mFastForwardAction = null;
-        }
-        if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
-            if (mSkipNextAction == null) {
-                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(mContext);
-            }
-            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT);
-            mSkipNextAction = null;
-        }
-
-        if (mFastForwardAction != null) {
-            int index = 0;
-            if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) {
-                index = playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mFastForwardAction.getIndex() != index) {
-                mFastForwardAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mFastForwardAction);
-            }
-        }
-        if (mRewindAction != null) {
-            int index = 0;
-            if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
-                index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mRewindAction.getIndex() != index) {
-                mRewindAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mRewindAction);
-            }
-        }
-
-        if (playbackSpeed == PLAYBACK_SPEED_PAUSED) {
-            updateProgress();
-            enableProgressUpdating(false);
-        } else {
-            enableProgressUpdating(true);
-        }
-
-        if (mFadeWhenPlaying && mFragment != null) {
-            mFragment.setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
-        }
-
-        if (mPlayPauseAction != null) {
-            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED ?
-                    PlaybackControlsRow.PlayPauseAction.PLAY :
-                    PlaybackControlsRow.PlayPauseAction.PAUSE;
-            if (mPlayPauseAction.getIndex() != index) {
-                mPlayPauseAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mPlayPauseAction);
-            }
-        }
-    }
-
-    private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) {
-        int index = adapter.indexOf(object);
-        if (index >= 0) {
-            adapter.notifyArrayItemRangeChanged(index, 1);
-        }
-    }
-
-    private static String getSpeedString(int speed) {
-        switch (speed) {
-            case PLAYBACK_SPEED_INVALID:
-                return "PLAYBACK_SPEED_INVALID";
-            case PLAYBACK_SPEED_PAUSED:
-                return "PLAYBACK_SPEED_PAUSED";
-            case PLAYBACK_SPEED_NORMAL:
-                return "PLAYBACK_SPEED_NORMAL";
-            case PLAYBACK_SPEED_FAST_L0:
-                return "PLAYBACK_SPEED_FAST_L0";
-            case PLAYBACK_SPEED_FAST_L1:
-                return "PLAYBACK_SPEED_FAST_L1";
-            case PLAYBACK_SPEED_FAST_L2:
-                return "PLAYBACK_SPEED_FAST_L2";
-            case PLAYBACK_SPEED_FAST_L3:
-                return "PLAYBACK_SPEED_FAST_L3";
-            case PLAYBACK_SPEED_FAST_L4:
-                return "PLAYBACK_SPEED_FAST_L4";
-            case -PLAYBACK_SPEED_FAST_L0:
-                return "-PLAYBACK_SPEED_FAST_L0";
-            case -PLAYBACK_SPEED_FAST_L1:
-                return "-PLAYBACK_SPEED_FAST_L1";
-            case -PLAYBACK_SPEED_FAST_L2:
-                return "-PLAYBACK_SPEED_FAST_L2";
-            case -PLAYBACK_SPEED_FAST_L3:
-                return "-PLAYBACK_SPEED_FAST_L3";
-            case -PLAYBACK_SPEED_FAST_L4:
-                return "-PLAYBACK_SPEED_FAST_L4";
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if there is a valid media item.
-     */
-    public abstract boolean hasValidMedia();
-
-    /**
-     * Returns true if media is currently playing.
-     */
-    public abstract boolean isMediaPlaying();
-
-    /**
-     * Returns the title of the media item.
-     */
-    public abstract CharSequence getMediaTitle();
-
-    /**
-     * Returns the subtitle of the media item.
-     */
-    public abstract CharSequence getMediaSubtitle();
-
-    /**
-     * Returns the duration of the media item in milliseconds.
-     */
-    public abstract int getMediaDuration();
-
-    /**
-     * Returns a bitmap of the art for the media item.
-     */
-    public abstract Drawable getMediaArt();
-
-    /**
-     * Returns a bitmask of actions supported by the media player.
-     */
-    public abstract long getSupportedActions();
-
-    /**
-     * Returns the current playback speed.  When playing normally,
-     * {@link #PLAYBACK_SPEED_NORMAL} should be returned.
-     */
-    public abstract int getCurrentSpeedId();
-
-    /**
-     * Returns the current position of the media item in milliseconds.
-     */
-    public abstract int getCurrentPosition();
-
-    /**
-     * Start playback at the given speed.
-     * @param speed The desired playback speed.  For normal playback this will be
-     *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
-     *              and negative values for rewind.
-     */
-    protected abstract void startPlayback(int speed);
-
-    /**
-     * Pause playback.
-     */
-    protected abstract void pausePlayback();
-
-    /**
-     * Skip to the next track.
-     */
-    protected abstract void skipToNext();
-
-    /**
-     * Skip to the previous track.
-     */
-    protected abstract void skipToPrevious();
-
-    /**
-     * Invoked when the playback controls row has changed.  The adapter containing this row
-     * should be notified.
-     */
-    protected abstract void onRowChanged(PlaybackControlsRow row);
-
-    /**
-     * Creates the primary action adapter.  May be overridden to add additional primary
-     * actions to the adapter.
-     */
-    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
-            PresenterSelector presenterSelector) {
-        return new SparseArrayObjectAdapter(presenterSelector);
-    }
-
-    /**
-     * Must be called appropriately by a subclass when the playback state has changed.
-     * It updates the playback state displayed on the media player.
-     */
-    protected void onStateChanged() {
-        if (DEBUG) Log.v(TAG, "onStateChanged");
-        // If a pending control button update is present, delay
-        // the update until the state settles.
-        if (!hasValidMedia()) {
-            return;
-        }
-        if (mHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE)) {
-            mHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE);
-            if (getCurrentSpeedId() != mPlaybackSpeed) {
-                if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update");
-                mHandler.sendEmptyMessageDelayed(MSG_UPDATE_PLAYBACK_STATE,
-                        UPDATE_PLAYBACK_STATE_DELAY_MS);
-            } else {
-                if (DEBUG) Log.v(TAG, "Update state matches expectation");
-                updatePlaybackState();
-            }
-        } else {
-            updatePlaybackState();
-        }
-    }
-
-    /**
-     * Must be called appropriately by a subclass when the metadata state has changed.
-     */
-    protected void onMetadataChanged() {
-        if (DEBUG) Log.v(TAG, "onMetadataChanged");
-        updateRowMetadata();
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
new file mode 100644
index 0000000..b97930a
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Fragment;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.animation.LogAccelerateInterpolator;
+import android.support.v17.leanback.animation.LogDecelerateInterpolator;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.ItemBridgeAdapter;
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * A fragment for displaying playback controls and related content.
+ *
+ * <p>
+ * A PlaybackFragment renders the elements of its {@link ObjectAdapter} as a set
+ * of rows in a vertical list.  The Adapter's {@link PresenterSelector} must maintain subclasses
+ * of {@link RowPresenter}.
+ * </p>
+ * <p>
+ * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be
+ * at position 0 in the adapter.
+ * </p>
+ */
+public class PlaybackFragment extends Fragment {
+    /**
+     * No background.
+     */
+    public static final int BG_NONE = 0;
+
+    /**
+     * A dark translucent background.
+     */
+    public static final int BG_DARK = 1;
+    private PlaybackGlue.HostLifecycleCallback mHostLifecycleCallback;
+
+    /**
+     * Resets the focus on the button in the middle of control row.
+     */
+    public void resetFocus() {
+        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
+                .findViewHolderForAdapterPosition(0);
+        if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
+            ((PlaybackRowPresenter) vh.getPresenter()).onReappear(
+                    (RowPresenter.ViewHolder) vh.getViewHolder());
+        }
+    }
+
+    private class SetSelectionRunnable implements Runnable {
+        int mPosition;
+        boolean mSmooth = true;
+
+        @Override
+        public void run() {
+            if (mRowsFragment == null) {
+                return;
+            }
+            mRowsFragment.setSelectedPosition(mPosition, mSmooth);
+        }
+    }
+
+    /**
+     * A light translucent background.
+     */
+    public static final int BG_LIGHT = 2;
+    private RowsFragment mRowsFragment;
+    private ObjectAdapter mAdapter;
+    private PlaybackRowPresenter mPresenter;
+    private Row mRow;
+    private BaseOnItemViewClickedListener mExternalItemClickedListener;
+    private BaseOnItemViewClickedListener mPlaybackItemClickedListener;
+    private BaseOnItemViewClickedListener mOnItemViewClickedListener = new BaseOnItemViewClickedListener() {
+        @Override
+        public void onItemClicked(Presenter.ViewHolder itemViewHolder,
+                                  Object item,
+                                  RowPresenter.ViewHolder rowViewHolder,
+                                  Object row) {
+            if (mPlaybackItemClickedListener != null
+                    && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder) {
+                mPlaybackItemClickedListener.onItemClicked(
+                        itemViewHolder, item, rowViewHolder, row);
+            }
+            if (mExternalItemClickedListener != null) {
+                mExternalItemClickedListener.onItemClicked(
+                        itemViewHolder, item, rowViewHolder, row);
+            }
+        }
+    };
+    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
+
+    public ObjectAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    /**
+     * Listener allowing the application to receive notification of fade in and/or fade out
+     * completion events.
+     */
+    public static class OnFadeCompleteListener {
+        public void onFadeInComplete() {
+        }
+
+        public void onFadeOutComplete() {
+        }
+    }
+
+    private static final String TAG = "PlaybackFragment";
+    private static final boolean DEBUG = false;
+    private static final int ANIMATION_MULTIPLIER = 1;
+
+    private static int START_FADE_OUT = 1;
+
+    // Fading status
+    private static final int IDLE = 0;
+    private static final int IN = 1;
+    private static final int OUT = 2;
+
+    private int mPaddingTop;
+    private int mPaddingBottom;
+    private View mRootView;
+    private int mBackgroundType = BG_DARK;
+    private int mBgDarkColor;
+    private int mBgLightColor;
+    private int mShowTimeMs;
+    private int mMajorFadeTranslateY, mMinorFadeTranslateY;
+    private int mAnimationTranslateY;
+    private OnFadeCompleteListener mFadeCompleteListener;
+    private View.OnKeyListener mInputEventHandler;
+    private boolean mFadingEnabled = true;
+    private int mFadingStatus = IDLE;
+    private int mBgAlpha;
+    private ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
+    private ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
+    private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
+
+    private final Animator.AnimatorListener mFadeListener =
+            new Animator.AnimatorListener() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    enableVerticalGridAnimations(false);
+                }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
+                    if (mBgAlpha > 0) {
+                        enableVerticalGridAnimations(true);
+                        startFadeTimer();
+                        if (mFadeCompleteListener != null) {
+                            mFadeCompleteListener.onFadeInComplete();
+                        }
+                    } else {
+                        VerticalGridView verticalView = getVerticalGridView();
+                        // reset focus to the primary actions only if the selected row was the controls row
+                        if (verticalView != null && verticalView.getSelectedPosition() == 0) {
+                            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                                    verticalView.findViewHolderForAdapterPosition(0);
+                            if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
+                                ((PlaybackRowPresenter)vh.getPresenter()).onReappear(
+                                        (RowPresenter.ViewHolder) vh.getViewHolder());
+                            }
+                        }
+                        if (mFadeCompleteListener != null) {
+                            mFadeCompleteListener.onFadeOutComplete();
+                        }
+                    }
+                    mFadingStatus = IDLE;
+                }
+            };
+
+    VerticalGridView getVerticalGridView() {
+        if (mRowsFragment == null) {
+            return null;
+        }
+        return mRowsFragment.getVerticalGridView();
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message message) {
+            if (message.what == START_FADE_OUT && mFadingEnabled) {
+                fade(false);
+            }
+        }
+    };
+
+    private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener =
+            new VerticalGridView.OnTouchInterceptListener() {
+                @Override
+                public boolean onInterceptTouchEvent(MotionEvent event) {
+                    return onInterceptInputEvent(event);
+                }
+            };
+
+    private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener =
+            new VerticalGridView.OnKeyInterceptListener() {
+                @Override
+                public boolean onInterceptKeyEvent(KeyEvent event) {
+                    return onInterceptInputEvent(event);
+                }
+            };
+
+    private void setBgAlpha(int alpha) {
+        mBgAlpha = alpha;
+        if (mRootView != null) {
+            mRootView.getBackground().setAlpha(alpha);
+        }
+    }
+
+    private void enableVerticalGridAnimations(boolean enable) {
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setAnimateChildLayout(enable);
+        }
+    }
+
+    /**
+     * Enables or disables view fading.  If enabled,
+     * the view will be faded in when the fragment starts,
+     * and will fade out after a time period.  The timeout
+     * period is reset each time {@link #tickle} is called.
+     */
+    public void setFadingEnabled(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled);
+        if (enabled != mFadingEnabled) {
+            mFadingEnabled = enabled;
+            if (mFadingEnabled) {
+                if (isResumed() && mFadingStatus == IDLE
+                        && !mHandler.hasMessages(START_FADE_OUT)) {
+                    startFadeTimer();
+                }
+            } else {
+                // Ensure fully opaque
+                mHandler.removeMessages(START_FADE_OUT);
+                fade(true);
+            }
+        }
+    }
+
+    /**
+     * Returns true if view fading is enabled.
+     */
+    public boolean isFadingEnabled() {
+        return mFadingEnabled;
+    }
+
+    /**
+     * Sets the listener to be called when fade in or out has completed.
+     */
+    public void setFadeCompleteListener(OnFadeCompleteListener listener) {
+        mFadeCompleteListener = listener;
+    }
+
+    /**
+     * Returns the listener to be called when fade in or out has completed.
+     */
+    public OnFadeCompleteListener getFadeCompleteListener() {
+        return mFadeCompleteListener;
+    }
+
+    /**
+     * Sets the input event handler.
+     */
+    public final void setOnKeyInterceptListener(View.OnKeyListener handler) {
+        mInputEventHandler = handler;
+    }
+
+    /**
+     * Returns the input event handler.
+     */
+    public final View.OnKeyListener getEventHandler() {
+        return mInputEventHandler;
+    }
+
+    /**
+     * Tickles the playback controls.  Fades in the view if it was faded out,
+     * otherwise resets the fade out timer.  Tickling on input events is handled
+     * by the fragment.
+     */
+    public void tickle() {
+        if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed());
+        if (!mFadingEnabled || !isResumed()) {
+            return;
+        }
+        if (mHandler.hasMessages(START_FADE_OUT)) {
+            // Restart the timer
+            startFadeTimer();
+        } else {
+            fade(true);
+        }
+    }
+
+    /**
+     * Fades out the playback overlay immediately.
+     */
+    public void fadeOut() {
+        mHandler.removeMessages(START_FADE_OUT);
+        fade(false);
+    }
+
+    /**
+     * Returns true/false indicating whether playback controls are visible or not.
+     */
+    private boolean areControlsHidden() {
+        return mFadingStatus == IDLE && mBgAlpha == 0;
+    }
+
+    private boolean onInterceptInputEvent(InputEvent event) {
+        final boolean controlsHidden = areControlsHidden();
+        if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
+        boolean consumeEvent = false;
+        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+
+        if (event instanceof KeyEvent) {
+            keyCode = ((KeyEvent) event).getKeyCode();
+            if (mInputEventHandler != null) {
+                consumeEvent = mInputEventHandler.onKey(getView(), keyCode, (KeyEvent) event);
+            }
+        }
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // Event may be consumed; regardless, if controls are hidden then these keys will
+                // bring up the controls.
+                if (controlsHidden) {
+                    consumeEvent = true;
+                }
+                tickle();
+                break;
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                // If fading enabled and controls are not hidden, back will be consumed to fade
+                // them out (even if the key was consumed by the handler).
+                if (mFadingEnabled && !controlsHidden) {
+                    consumeEvent = true;
+                    mHandler.removeMessages(START_FADE_OUT);
+                    fade(false);
+                } else if (consumeEvent) {
+                    tickle();
+                }
+                break;
+            default:
+                if (consumeEvent) {
+                    tickle();
+                }
+        }
+        return consumeEvent;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mFadingEnabled) {
+            setBgAlpha(0);
+            fade(true);
+        }
+        getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
+        getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+    }
+
+    private void startFadeTimer() {
+        if (mHandler != null) {
+            mHandler.removeMessages(START_FADE_OUT);
+            mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs);
+        }
+    }
+
+    private static ValueAnimator loadAnimator(Context context, int resId) {
+        ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId);
+        animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER);
+        return animator;
+    }
+
+    private void loadBgAnimator() {
+        AnimatorUpdateListener listener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                setBgAlpha((Integer) arg0.getAnimatedValue());
+            }
+        };
+
+        mBgFadeInAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_in);
+        mBgFadeInAnimator.addUpdateListener(listener);
+        mBgFadeInAnimator.addListener(mFadeListener);
+
+        mBgFadeOutAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_out);
+        mBgFadeOutAnimator.addUpdateListener(listener);
+        mBgFadeOutAnimator.addListener(mFadeListener);
+    }
+
+    private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
+    private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
+
+    private View getControlRowView() {
+        if (getVerticalGridView() == null) {
+            return null;
+        }
+        RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0);
+        if (vh == null) {
+            return null;
+        }
+        return vh.itemView;
+    }
+
+    private void loadControlRowAnimator() {
+        final AnimatorListener listener = new AnimatorListener() {
+            @Override
+            void getViews(ArrayList<View> views) {
+                View view = getControlRowView();
+                if (view != null) {
+                    views.add(view);
+                }
+            }
+        };
+        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                View view = getControlRowView();
+                if (view != null) {
+                    final float fraction = (Float) arg0.getAnimatedValue();
+                    if (DEBUG) Log.v(TAG, "fraction " + fraction);
+                    view.setAlpha(fraction);
+                    view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
+                }
+            }
+        };
+
+        mControlRowFadeInAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_in);
+        mControlRowFadeInAnimator.addUpdateListener(updateListener);
+        mControlRowFadeInAnimator.addListener(listener);
+        mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
+
+        mControlRowFadeOutAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_out);
+        mControlRowFadeOutAnimator.addUpdateListener(updateListener);
+        mControlRowFadeOutAnimator.addListener(listener);
+        mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
+    }
+
+    private void loadOtherRowAnimator() {
+        final AnimatorListener listener = new AnimatorListener() {
+            @Override
+            void getViews(ArrayList<View> views) {
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                final int count = getVerticalGridView().getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View view = getVerticalGridView().getChildAt(i);
+                    if (view != null) {
+                        views.add(view);
+                    }
+                }
+            }
+        };
+        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                final float fraction = (Float) arg0.getAnimatedValue();
+                for (View view : listener.mViews) {
+                    if (getVerticalGridView().getChildPosition(view) > 0) {
+                        view.setAlpha(fraction);
+                        view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
+                    }
+                }
+            }
+        };
+
+        mOtherRowFadeInAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_in);
+        mOtherRowFadeInAnimator.addListener(listener);
+        mOtherRowFadeInAnimator.addUpdateListener(updateListener);
+        mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
+
+        mOtherRowFadeOutAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_out);
+        mOtherRowFadeOutAnimator.addListener(listener);
+        mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
+        mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
+    }
+
+    private void fade(boolean fadeIn) {
+        if (DEBUG) Log.v(TAG, "fade " + fadeIn);
+        if (getView() == null) {
+            return;
+        }
+        if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) {
+            if (DEBUG) Log.v(TAG, "requested fade in progress");
+            return;
+        }
+        if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) {
+            if (DEBUG) Log.v(TAG, "fade is no-op");
+            return;
+        }
+
+        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+
+        if (mFadingStatus == IDLE) {
+            if (fadeIn) {
+                mBgFadeInAnimator.start();
+                mControlRowFadeInAnimator.start();
+                mOtherRowFadeInAnimator.start();
+            } else {
+                mBgFadeOutAnimator.start();
+                mControlRowFadeOutAnimator.start();
+                mOtherRowFadeOutAnimator.start();
+            }
+        } else {
+            if (fadeIn) {
+                mBgFadeOutAnimator.reverse();
+                mControlRowFadeOutAnimator.reverse();
+                mOtherRowFadeOutAnimator.reverse();
+            } else {
+                mBgFadeInAnimator.reverse();
+                mControlRowFadeInAnimator.reverse();
+                mOtherRowFadeInAnimator.reverse();
+            }
+        }
+
+        // If fading in while control row is focused, set initial translationY so
+        // views slide in from below.
+        if (fadeIn && mFadingStatus == IDLE) {
+            final int count = getVerticalGridView().getChildCount();
+            for (int i = 0; i < count; i++) {
+                getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY);
+            }
+        }
+
+        mFadingStatus = fadeIn ? IN : OUT;
+    }
+
+    /**
+     * Sets the selected row position with smooth animation.
+     */
+    public void setSelectedPosition(int position) {
+        setSelectedPosition(position, true);
+    }
+
+    /**
+     * Sets the selected row position.
+     */
+    public void setSelectedPosition(int position, boolean smooth) {
+        mSetSelectionRunnable.mPosition = position;
+        mSetSelectionRunnable.mSmooth = smooth;
+        if (getView() != null && getView().getHandler() != null) {
+            getView().getHandler().post(mSetSelectionRunnable);
+        }
+    }
+
+    private void setupChildFragmentLayout() {
+        setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
+    }
+
+    void setVerticalGridViewLayout(VerticalGridView listview) {
+        if (listview == null) {
+            return;
+        }
+        // Padding affects alignment when last row is focused
+        // (last is first when there's only one row).
+        setPadding(listview, mPaddingTop, mPaddingBottom);
+
+        // Item alignment affects focused row that isn't the last.
+        listview.setItemAlignmentOffset(0);
+        listview.setItemAlignmentOffsetPercent(50);
+
+        // Push rows to the bottom.
+        listview.setWindowAlignmentOffset(0);
+        listview.setWindowAlignmentOffsetPercent(50);
+        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
+    }
+
+    private static void setPadding(View view, int paddingTop, int paddingBottom) {
+        view.setPadding(view.getPaddingLeft(), paddingTop,
+                view.getPaddingRight(), paddingBottom);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mPaddingTop =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_top);
+        mPaddingBottom =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom);
+        mBgDarkColor =
+                getResources().getColor(R.color.lb_playback_controls_background_dark);
+        mBgLightColor =
+                getResources().getColor(R.color.lb_playback_controls_background_light);
+        mShowTimeMs =
+                getResources().getInteger(R.integer.lb_playback_controls_show_time_ms);
+        mMajorFadeTranslateY =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y);
+        mMinorFadeTranslateY =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y);
+
+        loadBgAnimator();
+        loadControlRowAnimator();
+        loadOtherRowAnimator();
+    }
+
+    /**
+     * Sets the background type.
+     *
+     * @param type One of BG_LIGHT, BG_DARK, or BG_NONE.
+     */
+    public void setBackgroundType(int type) {
+        switch (type) {
+            case BG_LIGHT:
+            case BG_DARK:
+            case BG_NONE:
+                if (type != mBackgroundType) {
+                    mBackgroundType = type;
+                    updateBackground();
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid background type");
+        }
+    }
+
+    /**
+     * Returns the background type.
+     */
+    public int getBackgroundType() {
+        return mBackgroundType;
+    }
+
+    private void updateBackground() {
+        if (mRootView != null) {
+            int color = mBgDarkColor;
+            switch (mBackgroundType) {
+                case BG_DARK:
+                    break;
+                case BG_LIGHT:
+                    color = mBgLightColor;
+                    break;
+                case BG_NONE:
+                    color = Color.TRANSPARENT;
+                    break;
+            }
+            mRootView.setBackground(new ColorDrawable(color));
+        }
+    }
+
+    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
+            new ItemBridgeAdapter.AdapterListener() {
+                @Override
+                public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
+                    if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
+                    if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) {
+                        if (DEBUG) Log.v(TAG, "setting alpha to 0");
+                        vh.getViewHolder().view.setAlpha(0);
+                    }
+                }
+
+                @Override
+                public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
+                    if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view);
+                    // Reset animation state
+                    vh.getViewHolder().view.setAlpha(1f);
+                    vh.getViewHolder().view.setTranslationY(0);
+                    vh.getViewHolder().view.setAlpha(1f);
+                }
+
+                @Override
+                public void onBind(ItemBridgeAdapter.ViewHolder vh) {
+                }
+            };
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        mRootView = inflater.inflate(R.layout.lb_playback_fragment, container, false);
+        mRowsFragment = (RowsFragment) getChildFragmentManager().findFragmentById(
+                R.id.playback_controls_dock);
+        if (mRowsFragment == null) {
+            mRowsFragment = new RowsFragment();
+            getChildFragmentManager().beginTransaction()
+                    .replace(R.id.playback_controls_dock, mRowsFragment)
+                    .commit();
+        }
+        if (mAdapter == null) {
+            setAdapter(new ArrayObjectAdapter(new ClassPresenterSelector()));
+        } else {
+            mRowsFragment.setAdapter(mAdapter);
+        }
+        mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
+
+        mBgAlpha = 255;
+        updateBackground();
+        mRowsFragment.setExternalAdapterListener(mAdapterListener);
+        return mRootView;
+    }
+
+    /**
+     * Sets the {@link PlaybackGlue.HostLifecycleCallback}. Implementor of this interface will
+     * take appropriate actions to take action when the hosting fragment starts/stops processing.
+     */
+    public void setHostLifecycleCallback(PlaybackGlue.HostLifecycleCallback hostLifecycleCallback) {
+        this.mHostLifecycleCallback = hostLifecycleCallback;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        setupChildFragmentLayout();
+        mRowsFragment.setAdapter(mAdapter);
+        if (mHostLifecycleCallback != null) {
+            mHostLifecycleCallback.onHostStart();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mHostLifecycleCallback != null) {
+            mHostLifecycleCallback.onHostStop();
+        }
+    }
+
+    /**
+     * This listener is called every time there is a click in {@link RowsFragment}. This can
+     * be used by users to take additional actions such as animations.
+     */
+    public void setOnItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
+        mExternalItemClickedListener = listener;
+    }
+
+    /**
+     * Sets the {@link BaseOnItemViewClickedListener} that would be invoked for clicks
+     * only on {@link android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder}.
+     */
+    public void setOnPlaybackItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
+        mPlaybackItemClickedListener = listener;
+    }
+
+    @Override
+    public void onDestroyView() {
+        mRootView = null;
+        super.onDestroyView();
+    }
+
+    /**
+     * Sets the playback row for the playback controls.
+     */
+    public void setPlaybackRow(Row row) {
+        this.mRow = row;
+        setupRow();
+        setupPresenter();
+    }
+
+    /**
+     * Sets the presenter for rendering the playback controls.
+     */
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        this.mPresenter = presenter;
+        setupPresenter();
+    }
+
+    /**
+     * Updates the ui when the row data changes.
+     */
+    public void notifyPlaybackRowChanged() {
+        if (mAdapter == null) {
+            return;
+        }
+        mAdapter.notifyItemRangeChanged(0, 1);
+    }
+
+    /**
+     * Sets the list of rows for the fragment.
+     */
+    public void setAdapter(ObjectAdapter adapter) {
+        mAdapter = adapter;
+        setupRow();
+        setupPresenter();
+        if (mRowsFragment != null) {
+            mRowsFragment.setAdapter(adapter);
+        }
+    }
+
+    private void setupRow() {
+        if (mAdapter instanceof ArrayObjectAdapter && mRow != null) {
+            ArrayObjectAdapter adapter = ((ArrayObjectAdapter) mAdapter);
+            if (adapter.size() == 0) {
+                adapter.add(mRow);
+            } else {
+                adapter.replace(0, mRow);
+            }
+        }
+    }
+
+    private void setupPresenter() {
+        if (mAdapter != null && mRow != null && mPresenter != null) {
+            PresenterSelector selector = mAdapter.getPresenterSelector();
+            if (selector == null) {
+                selector = new ClassPresenterSelector();
+                mAdapter.setPresenterSelector(selector);
+            }
+
+            if (selector instanceof ClassPresenterSelector) {
+                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
+            }
+        }
+    }
+
+    static abstract class AnimatorListener implements Animator.AnimatorListener {
+        ArrayList<View> mViews = new ArrayList<View>();
+        ArrayList<Integer> mLayerType = new ArrayList<Integer>();
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            getViews(mViews);
+            for (View view : mViews) {
+                mLayerType.add(view.getLayerType());
+                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            for (int i = 0; i < mViews.size(); i++) {
+                mViews.get(i).setLayerType(mLayerType.get(i), null);
+            }
+            mLayerType.clear();
+            mViews.clear();
+        }
+
+        abstract void getViews(ArrayList<View> views);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
new file mode 100644
index 0000000..fc384a5
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.View;
+
+/**
+ * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * the interaction between this class and {@link PlaybackFragment}.
+ */
+public class PlaybackFragmentGlueHost extends PlaybackGlue.PlaybackGlueHost {
+    private final PlaybackFragment mFragment;
+
+    public PlaybackFragmentGlueHost(PlaybackFragment fragment) {
+        this.mFragment = fragment;
+    }
+
+    @Override
+    public void setFadingEnabled(boolean enable) {
+        mFragment.setFadingEnabled(enable);
+    }
+
+    @Override
+    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
+        mFragment.setOnKeyInterceptListener(onKeyListener);
+    }
+
+    @Override
+    public void setOnActionClickedListener(final OnActionClickedListener listener) {
+        mFragment.setOnPlaybackItemViewClickedListener(new OnItemViewClickedListener() {
+            @Override
+            public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
+                if (item instanceof Action) {
+                    listener.onActionClicked((Action)item);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setHostLifeCycleCallback(PlaybackGlue.HostLifecycleCallback callback) {
+        mFragment.setHostLifecycleCallback(callback);
+    }
+
+    @Override
+    public void notifyPlaybackRowChanged() {
+        mFragment.notifyPlaybackRowChanged();
+    }
+
+    @Override
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        mFragment.setPlaybackRowPresenter(presenter);
+    }
+
+    @Override
+    public void setPlaybackRow(Row row) {
+        mFragment.setPlaybackRow(row);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java
new file mode 100644
index 0000000..867aa33
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.content.Context;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Row;
+import android.view.View;
+
+/**
+ * Base class for {@link PlaybackControlGlue}.
+ */
+public abstract class PlaybackGlue {
+    private final Context mContext;
+    private PlaybackGlueHost mPlaybackGlueHost;
+
+    /**
+     * Returns true when the media player is ready to start media playback. Subclasses must
+     * implement this method correctly.
+     */
+    public boolean isReadyForPlayback() { return true; }
+
+    /**
+     * Interface to allow clients to take action once the video is ready to play.
+     */
+    public static abstract class PlayerCallback {
+        /**
+         * This method is fired when the video is ready for playback.
+         */
+        public abstract void onReadyForPlayback();
+    }
+
+    /**
+     * Lifecycle callbacks triggered by the host(fragment e.g.) hosting the video controls/surface.
+     */
+    public static abstract class HostLifecycleCallback {
+        /**
+         * Callback triggered once the host(fragment) has started.
+         */
+        public abstract void onHostStart();
+
+        /**
+         * Callback triggered once the host(fragment) has finished.
+         */
+        public abstract void onHostStop();
+    }
+
+    /**
+     * Sets the {@link PlayerCallback} callback.
+     */
+    public void setPlayerCallback(MediaPlayerGlue.PlayerCallback mPlayerCallback) {}
+
+    /**
+     * Starts the media player.
+     */
+    public void play() {}
+
+    /**
+     * Pauses the media player.
+     */
+    public void pause() {}
+
+    /**
+     * Goes to the next media item.
+     */
+    public void next() {}
+
+    /**
+     * Goes to the previous media item.
+     */
+    public void previous() {}
+
+    /**
+     * This class represents the UI {@link PlaybackFragment} hosting playback controls and
+     * defines the interaction between {@link PlaybackGlue} and the host.
+     */
+    public static class PlaybackGlueHost {
+
+        /**
+         * Enables or disables view fading.  If enabled, the view will be faded in when the
+         * fragment starts and will fade out after a time period.
+         */
+        public void setFadingEnabled(boolean enable) {
+        }
+
+        /**
+         * Sets the {@link android.view.View.OnKeyListener} on the host. This would trigger
+         * the listener when a {@link android.view.KeyEvent} is unhandled by the host.
+         */
+        public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
+        }
+
+        /**
+         * Sets the {@link android.view.View.OnClickListener} on this fragment.
+         */
+        public void setOnActionClickedListener(OnActionClickedListener listener) {}
+
+        /**
+         * Sets the host {@link HostLifecycleCallback} callback on the host.
+         */
+        public void setHostLifeCycleCallback(HostLifecycleCallback callback) {
+        }
+
+        /**
+         * Notifies host about a change so it can update the view.
+         */
+        public void notifyPlaybackRowChanged() {}
+
+        /**
+         * Sets {@link PlaybackRowPresenter} for rendering the playback controls.
+         */
+        public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {}
+
+        /**
+         * Sets the {@link Row} that represents the information on control items that needs
+         * to be rendered.
+         */
+        public void setPlaybackRow(Row row) {}
+    }
+
+    /**
+     * Constructor.
+     */
+    public PlaybackGlue(Context context) {
+        this.mContext = context;
+    }
+
+    /**
+     * Returns the context.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * This method is used to configure the {@link PlaybackGlueHost} with required listeners
+     * and presenters.
+     */
+    public void setHost(PlaybackGlueHost host) {
+        mPlaybackGlueHost = host;
+    }
+
+    /**
+     * @return Associated {@link PlaybackGlueHost} or null if not attached to host.
+     */
+    public PlaybackGlueHost getHost() {
+        return mPlaybackGlueHost;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index 58eb2cb..c2566d6 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -13,22 +13,17 @@
  */
 package android.support.v17.leanback.app;
 
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
-import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.view.InputEvent;
-import android.view.animation.AccelerateInterpolator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.support.v7.widget.RecyclerView;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.animation.LogAccelerateInterpolator;
 import android.support.v17.leanback.animation.LogDecelerateInterpolator;
@@ -40,12 +35,15 @@
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
+import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
 
 import java.util.ArrayList;
 
@@ -60,7 +58,12 @@
  * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be
  * at position 0 in the adapter.
  * </p>
+ * <p>
+ *  This class is now deprecated, please us
+ * </p>
+ * @deprecated Use {@link PlaybackFragment}.
  */
+@Deprecated
 public class PlaybackOverlayFragment extends DetailsFragment {
 
     /**
@@ -89,19 +92,6 @@
         }
     }
 
-    /**
-     * Interface allowing the application to handle input events.
-     */
-    public interface InputEventHandler {
-        /**
-         * Called when an {@link InputEvent} is received.
-         *
-         * @return If the event should be consumed, return true. To allow the event to
-         * continue on to the next handler, return false.
-         */
-        public boolean handleInputEvent(InputEvent event);
-    }
-
     static final String TAG = "PlaybackOverlayFragment";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
@@ -123,7 +113,7 @@
     private int mMajorFadeTranslateY, mMinorFadeTranslateY;
     int mAnimationTranslateY;
     OnFadeCompleteListener mFadeCompleteListener;
-    private InputEventHandler mInputEventHandler;
+    private PlaybackControlGlue.InputEventHandler mInputEventHandler;
     boolean mFadingEnabled = true;
     int mFadingStatus = IDLE;
     int mBgAlpha;
@@ -266,9 +256,14 @@
         return mFadeCompleteListener;
     }
 
+    @Deprecated
+    public interface InputEventHandler extends PlaybackControlGlue.InputEventHandler {
+    }
+
     /**
      * Sets the input event handler.
      */
+    @Deprecated
     public final void setInputEventHandler(InputEventHandler handler) {
         mInputEventHandler = handler;
     }
@@ -276,7 +271,22 @@
     /**
      * Returns the input event handler.
      */
+    @Deprecated
     public final InputEventHandler getInputEventHandler() {
+        return (InputEventHandler)mInputEventHandler;
+    }
+
+    /**
+     * Sets the input event handler.
+     */
+    public final void setEventHandler(PlaybackControlGlue.InputEventHandler handler) {
+        mInputEventHandler = handler;
+    }
+
+    /**
+     * Returns the input event handler.
+     */
+    public final PlaybackControlGlue.InputEventHandler getEventHandler() {
         return mInputEventHandler;
     }
 
@@ -536,8 +546,8 @@
             return;
         }
 
-        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0 ?
-                mMajorFadeTranslateY : mMinorFadeTranslateY;
+        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
 
         if (mFadingStatus == IDLE) {
             if (fadeIn) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index 2c103a7..5603c09 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from PlaybackOverlayFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,22 +16,17 @@
  */
 package android.support.v17.leanback.app;
 
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
-import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.view.InputEvent;
-import android.view.animation.AccelerateInterpolator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.support.v7.widget.RecyclerView;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.animation.LogAccelerateInterpolator;
 import android.support.v17.leanback.animation.LogDecelerateInterpolator;
@@ -42,12 +38,15 @@
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
+import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
 
 import java.util.ArrayList;
 
@@ -62,7 +61,12 @@
  * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be
  * at position 0 in the adapter.
  * </p>
+ * <p>
+ *  This class is now deprecated, please us
+ * </p>
+ * @deprecated Use {@link PlaybackSupportFragment}.
  */
+@Deprecated
 public class PlaybackOverlaySupportFragment extends DetailsSupportFragment {
 
     /**
@@ -91,19 +95,6 @@
         }
     }
 
-    /**
-     * Interface allowing the application to handle input events.
-     */
-    public interface InputEventHandler {
-        /**
-         * Called when an {@link InputEvent} is received.
-         *
-         * @return If the event should be consumed, return true. To allow the event to
-         * continue on to the next handler, return false.
-         */
-        public boolean handleInputEvent(InputEvent event);
-    }
-
     static final String TAG = "PlaybackOverlaySupportFragment";
     static final boolean DEBUG = false;
     private static final int ANIMATION_MULTIPLIER = 1;
@@ -125,7 +116,7 @@
     private int mMajorFadeTranslateY, mMinorFadeTranslateY;
     int mAnimationTranslateY;
     OnFadeCompleteListener mFadeCompleteListener;
-    private InputEventHandler mInputEventHandler;
+    private PlaybackControlGlue.InputEventHandler mInputEventHandler;
     boolean mFadingEnabled = true;
     int mFadingStatus = IDLE;
     int mBgAlpha;
@@ -268,9 +259,14 @@
         return mFadeCompleteListener;
     }
 
+    @Deprecated
+    public interface InputEventHandler extends PlaybackControlGlue.InputEventHandler {
+    }
+
     /**
      * Sets the input event handler.
      */
+    @Deprecated
     public final void setInputEventHandler(InputEventHandler handler) {
         mInputEventHandler = handler;
     }
@@ -278,7 +274,22 @@
     /**
      * Returns the input event handler.
      */
+    @Deprecated
     public final InputEventHandler getInputEventHandler() {
+        return (InputEventHandler)mInputEventHandler;
+    }
+
+    /**
+     * Sets the input event handler.
+     */
+    public final void setEventHandler(PlaybackControlGlue.InputEventHandler handler) {
+        mInputEventHandler = handler;
+    }
+
+    /**
+     * Returns the input event handler.
+     */
+    public final PlaybackControlGlue.InputEventHandler getEventHandler() {
         return mInputEventHandler;
     }
 
@@ -538,8 +549,8 @@
             return;
         }
 
-        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0 ?
-                mMajorFadeTranslateY : mMinorFadeTranslateY;
+        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
 
         if (mFadingStatus == IDLE) {
             if (fadeIn) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
new file mode 100644
index 0000000..f01313d
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -0,0 +1,900 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from PlaybackFragment.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.support.v4.app.Fragment;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.animation.LogAccelerateInterpolator;
+import android.support.v17.leanback.animation.LogDecelerateInterpolator;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.ItemBridgeAdapter;
+import android.support.v17.leanback.widget.ObjectAdapter;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * A fragment for displaying playback controls and related content.
+ *
+ * <p>
+ * A PlaybackSupportFragment renders the elements of its {@link ObjectAdapter} as a set
+ * of rows in a vertical list.  The Adapter's {@link PresenterSelector} must maintain subclasses
+ * of {@link RowPresenter}.
+ * </p>
+ * <p>
+ * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be
+ * at position 0 in the adapter.
+ * </p>
+ */
+public class PlaybackSupportFragment extends Fragment {
+    /**
+     * No background.
+     */
+    public static final int BG_NONE = 0;
+
+    /**
+     * A dark translucent background.
+     */
+    public static final int BG_DARK = 1;
+    private PlaybackGlue.HostLifecycleCallback mHostLifecycleCallback;
+
+    /**
+     * Resets the focus on the button in the middle of control row.
+     */
+    public void resetFocus() {
+        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
+                .findViewHolderForAdapterPosition(0);
+        if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
+            ((PlaybackRowPresenter) vh.getPresenter()).onReappear(
+                    (RowPresenter.ViewHolder) vh.getViewHolder());
+        }
+    }
+
+    private class SetSelectionRunnable implements Runnable {
+        int mPosition;
+        boolean mSmooth = true;
+
+        @Override
+        public void run() {
+            if (mRowsSupportFragment == null) {
+                return;
+            }
+            mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
+        }
+    }
+
+    /**
+     * A light translucent background.
+     */
+    public static final int BG_LIGHT = 2;
+    private RowsSupportFragment mRowsSupportFragment;
+    private ObjectAdapter mAdapter;
+    private PlaybackRowPresenter mPresenter;
+    private Row mRow;
+    private BaseOnItemViewClickedListener mExternalItemClickedListener;
+    private BaseOnItemViewClickedListener mPlaybackItemClickedListener;
+    private BaseOnItemViewClickedListener mOnItemViewClickedListener = new BaseOnItemViewClickedListener() {
+        @Override
+        public void onItemClicked(Presenter.ViewHolder itemViewHolder,
+                                  Object item,
+                                  RowPresenter.ViewHolder rowViewHolder,
+                                  Object row) {
+            if (mPlaybackItemClickedListener != null
+                    && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder) {
+                mPlaybackItemClickedListener.onItemClicked(
+                        itemViewHolder, item, rowViewHolder, row);
+            }
+            if (mExternalItemClickedListener != null) {
+                mExternalItemClickedListener.onItemClicked(
+                        itemViewHolder, item, rowViewHolder, row);
+            }
+        }
+    };
+    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
+
+    public ObjectAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    /**
+     * Listener allowing the application to receive notification of fade in and/or fade out
+     * completion events.
+     */
+    public static class OnFadeCompleteListener {
+        public void onFadeInComplete() {
+        }
+
+        public void onFadeOutComplete() {
+        }
+    }
+
+    private static final String TAG = "PlaybackSupportFragment";
+    private static final boolean DEBUG = false;
+    private static final int ANIMATION_MULTIPLIER = 1;
+
+    private static int START_FADE_OUT = 1;
+
+    // Fading status
+    private static final int IDLE = 0;
+    private static final int IN = 1;
+    private static final int OUT = 2;
+
+    private int mPaddingTop;
+    private int mPaddingBottom;
+    private View mRootView;
+    private int mBackgroundType = BG_DARK;
+    private int mBgDarkColor;
+    private int mBgLightColor;
+    private int mShowTimeMs;
+    private int mMajorFadeTranslateY, mMinorFadeTranslateY;
+    private int mAnimationTranslateY;
+    private OnFadeCompleteListener mFadeCompleteListener;
+    private View.OnKeyListener mInputEventHandler;
+    private boolean mFadingEnabled = true;
+    private int mFadingStatus = IDLE;
+    private int mBgAlpha;
+    private ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
+    private ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
+    private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
+
+    private final Animator.AnimatorListener mFadeListener =
+            new Animator.AnimatorListener() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    enableVerticalGridAnimations(false);
+                }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
+                    if (mBgAlpha > 0) {
+                        enableVerticalGridAnimations(true);
+                        startFadeTimer();
+                        if (mFadeCompleteListener != null) {
+                            mFadeCompleteListener.onFadeInComplete();
+                        }
+                    } else {
+                        VerticalGridView verticalView = getVerticalGridView();
+                        // reset focus to the primary actions only if the selected row was the controls row
+                        if (verticalView != null && verticalView.getSelectedPosition() == 0) {
+                            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                                    verticalView.findViewHolderForAdapterPosition(0);
+                            if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
+                                ((PlaybackRowPresenter)vh.getPresenter()).onReappear(
+                                        (RowPresenter.ViewHolder) vh.getViewHolder());
+                            }
+                        }
+                        if (mFadeCompleteListener != null) {
+                            mFadeCompleteListener.onFadeOutComplete();
+                        }
+                    }
+                    mFadingStatus = IDLE;
+                }
+            };
+
+    VerticalGridView getVerticalGridView() {
+        if (mRowsSupportFragment == null) {
+            return null;
+        }
+        return mRowsSupportFragment.getVerticalGridView();
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message message) {
+            if (message.what == START_FADE_OUT && mFadingEnabled) {
+                fade(false);
+            }
+        }
+    };
+
+    private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener =
+            new VerticalGridView.OnTouchInterceptListener() {
+                @Override
+                public boolean onInterceptTouchEvent(MotionEvent event) {
+                    return onInterceptInputEvent(event);
+                }
+            };
+
+    private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener =
+            new VerticalGridView.OnKeyInterceptListener() {
+                @Override
+                public boolean onInterceptKeyEvent(KeyEvent event) {
+                    return onInterceptInputEvent(event);
+                }
+            };
+
+    private void setBgAlpha(int alpha) {
+        mBgAlpha = alpha;
+        if (mRootView != null) {
+            mRootView.getBackground().setAlpha(alpha);
+        }
+    }
+
+    private void enableVerticalGridAnimations(boolean enable) {
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setAnimateChildLayout(enable);
+        }
+    }
+
+    /**
+     * Enables or disables view fading.  If enabled,
+     * the view will be faded in when the fragment starts,
+     * and will fade out after a time period.  The timeout
+     * period is reset each time {@link #tickle} is called.
+     */
+    public void setFadingEnabled(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled);
+        if (enabled != mFadingEnabled) {
+            mFadingEnabled = enabled;
+            if (mFadingEnabled) {
+                if (isResumed() && mFadingStatus == IDLE
+                        && !mHandler.hasMessages(START_FADE_OUT)) {
+                    startFadeTimer();
+                }
+            } else {
+                // Ensure fully opaque
+                mHandler.removeMessages(START_FADE_OUT);
+                fade(true);
+            }
+        }
+    }
+
+    /**
+     * Returns true if view fading is enabled.
+     */
+    public boolean isFadingEnabled() {
+        return mFadingEnabled;
+    }
+
+    /**
+     * Sets the listener to be called when fade in or out has completed.
+     */
+    public void setFadeCompleteListener(OnFadeCompleteListener listener) {
+        mFadeCompleteListener = listener;
+    }
+
+    /**
+     * Returns the listener to be called when fade in or out has completed.
+     */
+    public OnFadeCompleteListener getFadeCompleteListener() {
+        return mFadeCompleteListener;
+    }
+
+    /**
+     * Sets the input event handler.
+     */
+    public final void setOnKeyInterceptListener(View.OnKeyListener handler) {
+        mInputEventHandler = handler;
+    }
+
+    /**
+     * Returns the input event handler.
+     */
+    public final View.OnKeyListener getEventHandler() {
+        return mInputEventHandler;
+    }
+
+    /**
+     * Tickles the playback controls.  Fades in the view if it was faded out,
+     * otherwise resets the fade out timer.  Tickling on input events is handled
+     * by the fragment.
+     */
+    public void tickle() {
+        if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed());
+        if (!mFadingEnabled || !isResumed()) {
+            return;
+        }
+        if (mHandler.hasMessages(START_FADE_OUT)) {
+            // Restart the timer
+            startFadeTimer();
+        } else {
+            fade(true);
+        }
+    }
+
+    /**
+     * Fades out the playback overlay immediately.
+     */
+    public void fadeOut() {
+        mHandler.removeMessages(START_FADE_OUT);
+        fade(false);
+    }
+
+    /**
+     * Returns true/false indicating whether playback controls are visible or not.
+     */
+    private boolean areControlsHidden() {
+        return mFadingStatus == IDLE && mBgAlpha == 0;
+    }
+
+    private boolean onInterceptInputEvent(InputEvent event) {
+        final boolean controlsHidden = areControlsHidden();
+        if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event);
+        boolean consumeEvent = false;
+        int keyCode = KeyEvent.KEYCODE_UNKNOWN;
+
+        if (event instanceof KeyEvent) {
+            keyCode = ((KeyEvent) event).getKeyCode();
+            if (mInputEventHandler != null) {
+                consumeEvent = mInputEventHandler.onKey(getView(), keyCode, (KeyEvent) event);
+            }
+        }
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // Event may be consumed; regardless, if controls are hidden then these keys will
+                // bring up the controls.
+                if (controlsHidden) {
+                    consumeEvent = true;
+                }
+                tickle();
+                break;
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                // If fading enabled and controls are not hidden, back will be consumed to fade
+                // them out (even if the key was consumed by the handler).
+                if (mFadingEnabled && !controlsHidden) {
+                    consumeEvent = true;
+                    mHandler.removeMessages(START_FADE_OUT);
+                    fade(false);
+                } else if (consumeEvent) {
+                    tickle();
+                }
+                break;
+            default:
+                if (consumeEvent) {
+                    tickle();
+                }
+        }
+        return consumeEvent;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mFadingEnabled) {
+            setBgAlpha(0);
+            fade(true);
+        }
+        getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
+        getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+    }
+
+    private void startFadeTimer() {
+        if (mHandler != null) {
+            mHandler.removeMessages(START_FADE_OUT);
+            mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs);
+        }
+    }
+
+    private static ValueAnimator loadAnimator(Context context, int resId) {
+        ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId);
+        animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER);
+        return animator;
+    }
+
+    private void loadBgAnimator() {
+        AnimatorUpdateListener listener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                setBgAlpha((Integer) arg0.getAnimatedValue());
+            }
+        };
+
+        mBgFadeInAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_in);
+        mBgFadeInAnimator.addUpdateListener(listener);
+        mBgFadeInAnimator.addListener(mFadeListener);
+
+        mBgFadeOutAnimator = loadAnimator(getActivity(), R.animator.lb_playback_bg_fade_out);
+        mBgFadeOutAnimator.addUpdateListener(listener);
+        mBgFadeOutAnimator.addListener(mFadeListener);
+    }
+
+    private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
+    private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
+
+    private View getControlRowView() {
+        if (getVerticalGridView() == null) {
+            return null;
+        }
+        RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0);
+        if (vh == null) {
+            return null;
+        }
+        return vh.itemView;
+    }
+
+    private void loadControlRowAnimator() {
+        final AnimatorListener listener = new AnimatorListener() {
+            @Override
+            void getViews(ArrayList<View> views) {
+                View view = getControlRowView();
+                if (view != null) {
+                    views.add(view);
+                }
+            }
+        };
+        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                View view = getControlRowView();
+                if (view != null) {
+                    final float fraction = (Float) arg0.getAnimatedValue();
+                    if (DEBUG) Log.v(TAG, "fraction " + fraction);
+                    view.setAlpha(fraction);
+                    view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
+                }
+            }
+        };
+
+        mControlRowFadeInAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_in);
+        mControlRowFadeInAnimator.addUpdateListener(updateListener);
+        mControlRowFadeInAnimator.addListener(listener);
+        mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
+
+        mControlRowFadeOutAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_out);
+        mControlRowFadeOutAnimator.addUpdateListener(updateListener);
+        mControlRowFadeOutAnimator.addListener(listener);
+        mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
+    }
+
+    private void loadOtherRowAnimator() {
+        final AnimatorListener listener = new AnimatorListener() {
+            @Override
+            void getViews(ArrayList<View> views) {
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                final int count = getVerticalGridView().getChildCount();
+                for (int i = 0; i < count; i++) {
+                    View view = getVerticalGridView().getChildAt(i);
+                    if (view != null) {
+                        views.add(view);
+                    }
+                }
+            }
+        };
+        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                if (getVerticalGridView() == null) {
+                    return;
+                }
+                final float fraction = (Float) arg0.getAnimatedValue();
+                for (View view : listener.mViews) {
+                    if (getVerticalGridView().getChildPosition(view) > 0) {
+                        view.setAlpha(fraction);
+                        view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
+                    }
+                }
+            }
+        };
+
+        mOtherRowFadeInAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_in);
+        mOtherRowFadeInAnimator.addListener(listener);
+        mOtherRowFadeInAnimator.addUpdateListener(updateListener);
+        mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
+
+        mOtherRowFadeOutAnimator = loadAnimator(
+                getActivity(), R.animator.lb_playback_controls_fade_out);
+        mOtherRowFadeOutAnimator.addListener(listener);
+        mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
+        mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
+    }
+
+    private void fade(boolean fadeIn) {
+        if (DEBUG) Log.v(TAG, "fade " + fadeIn);
+        if (getView() == null) {
+            return;
+        }
+        if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) {
+            if (DEBUG) Log.v(TAG, "requested fade in progress");
+            return;
+        }
+        if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) {
+            if (DEBUG) Log.v(TAG, "fade is no-op");
+            return;
+        }
+
+        mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0
+                ? mMajorFadeTranslateY : mMinorFadeTranslateY;
+
+        if (mFadingStatus == IDLE) {
+            if (fadeIn) {
+                mBgFadeInAnimator.start();
+                mControlRowFadeInAnimator.start();
+                mOtherRowFadeInAnimator.start();
+            } else {
+                mBgFadeOutAnimator.start();
+                mControlRowFadeOutAnimator.start();
+                mOtherRowFadeOutAnimator.start();
+            }
+        } else {
+            if (fadeIn) {
+                mBgFadeOutAnimator.reverse();
+                mControlRowFadeOutAnimator.reverse();
+                mOtherRowFadeOutAnimator.reverse();
+            } else {
+                mBgFadeInAnimator.reverse();
+                mControlRowFadeInAnimator.reverse();
+                mOtherRowFadeInAnimator.reverse();
+            }
+        }
+
+        // If fading in while control row is focused, set initial translationY so
+        // views slide in from below.
+        if (fadeIn && mFadingStatus == IDLE) {
+            final int count = getVerticalGridView().getChildCount();
+            for (int i = 0; i < count; i++) {
+                getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY);
+            }
+        }
+
+        mFadingStatus = fadeIn ? IN : OUT;
+    }
+
+    /**
+     * Sets the selected row position with smooth animation.
+     */
+    public void setSelectedPosition(int position) {
+        setSelectedPosition(position, true);
+    }
+
+    /**
+     * Sets the selected row position.
+     */
+    public void setSelectedPosition(int position, boolean smooth) {
+        mSetSelectionRunnable.mPosition = position;
+        mSetSelectionRunnable.mSmooth = smooth;
+        if (getView() != null && getView().getHandler() != null) {
+            getView().getHandler().post(mSetSelectionRunnable);
+        }
+    }
+
+    private void setupChildFragmentLayout() {
+        setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
+    }
+
+    void setVerticalGridViewLayout(VerticalGridView listview) {
+        if (listview == null) {
+            return;
+        }
+        // Padding affects alignment when last row is focused
+        // (last is first when there's only one row).
+        setPadding(listview, mPaddingTop, mPaddingBottom);
+
+        // Item alignment affects focused row that isn't the last.
+        listview.setItemAlignmentOffset(0);
+        listview.setItemAlignmentOffsetPercent(50);
+
+        // Push rows to the bottom.
+        listview.setWindowAlignmentOffset(0);
+        listview.setWindowAlignmentOffsetPercent(50);
+        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
+    }
+
+    private static void setPadding(View view, int paddingTop, int paddingBottom) {
+        view.setPadding(view.getPaddingLeft(), paddingTop,
+                view.getPaddingRight(), paddingBottom);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mPaddingTop =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_top);
+        mPaddingBottom =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom);
+        mBgDarkColor =
+                getResources().getColor(R.color.lb_playback_controls_background_dark);
+        mBgLightColor =
+                getResources().getColor(R.color.lb_playback_controls_background_light);
+        mShowTimeMs =
+                getResources().getInteger(R.integer.lb_playback_controls_show_time_ms);
+        mMajorFadeTranslateY =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y);
+        mMinorFadeTranslateY =
+                getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y);
+
+        loadBgAnimator();
+        loadControlRowAnimator();
+        loadOtherRowAnimator();
+    }
+
+    /**
+     * Sets the background type.
+     *
+     * @param type One of BG_LIGHT, BG_DARK, or BG_NONE.
+     */
+    public void setBackgroundType(int type) {
+        switch (type) {
+            case BG_LIGHT:
+            case BG_DARK:
+            case BG_NONE:
+                if (type != mBackgroundType) {
+                    mBackgroundType = type;
+                    updateBackground();
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid background type");
+        }
+    }
+
+    /**
+     * Returns the background type.
+     */
+    public int getBackgroundType() {
+        return mBackgroundType;
+    }
+
+    private void updateBackground() {
+        if (mRootView != null) {
+            int color = mBgDarkColor;
+            switch (mBackgroundType) {
+                case BG_DARK:
+                    break;
+                case BG_LIGHT:
+                    color = mBgLightColor;
+                    break;
+                case BG_NONE:
+                    color = Color.TRANSPARENT;
+                    break;
+            }
+            mRootView.setBackground(new ColorDrawable(color));
+        }
+    }
+
+    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
+            new ItemBridgeAdapter.AdapterListener() {
+                @Override
+                public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
+                    if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
+                    if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) {
+                        if (DEBUG) Log.v(TAG, "setting alpha to 0");
+                        vh.getViewHolder().view.setAlpha(0);
+                    }
+                }
+
+                @Override
+                public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
+                    if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view);
+                    // Reset animation state
+                    vh.getViewHolder().view.setAlpha(1f);
+                    vh.getViewHolder().view.setTranslationY(0);
+                    vh.getViewHolder().view.setAlpha(1f);
+                }
+
+                @Override
+                public void onBind(ItemBridgeAdapter.ViewHolder vh) {
+                }
+            };
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        mRootView = inflater.inflate(R.layout.lb_playback_fragment, container, false);
+        mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
+                R.id.playback_controls_dock);
+        if (mRowsSupportFragment == null) {
+            mRowsSupportFragment = new RowsSupportFragment();
+            getChildFragmentManager().beginTransaction()
+                    .replace(R.id.playback_controls_dock, mRowsSupportFragment)
+                    .commit();
+        }
+        if (mAdapter == null) {
+            setAdapter(new ArrayObjectAdapter(new ClassPresenterSelector()));
+        } else {
+            mRowsSupportFragment.setAdapter(mAdapter);
+        }
+        mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
+
+        mBgAlpha = 255;
+        updateBackground();
+        mRowsSupportFragment.setExternalAdapterListener(mAdapterListener);
+        return mRootView;
+    }
+
+    /**
+     * Sets the {@link PlaybackGlue.HostLifecycleCallback}. Implementor of this interface will
+     * take appropriate actions to take action when the hosting fragment starts/stops processing.
+     */
+    public void setHostLifecycleCallback(PlaybackGlue.HostLifecycleCallback hostLifecycleCallback) {
+        this.mHostLifecycleCallback = hostLifecycleCallback;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        setupChildFragmentLayout();
+        mRowsSupportFragment.setAdapter(mAdapter);
+        if (mHostLifecycleCallback != null) {
+            mHostLifecycleCallback.onHostStart();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mHostLifecycleCallback != null) {
+            mHostLifecycleCallback.onHostStop();
+        }
+    }
+
+    /**
+     * This listener is called every time there is a click in {@link RowsSupportFragment}. This can
+     * be used by users to take additional actions such as animations.
+     */
+    public void setOnItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
+        mExternalItemClickedListener = listener;
+    }
+
+    /**
+     * Sets the {@link BaseOnItemViewClickedListener} that would be invoked for clicks
+     * only on {@link android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder}.
+     */
+    public void setOnPlaybackItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
+        mPlaybackItemClickedListener = listener;
+    }
+
+    @Override
+    public void onDestroyView() {
+        mRootView = null;
+        super.onDestroyView();
+    }
+
+    /**
+     * Sets the playback row for the playback controls.
+     */
+    public void setPlaybackRow(Row row) {
+        this.mRow = row;
+        setupRow();
+        setupPresenter();
+    }
+
+    /**
+     * Sets the presenter for rendering the playback controls.
+     */
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        this.mPresenter = presenter;
+        setupPresenter();
+    }
+
+    /**
+     * Updates the ui when the row data changes.
+     */
+    public void notifyPlaybackRowChanged() {
+        if (mAdapter == null) {
+            return;
+        }
+        mAdapter.notifyItemRangeChanged(0, 1);
+    }
+
+    /**
+     * Sets the list of rows for the fragment.
+     */
+    public void setAdapter(ObjectAdapter adapter) {
+        mAdapter = adapter;
+        setupRow();
+        setupPresenter();
+        if (mRowsSupportFragment != null) {
+            mRowsSupportFragment.setAdapter(adapter);
+        }
+    }
+
+    private void setupRow() {
+        if (mAdapter instanceof ArrayObjectAdapter && mRow != null) {
+            ArrayObjectAdapter adapter = ((ArrayObjectAdapter) mAdapter);
+            if (adapter.size() == 0) {
+                adapter.add(mRow);
+            } else {
+                adapter.replace(0, mRow);
+            }
+        }
+    }
+
+    private void setupPresenter() {
+        if (mAdapter != null && mRow != null && mPresenter != null) {
+            PresenterSelector selector = mAdapter.getPresenterSelector();
+            if (selector == null) {
+                selector = new ClassPresenterSelector();
+                mAdapter.setPresenterSelector(selector);
+            }
+
+            if (selector instanceof ClassPresenterSelector) {
+                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
+            }
+        }
+    }
+
+    static abstract class AnimatorListener implements Animator.AnimatorListener {
+        ArrayList<View> mViews = new ArrayList<View>();
+        ArrayList<Integer> mLayerType = new ArrayList<Integer>();
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            getViews(mViews);
+            for (View view : mViews) {
+                mLayerType.add(view.getLayerType());
+                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            for (int i = 0; i < mViews.size(); i++) {
+                mViews.get(i).setLayerType(mLayerType.get(i), null);
+            }
+            mLayerType.clear();
+            mViews.clear();
+        }
+
+        abstract void getViews(ArrayList<View> views);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
new file mode 100644
index 0000000..3227747
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
@@ -0,0 +1,81 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from VideoPlaybackFragmentGlueHost.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.view.View;
+
+/**
+ * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * the interaction between this class and {@link PlaybackSupportFragment}.
+ */
+public class PlaybackSupportFragmentGlueHost extends PlaybackGlue.PlaybackGlueHost {
+    private final PlaybackSupportFragment mFragment;
+
+    public PlaybackSupportFragmentGlueHost(PlaybackSupportFragment fragment) {
+        this.mFragment = fragment;
+    }
+
+    @Override
+    public void setFadingEnabled(boolean enable) {
+        mFragment.setFadingEnabled(enable);
+    }
+
+    @Override
+    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
+        mFragment.setOnKeyInterceptListener(onKeyListener);
+    }
+
+    @Override
+    public void setOnActionClickedListener(final OnActionClickedListener listener) {
+        mFragment.setOnPlaybackItemViewClickedListener(new OnItemViewClickedListener() {
+            @Override
+            public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                                      RowPresenter.ViewHolder rowViewHolder, Row row) {
+                if (item instanceof Action) {
+                    listener.onActionClicked((Action)item);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void setHostLifeCycleCallback(PlaybackGlue.HostLifecycleCallback callback) {
+        mFragment.setHostLifecycleCallback(callback);
+    }
+
+    @Override
+    public void notifyPlaybackRowChanged() {
+        mFragment.notifyPlaybackRowChanged();
+    }
+
+    @Override
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
+        mFragment.setPlaybackRowPresenter(presenter);
+    }
+
+    @Override
+    public void setPlaybackRow(Row row) {
+        mFragment.setPlaybackRow(row);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index 7b93115..6e1c008 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -17,12 +17,12 @@
 import android.animation.TimeAnimator.TimeListener;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.HorizontalGridView;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
@@ -201,8 +201,8 @@
             if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
             for (int i = 0; i < count; i++) {
                 View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh
-                        = (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
+                ItemBridgeAdapter.ViewHolder vh =
+                        (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
                 setRowViewExpanded(vh, mExpand);
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
index 4086658..a5acb41 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from RowsFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -19,12 +20,12 @@
 import android.animation.TimeAnimator.TimeListener;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
+import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.HorizontalGridView;
 import android.support.v17.leanback.widget.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.Presenter;
@@ -203,8 +204,8 @@
             if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
             for (int i = 0; i < count; i++) {
                 View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh
-                        = (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
+                ItemBridgeAdapter.ViewHolder vh =
+                        (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
                 setRowViewExpanded(vh, mExpand);
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
index 70393c4..93886f9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
@@ -13,6 +13,8 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.Manifest;
 import android.app.Fragment;
 import android.content.Intent;
@@ -30,6 +32,7 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SearchBar;
+import android.support.v17.leanback.widget.SearchOrbView;
 import android.support.v17.leanback.widget.SpeechRecognitionCallback;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.util.Log;
@@ -42,8 +45,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 /**
  * A fragment to handle searches. An application will supply an implementation
  * of the {@link SearchResultProvider} interface to handle the search and return
@@ -171,8 +172,10 @@
                 if (mResultAdapter != null) {
                     mResultAdapter.registerObserver(mAdapterObserver);
                 }
-                if (DEBUG) Log.v(TAG, "mResultAdapter " + mResultAdapter + " size " +
-                        (mResultAdapter == null ? 0 : mResultAdapter.size()));
+                if (DEBUG) {
+                    Log.v(TAG, "mResultAdapter " + mResultAdapter + " size "
+                            + (mResultAdapter == null ? 0 : mResultAdapter.size()));
+                }
                 // delay the first time to avoid setting a empty result adapter
                 // until we got first onChange() from the provider
                 if (!(firstTime && (mResultAdapter == null || mResultAdapter.size() == 0))) {
@@ -182,9 +185,11 @@
             }
             updateSearchBarNextFocusId();
 
-            if (DEBUG) Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition +
-                    " mResultAdapter " + mResultAdapter +
-                    " adapter " + mRowsFragment.getAdapter());
+            if (DEBUG) {
+                Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition
+                        + " mResultAdapter " + mResultAdapter
+                        + " adapter " + mRowsFragment.getAdapter());
+            }
             if (mAutoStartRecognition) {
                 mHandler.removeCallbacks(mStartRecognitionRunnable);
                 mHandler.postDelayed(mStartRecognitionRunnable, SPEECH_RECOGNITION_DELAY_MS);
@@ -223,8 +228,8 @@
 
     private boolean mIsPaused;
     private boolean mPendingStartRecognitionWhenPaused;
-    private SearchBar.SearchBarPermissionListener mPermissionListener
-            = new SearchBar.SearchBarPermissionListener() {
+    private SearchBar.SearchBarPermissionListener mPermissionListener =
+            new SearchBar.SearchBarPermissionListener() {
         @Override
         public void requestAudioPermission() {
             PermissionHelper.requestPermissions(SearchFragment.this,
@@ -514,6 +519,28 @@
     }
 
     /**
+     * Sets background color of not-listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+        if (mSearchBar != null) {
+            mSearchBar.setSearchAffordanceColors(colors);
+        }
+    }
+
+    /**
+     * Sets background color of listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
+        if (mSearchBar != null) {
+            mSearchBar.setSearchAffordanceColorsInListening(colors);
+        }
+    }
+
+    /**
      * Displays the completions shown by the IME. An application may provide
      * a list of query completions that the system will show in the IME.
      *
@@ -643,15 +670,15 @@
         if (mSearchBar == null || mResultAdapter == null) {
             return;
         }
-        final int viewId = (mResultAdapter.size() == 0 || mRowsFragment == null ||
-                mRowsFragment.getVerticalGridView() == null) ? 0 :
-                mRowsFragment.getVerticalGridView().getId();
+        final int viewId = (mResultAdapter.size() == 0 || mRowsFragment == null
+                || mRowsFragment.getVerticalGridView() == null)
+                        ? 0 : mRowsFragment.getVerticalGridView().getId();
         mSearchBar.setNextFocusDownId(viewId);
     }
 
     void updateFocus() {
-        if (mResultAdapter != null && mResultAdapter.size() > 0 &&
-                mRowsFragment != null && mRowsFragment.getAdapter() == mResultAdapter) {
+        if (mResultAdapter != null && mResultAdapter.size() > 0
+                && mRowsFragment != null && mRowsFragment.getAdapter() == mResultAdapter) {
             focusOnResults();
         } else {
             mSearchBar.requestFocus();
@@ -659,9 +686,8 @@
     }
 
     private void focusOnResults() {
-        if (mRowsFragment == null ||
-                mRowsFragment.getVerticalGridView() == null ||
-                mResultAdapter.size() == 0) {
+        if (mRowsFragment == null || mRowsFragment.getVerticalGridView() == null
+                || mResultAdapter.size() == 0) {
             return;
         }
         if (mRowsFragment.getVerticalGridView().requestFocus()) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
index 7b9582c..c8a058d 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from SearchFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,6 +16,8 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.Manifest;
 import android.support.v4.app.Fragment;
 import android.content.Intent;
@@ -32,6 +35,7 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SearchBar;
+import android.support.v17.leanback.widget.SearchOrbView;
 import android.support.v17.leanback.widget.SpeechRecognitionCallback;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.util.Log;
@@ -44,8 +48,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 /**
  * A fragment to handle searches. An application will supply an implementation
  * of the {@link SearchResultProvider} interface to handle the search and return
@@ -173,8 +175,10 @@
                 if (mResultAdapter != null) {
                     mResultAdapter.registerObserver(mAdapterObserver);
                 }
-                if (DEBUG) Log.v(TAG, "mResultAdapter " + mResultAdapter + " size " +
-                        (mResultAdapter == null ? 0 : mResultAdapter.size()));
+                if (DEBUG) {
+                    Log.v(TAG, "mResultAdapter " + mResultAdapter + " size "
+                            + (mResultAdapter == null ? 0 : mResultAdapter.size()));
+                }
                 // delay the first time to avoid setting a empty result adapter
                 // until we got first onChange() from the provider
                 if (!(firstTime && (mResultAdapter == null || mResultAdapter.size() == 0))) {
@@ -184,9 +188,11 @@
             }
             updateSearchBarNextFocusId();
 
-            if (DEBUG) Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition +
-                    " mResultAdapter " + mResultAdapter +
-                    " adapter " + mRowsSupportFragment.getAdapter());
+            if (DEBUG) {
+                Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition
+                        + " mResultAdapter " + mResultAdapter
+                        + " adapter " + mRowsSupportFragment.getAdapter());
+            }
             if (mAutoStartRecognition) {
                 mHandler.removeCallbacks(mStartRecognitionRunnable);
                 mHandler.postDelayed(mStartRecognitionRunnable, SPEECH_RECOGNITION_DELAY_MS);
@@ -225,8 +231,8 @@
 
     private boolean mIsPaused;
     private boolean mPendingStartRecognitionWhenPaused;
-    private SearchBar.SearchBarPermissionListener mPermissionListener
-            = new SearchBar.SearchBarPermissionListener() {
+    private SearchBar.SearchBarPermissionListener mPermissionListener =
+            new SearchBar.SearchBarPermissionListener() {
         @Override
         public void requestAudioPermission() {
             PermissionHelper.requestPermissions(SearchSupportFragment.this,
@@ -516,6 +522,28 @@
     }
 
     /**
+     * Sets background color of not-listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+        if (mSearchBar != null) {
+            mSearchBar.setSearchAffordanceColors(colors);
+        }
+    }
+
+    /**
+     * Sets background color of listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
+        if (mSearchBar != null) {
+            mSearchBar.setSearchAffordanceColorsInListening(colors);
+        }
+    }
+
+    /**
      * Displays the completions shown by the IME. An application may provide
      * a list of query completions that the system will show in the IME.
      *
@@ -645,15 +673,15 @@
         if (mSearchBar == null || mResultAdapter == null) {
             return;
         }
-        final int viewId = (mResultAdapter.size() == 0 || mRowsSupportFragment == null ||
-                mRowsSupportFragment.getVerticalGridView() == null) ? 0 :
-                mRowsSupportFragment.getVerticalGridView().getId();
+        final int viewId = (mResultAdapter.size() == 0 || mRowsSupportFragment == null
+                || mRowsSupportFragment.getVerticalGridView() == null)
+                        ? 0 : mRowsSupportFragment.getVerticalGridView().getId();
         mSearchBar.setNextFocusDownId(viewId);
     }
 
     void updateFocus() {
-        if (mResultAdapter != null && mResultAdapter.size() > 0 &&
-                mRowsSupportFragment != null && mRowsSupportFragment.getAdapter() == mResultAdapter) {
+        if (mResultAdapter != null && mResultAdapter.size() > 0
+                && mRowsSupportFragment != null && mRowsSupportFragment.getAdapter() == mResultAdapter) {
             focusOnResults();
         } else {
             mSearchBar.requestFocus();
@@ -661,9 +689,8 @@
     }
 
     private void focusOnResults() {
-        if (mRowsSupportFragment == null ||
-                mRowsSupportFragment.getVerticalGridView() == null ||
-                mResultAdapter.size() == 0) {
+        if (mRowsSupportFragment == null || mRowsSupportFragment.getVerticalGridView() == null
+                || mResultAdapter.size() == 0) {
             return;
         }
         if (mRowsSupportFragment.getVerticalGridView().requestFocus()) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
index 309de0c..2a87f10 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from VerticalGridFragment.java.  DO NOT MODIFY. */
 
 /*
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java
new file mode 100644
index 0000000..88fe1ba
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoFragment.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Bundle;
+import android.support.v17.leanback.R;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Subclass of {@link PlaybackFragment} that is responsible for providing a {@link SurfaceView}
+ * and rendering video.
+ */
+public class VideoFragment extends PlaybackFragment {
+    private SurfaceView mVideoSurface;
+    private SurfaceHolder.Callback mMediaPlaybackCallback;
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View root = super.onCreateView(inflater, container, savedInstanceState);
+        mVideoSurface = (SurfaceView) inflater.inflate(R.layout.lb_video_surface, container, false);
+        ((ViewGroup) root.findViewById(R.id.playback_fragment_root)).addView(mVideoSurface, 0);
+        if (mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().addCallback(mMediaPlaybackCallback);
+        }
+        setBackgroundType(PlaybackFragment.BG_LIGHT);
+        return root;
+    }
+
+    /**
+     * Adds {@link SurfaceHolder.Callback} to {@link android.view.SurfaceView}.
+     */
+    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
+        if (mVideoSurface != null && mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().removeCallback(mMediaPlaybackCallback);
+        }
+
+        mMediaPlaybackCallback = callback;
+        if (mVideoSurface != null && mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().addCallback(mMediaPlaybackCallback);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
new file mode 100644
index 0000000..82a650ef
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.view.SurfaceHolder;
+
+/**
+ * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * the interaction between this class and {@link VideoFragment}.
+ */
+public class VideoFragmentGlueHost extends PlaybackFragmentGlueHost {
+    private final VideoFragment mFragment;
+
+    public VideoFragmentGlueHost(VideoFragment fragment) {
+        super(fragment);
+        this.mFragment = fragment;
+    }
+
+    /**
+     * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
+     * {@link PlaybackGlue.PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
+     * have a reference to the component hosting it for rendering the video.
+     */
+    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
+        mFragment.setSurfaceHolderCallback(callback);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java
new file mode 100644
index 0000000..a7fb0f4
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragment.java
@@ -0,0 +1,61 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from VideoFragment.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Bundle;
+import android.support.v17.leanback.R;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Subclass of {@link PlaybackSupportFragment} that is responsible for providing a {@link SurfaceView}
+ * and rendering video.
+ */
+public class VideoSupportFragment extends PlaybackSupportFragment {
+    private SurfaceView mVideoSurface;
+    private SurfaceHolder.Callback mMediaPlaybackCallback;
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        View root = super.onCreateView(inflater, container, savedInstanceState);
+        mVideoSurface = (SurfaceView) inflater.inflate(R.layout.lb_video_surface, container, false);
+        ((ViewGroup) root.findViewById(R.id.playback_fragment_root)).addView(mVideoSurface, 0);
+        if (mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().addCallback(mMediaPlaybackCallback);
+        }
+        setBackgroundType(PlaybackSupportFragment.BG_LIGHT);
+        return root;
+    }
+
+    /**
+     * Adds {@link SurfaceHolder.Callback} to {@link android.view.SurfaceView}.
+     */
+    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
+        if (mVideoSurface != null && mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().removeCallback(mMediaPlaybackCallback);
+        }
+
+        mMediaPlaybackCallback = callback;
+        if (mVideoSurface != null && mMediaPlaybackCallback != null) {
+            mVideoSurface.getHolder().addCallback(mMediaPlaybackCallback);
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
new file mode 100644
index 0000000..1be6fe9
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
@@ -0,0 +1,41 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from VideoVideoFragmentGlueHost.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.view.SurfaceHolder;
+
+/**
+ * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * the interaction between this class and {@link VideoSupportFragment}.
+ */
+public class VideoSupportFragmentGlueHost extends PlaybackSupportFragmentGlueHost {
+    private final VideoSupportFragment mFragment;
+
+    public VideoSupportFragmentGlueHost(VideoSupportFragment fragment) {
+        super(fragment);
+        this.mFragment = fragment;
+    }
+
+    /**
+     * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
+     * {@link PlaybackGlue.PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
+     * have a reference to the component hosting it for rendering the video.
+     */
+    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
+        mFragment.setSurfaceHolderCallback(callback);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java b/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java
new file mode 100644
index 0000000..1fd3722
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java
@@ -0,0 +1,177 @@
+/*
+ * 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.v17.leanback.graphics;
+
+import android.graphics.Rect;
+
+/**
+ * This class contains the rules for updating the bounds of a
+ * {@link CompositeDrawable.ChildDrawable}. It contains four rules, one for each value of the
+ * rectangular bound - left/top/right/bottom.
+ */
+public class BoundsRule {
+    static final int INHERIT_PARENT = 0;
+    static final int ABSOLUTE_VALUE = 1;
+    static final int INHERIT_WITH_OFFSET = 2;
+
+    /**
+     * This class represents individual rules for updating the bounds. Currently we support
+     * 3 different rule types -
+     *
+     * <ul>
+     *     <li>inheritFromParent: it applies a percentage to the parent property to compute
+     *     the final value </li>
+     *     <li>absoluteValue: it always used the supplied absolute value</li>
+     *     <li>inheritFromParentWithOffset: this uses a combination of INHERIT_PARENT
+     *     and ABSOLUTE_VALUE. First it applies the percentage on the parent and then adds the
+     *     offset to compute the final value</li>
+     * </ul>
+     */
+    public final static class ValueRule {
+        private final int type;
+        private float fraction;
+        private int absoluteValue;
+
+        ValueRule(int type, int absoluteValue, float fraction) {
+            this.type = type;
+            this.absoluteValue = absoluteValue;
+            this.fraction = fraction;
+        }
+
+        ValueRule(ValueRule rule) {
+            this.type = rule.type;
+            this.fraction = rule.fraction;
+            this.absoluteValue = rule.absoluteValue;
+        }
+
+        /**
+         * Sets the fractional value (percentage of parent) for this rule.
+         */
+        public void setFraction(float fraction) {
+            this.fraction = fraction;
+        }
+
+        /**
+         * Returns the current fractional value.
+         */
+        public float getFraction() {
+            return fraction;
+        }
+
+        /**
+         * Sets the absolute value for this rule.
+         */
+        public void setAbsoluteValue(int absoluteValue) {
+            this.absoluteValue = absoluteValue;
+        }
+
+        /**
+         * Returns the current absolute value.
+         */
+        public int getAbsoluteValue() {
+            return absoluteValue;
+        }
+    }
+
+    /**
+     * Factory method for creating ValueRule of type INHERIT_FROM_PARENT.
+     */
+    public static ValueRule inheritFromParent(float fraction) {
+        return new ValueRule(INHERIT_PARENT, 0, fraction);
+    }
+
+    /**
+     * Factory method for creating ValueRule of type ABSOLUTE_VALUE.
+     */
+    public static ValueRule absoluteValue(int value) {
+        return new ValueRule(ABSOLUTE_VALUE, value, 0);
+    }
+
+    /**
+     * Factory method for creating ValueRule of type INHERIT_WITH_OFFSET.
+     */
+    public static ValueRule inheritFromParentWithOffset(float fraction, int value) {
+        return new ValueRule(INHERIT_WITH_OFFSET, value, fraction);
+    }
+
+    /**
+     * Takes in the current bounds and sets the final values based on the individual rules in the
+     * result object.
+     *
+     * @param rect Represents the current bounds.
+     * @param result Represents the final bounds.
+     */
+    public void calculateBounds(Rect rect, Rect result) {
+        if (mLeft == null) {
+            result.left = rect.left;
+        } else {
+            result.left = doCalculate(rect.left, mLeft, rect.width());
+        }
+
+        if (mRight == null) {
+            result.right = rect.right;
+        } else {
+            result.right = doCalculate(rect.left, mRight, rect.width());
+        }
+
+        if (mTop == null) {
+            result.top = rect.top;
+        } else {
+            result.top = doCalculate(rect.top, mTop, rect.height());
+        }
+
+        if (mBottom == null) {
+            result.bottom = rect.bottom;
+        } else {
+            result.bottom = doCalculate(rect.top, mBottom, rect.height());
+        }
+    }
+
+    public BoundsRule() {}
+
+    public BoundsRule(BoundsRule boundsRule) {
+        this.mLeft = boundsRule.mLeft != null ? new ValueRule(boundsRule.mLeft) : null;
+        this.mRight = boundsRule.mRight != null ? new ValueRule(boundsRule.mRight) : null;
+        this.mTop = boundsRule.mTop != null ? new ValueRule(boundsRule.mTop) : null;
+        this.mBottom = boundsRule.mBottom != null ? new ValueRule(boundsRule.mBottom) : null;
+    }
+
+    private int doCalculate(int value, ValueRule rule, int size) {
+        int offset = 0;
+        switch(rule.type) {
+            case INHERIT_WITH_OFFSET:
+                offset = rule.absoluteValue;
+            case INHERIT_PARENT:
+                return value + offset + (int)(rule.fraction * size);
+            case ABSOLUTE_VALUE:
+                return rule.absoluteValue;
+        }
+
+        throw new IllegalArgumentException("Invalid type: "+rule.type);
+    }
+
+    /** {@link ValueRule} for left attribute of {@link BoundsRule} */
+    public ValueRule mLeft;
+
+    /** {@link ValueRule} for top attribute of {@link BoundsRule} */
+    public ValueRule mTop;
+
+    /** {@link ValueRule} for right attribute of {@link BoundsRule} */
+    public ValueRule mRight;
+
+    /** {@link ValueRule} for bottom attribute of {@link BoundsRule} */
+    public ValueRule mBottom;
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
new file mode 100644
index 0000000..b6da479
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
@@ -0,0 +1,529 @@
+/*
+ * 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.v17.leanback.graphics;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.util.Property;
+
+import java.util.ArrayList;
+
+/**
+ * Generic drawable class that can be composed of multiple children. Whenever the bounds changes
+ * for this class, it updates those of it's children.
+ */
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class CompositeDrawable extends Drawable implements Drawable.Callback {
+
+    static class CompositeState extends Drawable.ConstantState {
+
+        final ArrayList<ChildDrawable> mChildren;
+
+        CompositeState() {
+            mChildren = new ArrayList<ChildDrawable>();
+        }
+
+        CompositeState(CompositeState other, CompositeDrawable parent, Resources res) {
+            final int n = other.mChildren.size();
+            mChildren = new ArrayList<ChildDrawable>(n);
+            for (int k = 0; k < n; k++) {
+                mChildren.add(new ChildDrawable(other.mChildren.get(k), parent, res));
+            }
+        }
+
+        @NonNull
+        @Override
+        public Drawable newDrawable() {
+            return new CompositeDrawable(this);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+
+    }
+
+    CompositeState mState;
+    boolean mMutated = false;
+
+    public CompositeDrawable() {
+        mState = new CompositeState();
+    }
+
+    CompositeDrawable(CompositeState state) {
+        mState = state;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mState;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mState = new CompositeState(mState, this, null);
+            final ArrayList<ChildDrawable> children = mState.mChildren;
+            for (int i = 0, n = children.size(); i < n; i++) {
+                final Drawable dr = children.get(i).mDrawable;
+                if (dr != null) {
+                    dr.mutate();
+                }
+            }
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * Adds the supplied region.
+     */
+    public void addChildDrawable(Drawable drawable) {
+        mState.mChildren.add(new ChildDrawable(drawable, this));
+    }
+
+    /**
+     * Returns the {@link Drawable} for the given index.
+     */
+    public Drawable getDrawable(int index) {
+        return mState.mChildren.get(index).mDrawable;
+    }
+
+    /**
+     * Returns the {@link ChildDrawable} at the given index.
+     */
+    public ChildDrawable getChildAt(int index) {
+        return mState.mChildren.get(index);
+    }
+
+    /**
+     * Removes the child corresponding to the given index.
+     */
+    public void removeChild(int index) {
+        mState.mChildren.remove(index);
+    }
+
+    /**
+     * Removes the given region.
+     */
+    public void removeDrawable(Drawable drawable) {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0; i < children.size(); i++) {
+            if (drawable == children.get(i).mDrawable) {
+                children.get(i).mDrawable.setCallback(null);
+                children.remove(i);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Returns the total number of children.
+     */
+    public int getChildCount() {
+        return mState.mChildren.size();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0; i < children.size(); i++) {
+            children.get(i).mDrawable.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        updateBounds(bounds);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0; i < children.size(); i++) {
+            children.get(i).mDrawable.setColorFilter(colorFilter);
+        }
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.UNKNOWN;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0; i < children.size(); i++) {
+            children.get(i).mDrawable.setAlpha(alpha);
+        }
+    }
+
+    /**
+     * @return Alpha value between 0(inclusive) and 255(inclusive)
+     */
+    public int getAlpha() {
+        final Drawable dr = getFirstNonNullDrawable();
+        if (dr != null) {
+            return DrawableCompat.getAlpha(dr);
+        } else {
+            return 0xFF;
+        }
+    }
+
+    final Drawable getFirstNonNullDrawable() {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0, n = children.size(); i < n; i++) {
+            final Drawable dr = children.get(i).mDrawable;
+            if (dr != null) {
+                return dr;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void invalidateDrawable(Drawable who) {
+        invalidateSelf();
+    }
+
+    @Override
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        unscheduleSelf(what);
+    }
+
+    /**
+     * Updates the bounds based on the {@link BoundsRule}.
+     */
+    void updateBounds(Rect bounds) {
+        final ArrayList<ChildDrawable> children = mState.mChildren;
+        for (int i = 0; i < children.size(); i++) {
+            ChildDrawable childDrawable = children.get(i);
+            childDrawable.updateBounds(bounds);
+        }
+    }
+
+    /**
+     * Wrapper class holding a drawable object and {@link BoundsRule} to update drawable bounds
+     * when parent bound changes.
+     */
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    public static final class ChildDrawable {
+        private final BoundsRule mBoundsRule;
+        private final Drawable mDrawable;
+        private final Rect adjustedBounds = new Rect();
+        final CompositeDrawable mParent;
+
+        public ChildDrawable(Drawable drawable, CompositeDrawable parent) {
+            this.mDrawable = drawable;
+            this.mParent = parent;
+            this.mBoundsRule = new BoundsRule();
+            drawable.setCallback(parent);
+        }
+
+        ChildDrawable(ChildDrawable orig, CompositeDrawable parent, Resources res) {
+            final Drawable dr = orig.mDrawable;
+            final Drawable clone;
+            if (dr != null) {
+                final ConstantState cs = dr.getConstantState();
+                if (res != null) {
+                    clone = cs.newDrawable(res);
+                } else {
+                    clone = cs.newDrawable();
+                }
+                clone.setCallback(parent);
+                DrawableCompat.setLayoutDirection(clone, DrawableCompat.getLayoutDirection(dr));
+                clone.setBounds(dr.getBounds());
+                clone.setLevel(dr.getLevel());
+            } else {
+                clone = null;
+            }
+            if (orig.mBoundsRule != null) {
+                this.mBoundsRule = new BoundsRule(orig.mBoundsRule);
+            } else {
+                this.mBoundsRule = new BoundsRule();
+            }
+            mDrawable = clone;
+            mParent = parent;
+        }
+
+        /**
+         * Returns the instance of {@link BoundsRule}.
+         */
+        public BoundsRule getBoundsRule() {
+            return this.mBoundsRule;
+        }
+
+        /**
+         * Returns the {@link Drawable}.
+         */
+        public Drawable getDrawable() {
+            return mDrawable;
+        }
+
+        /**
+         * Updates the bounds based on the {@link BoundsRule}.
+         */
+        void updateBounds(Rect bounds) {
+            mBoundsRule.calculateBounds(bounds, adjustedBounds);
+            mDrawable.setBounds(adjustedBounds);
+        }
+
+        /**
+         * After changing the {@link BoundsRule}, user should call this function
+         * for the drawable to recalculate its bounds.
+         */
+        public void recomputeBounds() {
+            updateBounds(mParent.getBounds());
+        }
+
+        /**
+         * Implementation of {@link Property} for overrideTop attribute.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Integer> TOP_ABSOLUTE =
+                new Property<CompositeDrawable.ChildDrawable, Integer>(
+                        Integer.class, "absoluteTop") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
+                if (obj.getBoundsRule().mTop == null) {
+                    obj.getBoundsRule().mTop = BoundsRule.absoluteValue(value);
+                } else {
+                    obj.getBoundsRule().mTop.setAbsoluteValue(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Integer get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mTop == null) {
+                    return obj.mParent.getBounds().top;
+                }
+                return obj.getBoundsRule().mTop.getAbsoluteValue();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overrideBottom attribute.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Integer> BOTTOM_ABSOLUTE =
+                new Property<CompositeDrawable.ChildDrawable, Integer>(
+                        Integer.class, "absoluteBottom") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
+                if (obj.getBoundsRule().mBottom == null) {
+                    obj.getBoundsRule().mBottom = BoundsRule.absoluteValue(value);
+                } else {
+                    obj.getBoundsRule().mBottom.setAbsoluteValue(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Integer get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mBottom == null) {
+                    return obj.mParent.getBounds().bottom;
+                }
+                return obj.getBoundsRule().mBottom.getAbsoluteValue();
+            }
+        };
+
+
+        /**
+         * Implementation of {@link Property} for overrideLeft attribute.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Integer> LEFT_ABSOLUTE =
+                new Property<CompositeDrawable.ChildDrawable, Integer>(
+                        Integer.class, "absoluteLeft") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
+                if (obj.getBoundsRule().mLeft == null) {
+                    obj.getBoundsRule().mLeft = BoundsRule.absoluteValue(value);
+                } else {
+                    obj.getBoundsRule().mLeft.setAbsoluteValue(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Integer get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mLeft == null) {
+                    return obj.mParent.getBounds().left;
+                }
+                return obj.getBoundsRule().mLeft.getAbsoluteValue();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overrideRight attribute.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Integer> RIGHT_ABSOLUTE =
+                new Property<CompositeDrawable.ChildDrawable, Integer>(
+                        Integer.class, "absoluteRight") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
+                if (obj.getBoundsRule().mRight == null) {
+                    obj.getBoundsRule().mRight = BoundsRule.absoluteValue(value);
+                } else {
+                    obj.getBoundsRule().mRight.setAbsoluteValue(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Integer get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mRight == null) {
+                    return obj.mParent.getBounds().right;
+                }
+                return obj.getBoundsRule().mRight.getAbsoluteValue();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overwriting the bottom attribute of
+         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
+         * change the bounds rules as a percentage of parent size. This is preferable over
+         * {@see PROPERTY_TOP_ABSOLUTE} when the exact start/end position of scroll movement
+         * isn't available at compile time.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Float> TOP_FRACTION =
+                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionTop") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
+                if (obj.getBoundsRule().mTop == null) {
+                    obj.getBoundsRule().mTop = BoundsRule.inheritFromParent(value);
+                } else {
+                    obj.getBoundsRule().mTop.setFraction(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Float get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mTop == null) {
+                    return 0f;
+                }
+                return obj.getBoundsRule().mTop.getFraction();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overwriting the bottom attribute of
+         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
+         * change the bounds rules as a percentage of parent size. This is preferable over
+         * {@see PROPERTY_BOTTOM_ABSOLUTE} when the exact start/end position of scroll movement
+         * isn't available at compile time.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Float> BOTTOM_FRACTION =
+                new Property<CompositeDrawable.ChildDrawable, Float>(
+                        Float.class, "fractionBottom") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
+                if (obj.getBoundsRule().mBottom == null) {
+                    obj.getBoundsRule().mBottom = BoundsRule.inheritFromParent(value);
+                } else {
+                    obj.getBoundsRule().mBottom.setFraction(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Float get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mBottom == null) {
+                    return 1f;
+                }
+                return obj.getBoundsRule().mBottom.getFraction();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overwriting the bottom attribute of
+         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
+         * change the bounds rules as a percentage of parent size. This is preferable over
+         * {@see PROPERTY_LEFT_ABSOLUTE} when the exact start/end position of scroll movement
+         * isn't available at compile time.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Float> LEFT_FRACTION =
+                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionLeft") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
+                if (obj.getBoundsRule().mLeft == null) {
+                    obj.getBoundsRule().mLeft = BoundsRule.inheritFromParent(value);
+                } else {
+                    obj.getBoundsRule().mLeft.setFraction(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Float get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mLeft == null) {
+                    return 0f;
+                }
+                return obj.getBoundsRule().mLeft.getFraction();
+            }
+        };
+
+        /**
+         * Implementation of {@link Property} for overwriting the bottom attribute of
+         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
+         * change the bounds rules as a percentage of parent size. This is preferable over
+         * {@see PROPERTY_RIGHT_ABSOLUTE} when the exact start/end position of scroll movement
+         * isn't available at compile time.
+         */
+        public static final Property<CompositeDrawable.ChildDrawable, Float> RIGHT_FRACTION =
+                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractoinRight") {
+            @Override
+            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
+                if (obj.getBoundsRule().mRight == null) {
+                    obj.getBoundsRule().mRight = BoundsRule.inheritFromParent(value);
+                } else {
+                    obj.getBoundsRule().mRight.setFraction(value);
+                }
+
+                obj.recomputeBounds();
+            }
+
+            @Override
+            public Float get(CompositeDrawable.ChildDrawable obj) {
+                if (obj.getBoundsRule().mRight == null) {
+                    return 1f;
+                }
+                return obj.getBoundsRule().mRight.getFraction();
+            }
+        };
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
new file mode 100644
index 0000000..7320701
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
@@ -0,0 +1,196 @@
+/*
+ * 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.v17.leanback.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+
+/**
+ * Subclass of {@link Drawable} that can be used to draw a bitmap into a region. Bitmap
+ * will be scaled to fit the full width of the region and will be aligned to the top left corner.
+ * Any region outside the bounds will be clipped during {@link #draw(Canvas)} call. Top
+ * position of the bitmap can be controlled by {@link #setVerticalOffset(int)} call.
+ */
+public class FitWidthBitmapDrawable extends Drawable {
+
+    static class BitmapState extends Drawable.ConstantState {
+        Paint mPaint;
+        Bitmap mBitmap;
+        Rect mSource;
+        final Rect mDefaultSource = new Rect();
+        int mOffset;
+
+        BitmapState() {
+            mPaint = new Paint();
+        }
+
+        BitmapState(BitmapState other) {
+            mBitmap = other.mBitmap;
+            mPaint = new Paint(other.mPaint);
+            mSource = other.mSource != null ? new Rect(other.mSource) : null;
+            mDefaultSource.set(other.mDefaultSource);
+            mOffset = other.mOffset;
+        }
+
+        @NonNull
+        @Override
+        public Drawable newDrawable() {
+            return new FitWidthBitmapDrawable(this);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
+
+    final Rect mDest = new Rect();
+    BitmapState mBitmapState;
+    boolean mMutated = false;
+
+    public FitWidthBitmapDrawable() {
+        mBitmapState = new BitmapState();
+    }
+
+    FitWidthBitmapDrawable(BitmapState state) {
+        mBitmapState = state;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mBitmapState;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mBitmapState = new BitmapState(mBitmapState);
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the bitmap.
+     */
+    public void setBitmap(Bitmap bitmap) {
+        mBitmapState.mBitmap = bitmap;
+        if (bitmap != null) {
+            mBitmapState.mDefaultSource.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        } else {
+            mBitmapState.mDefaultSource.set(0, 0, 0, 0);
+        }
+        mBitmapState.mSource = null;
+    }
+
+    /**
+     * Returns the bitmap.
+     */
+    public Bitmap getBitmap() {
+        return mBitmapState.mBitmap;
+    }
+
+    /**
+     * Sets the {@link Rect} used for extracting the bitmap.
+     */
+    public void setSource(Rect source) {
+        mBitmapState.mSource = source;
+    }
+
+    /**
+     * Returns the {@link Rect} used for extracting the bitmap.
+     */
+    public Rect getSource() {
+        return mBitmapState.mSource;
+    }
+
+    /**
+     * Sets the vertical offset which will be used for drawing the bitmap. The bitmap drawing
+     * will start the provided vertical offset.
+     */
+    public void setVerticalOffset(int offset) {
+        mBitmapState.mOffset = offset;
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the current vertical offset.
+     */
+    public int getVerticalOffset() {
+        return mBitmapState.mOffset;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mBitmapState.mBitmap != null) {
+            Rect bounds = getBounds();
+            mDest.left = 0;
+            mDest.top = mBitmapState.mOffset;
+            mDest.right = bounds.width();
+
+            Rect source = validateSource();
+            float scale = (float) bounds.width() / source.width();
+            mDest.bottom = mDest.top + (int) (source.height() * scale);
+            int i = canvas.save();
+            canvas.clipRect(bounds);
+            canvas.drawBitmap(mBitmapState.mBitmap, source, mDest, mBitmapState.mPaint);
+            canvas.restoreToCount(i);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final int oldAlpha = mBitmapState.mPaint.getAlpha();
+        if (alpha != oldAlpha) {
+            mBitmapState.mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
+    }
+
+    /**
+     * @return Alpha value between 0(inclusive) and 255(inclusive)
+     */
+    public int getAlpha() {
+        return mBitmapState.mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mBitmapState.mPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public int getOpacity() {
+        final Bitmap bitmap = mBitmapState.mBitmap;
+        return (bitmap == null || bitmap.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255)
+                ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
+    }
+
+    private Rect validateSource() {
+        if (mBitmapState.mSource == null) {
+            return mBitmapState.mDefaultSource;
+        } else {
+            return mBitmapState.mSource;
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java b/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java
deleted file mode 100644
index 4f1cd33..0000000
--- a/v17/leanback/src/android/support/v17/leanback/os/TraceHelper.java
+++ /dev/null
@@ -1,85 +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.v17.leanback.os;
-
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.os.TraceHelperJbmr2;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-
-/**
- * Helper for systrace events.
- * @hide
- */
-@RestrictTo(GROUP_ID)
-public final class TraceHelper {
-
-    final static TraceHelperVersionImpl sImpl;
-
-    interface TraceHelperVersionImpl {
-        void beginSection(String section);
-        void endSection();
-    }
-
-    private static final class TraceHelperStubImpl implements TraceHelperVersionImpl {
-        TraceHelperStubImpl() {
-        }
-
-        @Override
-        public void beginSection(String section) {
-        }
-
-        @Override
-        public void endSection() {
-        }
-    }
-
-    private static final class TraceHelperJbmr2Impl implements TraceHelperVersionImpl {
-        TraceHelperJbmr2Impl() {
-        }
-
-        @Override
-        public void beginSection(String section) {
-            TraceHelperJbmr2.beginSection(section);
-        }
-
-        @Override
-        public void endSection() {
-            TraceHelperJbmr2.endSection();
-        }
-    }
-
-    private TraceHelper() {
-    }
-
-    static {
-        if (Build.VERSION.SDK_INT >= 18) {
-            sImpl = new TraceHelperJbmr2Impl();
-        } else {
-            sImpl = new TraceHelperStubImpl();
-        }
-    }
-
-    public static void beginSection(String section) {
-        sImpl.beginSection(section);
-    }
-
-    public static void endSection() {
-        sImpl.endSection();
-    }
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/system/Settings.java b/v17/leanback/src/android/support/v17/leanback/system/Settings.java
index ec0316c..cdd2633 100644
--- a/v17/leanback/src/android/support/v17/leanback/system/Settings.java
+++ b/v17/leanback/src/android/support/v17/leanback/system/Settings.java
@@ -16,6 +16,8 @@
 
 package android.support.v17.leanback.system;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -26,8 +28,6 @@
 import android.support.v17.leanback.widget.ShadowOverlayContainer;
 import android.util.Log;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Provides various preferences affecting Leanback runtime behavior.
  * <p>Note this class is not thread safe and its methods should only
@@ -129,8 +129,9 @@
     private Customizations getCustomizations(Context context) {
         final PackageManager pm = context.getPackageManager();
         final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
-        if (DEBUG) Log.v(TAG, "getting oem customizations by intent: " +
-                ACTION_PARTNER_CUSTOMIZATION);
+        if (DEBUG) {
+            Log.v(TAG, "getting oem customizations by intent: " + ACTION_PARTNER_CUSTOMIZATION);
+        }
 
         Resources resources = null;
         String packageName = null;
@@ -151,7 +152,7 @@
     }
 
     private static boolean isSystemApp(ResolveInfo info) {
-        return (info.activityInfo != null &&
-                (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+        return (info.activityInfo != null
+                && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 5b72643..c514323 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -157,6 +157,9 @@
         public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject);
 
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup);
+
+        public void setEpicenterCallback(Object transitionObject,
+                TransitionEpicenterCallback callback);
     }
 
     /**
@@ -393,6 +396,11 @@
         @Override
         public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
         }
+
+        @Override
+        public void setEpicenterCallback(Object transitionObject,
+                TransitionEpicenterCallback callback) {
+        }
     }
 
     /**
@@ -636,6 +644,11 @@
             return TransitionHelperApi21.createChangeTransform();
         }
 
+        @Override
+        public void setEpicenterCallback(Object transitionObject,
+                TransitionEpicenterCallback callback) {
+            TransitionHelperApi21.setEpicenterCallback(transitionObject, callback);
+        }
     }
 
     static {
@@ -847,6 +860,11 @@
         sImpl.setTransitionGroup(viewGroup, transitionGroup);
     }
 
+    public static void setEpicenterCallback(Object transition,
+            TransitionEpicenterCallback callback) {
+        sImpl.setEpicenterCallback(transition, callback);
+    }
+
     /**
      * @deprecated Use static calls.
      */
diff --git a/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
new file mode 100644
index 0000000..523bd4c
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.util;
+
+import java.lang.Exception;
+
+/**
+ * Math Utilities for leanback library.
+ * @hide
+ */
+public final class MathUtil {
+
+    private MathUtil() {
+        // Prevent construction of this util class
+    }
+
+    /**
+     * Convert long to int safely. Similar with Math.toIntExact() in Java 8.
+     * @param numLong Number of type long to convert.
+     * @return int version of input.
+     * @throws ArithmeticException If input overflows int.
+     */
+    public static int safeLongToInt(long numLong) {
+        if ((int) numLong != numLong) {
+            throw new ArithmeticException("Input overflows int.\n");
+        }
+        return (int) numLong;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
index f603536..8d176c4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
@@ -101,9 +101,9 @@
             mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
                 @Override
                 public boolean onPreDraw() {
-                    if (mSubtitle.getVisibility() == View.VISIBLE &&
-                            mSubtitle.getTop() > view.getHeight() &&
-                            mTitle.getLineCount() > 1) {
+                    if (mSubtitle.getVisibility() == View.VISIBLE
+                            && mSubtitle.getTop() > view.getHeight()
+                            && mTitle.getLineCount() > 1) {
                         mTitle.setMaxLines(mTitle.getLineCount() - 1);
                         return false;
                     }
@@ -166,8 +166,8 @@
             hasTitle = false;
         } else {
             vh.mTitle.setVisibility(View.VISIBLE);
-            vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight() +
-                    vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
+            vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight()
+                    + vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
             vh.mTitle.setMaxLines(vh.mTitleMaxLines);
         }
         setTopMargin(vh.mTitle, vh.mTitleMargin);
@@ -179,8 +179,8 @@
         } else {
             vh.mSubtitle.setVisibility(View.VISIBLE);
             if (hasTitle) {
-                setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin +
-                        vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
+                setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin
+                        + vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
             } else {
                 setTopMargin(vh.mSubtitle, 0);
             }
@@ -190,15 +190,15 @@
             vh.mBody.setVisibility(View.GONE);
         } else {
             vh.mBody.setVisibility(View.VISIBLE);
-            vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight() +
-                    vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier());
+            vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight()
+                    + vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier());
 
             if (hasSubtitle) {
-                setTopMargin(vh.mBody, vh.mUnderSubtitleBaselineMargin +
-                        vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent);
+                setTopMargin(vh.mBody, vh.mUnderSubtitleBaselineMargin
+                        + vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent);
             } else if (hasTitle) {
-                setTopMargin(vh.mBody, vh.mUnderTitleBaselineMargin +
-                        vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
+                setTopMargin(vh.mBody, vh.mUnderTitleBaselineMargin
+                        + vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
             } else {
                 setTopMargin(vh.mBody, 0);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
index 87652dc..c607d64 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
@@ -203,9 +203,10 @@
             TypedValue typedValue = new TypedValue();
             boolean found = view.getContext().getTheme().resolveAttribute(
                     R.attr.playbackMediaItemNumberViewFlipperLayout, typedValue, true);
-            View mergeView = LayoutInflater.from(view.getContext()).
-                    inflate(found ? typedValue.resourceId :
-                            R.layout.lb_media_item_number_view_flipper,
+            View mergeView = LayoutInflater.from(view.getContext())
+                    .inflate(found
+                            ? typedValue.resourceId
+                            : R.layout.lb_media_item_number_view_flipper,
                             mMediaItemNumberViewFlipper, true);
 
             mMediaItemNumberView = (TextView) mergeView.findViewById(R.id.initial);
@@ -241,8 +242,8 @@
             mMediaItemRowActions = actionList;
             for (int i = mActionViewHolders.size(); i < actionList.length; i++) {
                 final int actionIndex = i;
-                final Presenter.ViewHolder actionViewHolder = actionPresenter.
-                        onCreateViewHolder(getMediaItemActionsContainer());
+                final Presenter.ViewHolder actionViewHolder =
+                        actionPresenter.onCreateViewHolder(getMediaItemActionsContainer());
                 getMediaItemActionsContainer().addView(actionViewHolder.view);
                 mActionViewHolders.add(actionViewHolder);
                 actionViewHolder.view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@@ -426,8 +427,8 @@
         if (mThemeId != 0) {
             context = new ContextThemeWrapper(context, mThemeId);
         }
-        View view = LayoutInflater.from(context).
-                inflate(R.layout.lb_row_media_item, parent, false);
+        View view =
+                LayoutInflater.from(context).inflate(R.layout.lb_row_media_item, parent, false);
         final ViewHolder vh = new ViewHolder(view);
         vh.mRowPresenter = this;
         if (mBackgroundColorSet) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
index 20308c5..b9c4948 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseCardView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.VisibleForTesting;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -125,9 +126,12 @@
     private final int mActivatedAnimDuration;
     private final int mSelectedAnimDuration;
 
+    /**
+     * Distance of top of info view to bottom of MainView, it will shift up when extra view appears.
+     */
     float mInfoOffset;
     float mInfoVisFraction;
-    float mInfoAlpha = 1.0f;
+    float mInfoAlpha;
     private Animation mAnim;
 
     private final static int[] LB_PRESSED_STATE_SET = new int[]{
@@ -195,7 +199,8 @@
         mExtraViewList = new ArrayList<View>();
 
         mInfoOffset = 0.0f;
-        mInfoVisFraction = 0.0f;
+        mInfoVisFraction = getFinalInfoVisFraction();
+        mInfoAlpha = getFinalInfoAlpha();
     }
 
     /**
@@ -242,8 +247,8 @@
                 // Valid card type
                 mCardType = type;
             } else {
-                Log.e(TAG, "Invalid card type specified: " + type +
-                        ". Defaulting to type CARD_TYPE_MAIN_ONLY.");
+                Log.e(TAG, "Invalid card type specified: " + type
+                        + ". Defaulting to type CARD_TYPE_MAIN_ONLY.");
                 mCardType = CARD_TYPE_MAIN_ONLY;
             }
             requestLayout();
@@ -269,16 +274,30 @@
      */
     public void setInfoVisibility(int visibility) {
         if (mInfoVisibility != visibility) {
+            cancelAnimations();
             mInfoVisibility = visibility;
-            if (mInfoVisibility == CARD_REGION_VISIBLE_SELECTED && isSelected()) {
-                mInfoVisFraction = 1.0f;
-            } else {
-                mInfoVisFraction = 0.0f;
-            }
+            mInfoVisFraction = getFinalInfoVisFraction();
             requestLayout();
+            float newInfoAlpha = getFinalInfoAlpha();
+            if (newInfoAlpha != mInfoAlpha) {
+                mInfoAlpha = newInfoAlpha;
+                for (int i = 0; i < mInfoViewList.size(); i++) {
+                    mInfoViewList.get(i).setAlpha(mInfoAlpha);
+                }
+            }
         }
     }
 
+    final float getFinalInfoVisFraction() {
+        return mCardType == CARD_TYPE_INFO_UNDER && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED
+                && !isSelected() ? 0.0f : 1.0f;
+    }
+
+    final float getFinalInfoAlpha() {
+        return mCardType == CARD_TYPE_INFO_OVER && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED
+                && !isSelected() ? 0.0f : 1.0f;
+    }
+
     /**
      * Returns the visibility of the info region of the card.
      */
@@ -293,17 +312,20 @@
      *     be one of {@link #CARD_REGION_VISIBLE_ALWAYS},
      *     {@link #CARD_REGION_VISIBLE_SELECTED}, or
      *     {@link #CARD_REGION_VISIBLE_ACTIVATED}.
+     * @deprecated Extra view's visibility is controlled by {@link #setInfoVisibility(int)}
      */
+    @Deprecated
     public void setExtraVisibility(int visibility) {
         if (mExtraVisibility != visibility) {
             mExtraVisibility = visibility;
-            requestLayout();
         }
     }
 
     /**
      * Returns the visibility of the extra region of the card.
+     * @deprecated Extra view's visibility is controlled by {@link #getInfoVisibility()}
      */
+    @Deprecated
     public int getExtraVisibility() {
         return mExtraVisibility;
     }
@@ -400,13 +422,13 @@
         }
 
         boolean infoAnimating = hasInfoRegion() && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED;
-        mMeasuredHeight = (int) (mainHeight +
-                (infoAnimating ? (infoHeight * mInfoVisFraction) : infoHeight)
+        mMeasuredHeight = (int) (mainHeight
+                + (infoAnimating ? (infoHeight * mInfoVisFraction) : infoHeight)
                 + extraHeight - (infoAnimating ? 0 : mInfoOffset));
 
         // Report our final dimensions.
-        setMeasuredDimension(View.resolveSizeAndState(mMeasuredWidth + getPaddingLeft() +
-                getPaddingRight(), widthMeasureSpec, state),
+        setMeasuredDimension(View.resolveSizeAndState(mMeasuredWidth + getPaddingLeft()
+                + getPaddingRight(), widthMeasureSpec, state),
                 View.resolveSizeAndState(mMeasuredHeight + getPaddingTop() + getPaddingBottom(),
                         heightMeasureSpec, state << View.MEASURED_HEIGHT_STATE_SHIFT));
     }
@@ -488,8 +510,6 @@
         super.onDetachedFromWindow();
         removeCallbacks(mAnimationTrigger);
         cancelAnimations();
-        mInfoOffset = 0.0f;
-        mInfoVisFraction = 0.0f;
     }
 
     private boolean hasInfoRegion() {
@@ -500,6 +520,9 @@
         return mCardType == CARD_TYPE_INFO_UNDER_WITH_EXTRA;
     }
 
+    /**
+     * Returns target visibility of info region.
+     */
     private boolean isRegionVisible(int regionVisibility) {
         switch (regionVisibility) {
             case CARD_REGION_VISIBLE_ALWAYS:
@@ -507,7 +530,28 @@
             case CARD_REGION_VISIBLE_ACTIVATED:
                 return isActivated();
             case CARD_REGION_VISIBLE_SELECTED:
-                return isActivated() && isSelected();
+                return isSelected();
+            default:
+                if (DEBUG) Log.e(TAG, "invalid region visibility state: " + regionVisibility);
+                return false;
+        }
+    }
+
+    /**
+     * Unlike isRegionVisible(), this method returns true when it is fading out when unselected.
+     */
+    private boolean isCurrentRegionVisible(int regionVisibility) {
+        switch (regionVisibility) {
+            case CARD_REGION_VISIBLE_ALWAYS:
+                return true;
+            case CARD_REGION_VISIBLE_ACTIVATED:
+                return isActivated();
+            case CARD_REGION_VISIBLE_SELECTED:
+                if (mCardType == CARD_TYPE_INFO_UNDER) {
+                    return mInfoVisFraction > 0f;
+                } else {
+                    return isSelected();
+                }
             default:
                 if (DEBUG) Log.e(TAG, "invalid region visibility state: " + regionVisibility);
                 return false;
@@ -521,13 +565,9 @@
 
         final int count = getChildCount();
 
-        boolean infoVisible = isRegionVisible(mInfoVisibility);
+        boolean infoVisible = hasInfoRegion() && isCurrentRegionVisible(mInfoVisibility);
         boolean extraVisible = hasExtraRegion() && mInfoOffset > 0f;
 
-        if (mCardType == CARD_TYPE_INFO_UNDER && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED) {
-            infoVisible = infoVisible && mInfoVisFraction > 0f;
-        }
-
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
 
@@ -538,6 +578,7 @@
             BaseCardView.LayoutParams lp = (BaseCardView.LayoutParams) child
                     .getLayoutParams();
             if (lp.viewType == LayoutParams.VIEW_TYPE_INFO) {
+                child.setAlpha(mInfoAlpha);
                 mInfoViewList.add(child);
                 child.setVisibility(infoVisible ? View.VISIBLE : View.GONE);
             } else if (lp.viewType == LayoutParams.VIEW_TYPE_EXTRA) {
@@ -579,11 +620,8 @@
     }
 
     private void applyActiveState(boolean active) {
-        if (hasInfoRegion() && mInfoVisibility <= CARD_REGION_VISIBLE_ACTIVATED) {
-            setInfoViewVisibility(active);
-        }
-        if (hasExtraRegion() && mExtraVisibility <= CARD_REGION_VISIBLE_ACTIVATED) {
-            //setExtraVisibility(active);
+        if (hasInfoRegion() && mInfoVisibility == CARD_REGION_VISIBLE_ACTIVATED) {
+            setInfoViewVisibility(isRegionVisible(mInfoVisibility));
         }
     }
 
@@ -643,6 +681,7 @@
         if (mAnim != null) {
             mAnim.cancel();
             mAnim = null;
+            clearAnimation();
         }
     }
 
@@ -695,32 +734,30 @@
     private void animateInfoHeight(boolean shown) {
         cancelAnimations();
 
-        int extraHeight = 0;
         if (shown) {
-            int widthSpec = MeasureSpec.makeMeasureSpec(mMeasuredWidth, MeasureSpec.EXACTLY);
-            int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-            for (int i = 0; i < mExtraViewList.size(); i++) {
-                View extraView = mExtraViewList.get(i);
+            for (int i = 0; i < mInfoViewList.size(); i++) {
+                View extraView = mInfoViewList.get(i);
                 extraView.setVisibility(View.VISIBLE);
-                extraView.measure(widthSpec, heightSpec);
-                extraHeight = Math.max(extraHeight, extraView.getMeasuredHeight());
             }
         }
 
-        mAnim = new InfoHeightAnimation(mInfoVisFraction, shown ? 1.0f : 0f);
+        float targetFraction = shown ? 1.0f : 0f;
+        if (mInfoVisFraction == targetFraction) {
+            return;
+        }
+        mAnim = new InfoHeightAnimation(mInfoVisFraction, targetFraction);
         mAnim.setDuration(mSelectedAnimDuration);
         mAnim.setInterpolator(new AccelerateDecelerateInterpolator());
         mAnim.setAnimationListener(new Animation.AnimationListener() {
-                @Override
+            @Override
             public void onAnimationStart(Animation animation) {
             }
 
-                @Override
+            @Override
             public void onAnimationEnd(Animation animation) {
-                if (mInfoOffset == 0f) {
-                    for (int i = 0; i < mExtraViewList.size(); i++) {
-                        mExtraViewList.get(i).setVisibility(View.GONE);
+                if (mInfoVisFraction == 0f) {
+                    for (int i = 0; i < mInfoViewList.size(); i++) {
+                        mInfoViewList.get(i).setVisibility(View.GONE);
                     }
                 }
             }
@@ -745,6 +782,10 @@
                 mInfoViewList.get(i).setVisibility(View.VISIBLE);
             }
         }
+        float targetAlpha = shown ? 1.0f : 0.0f;
+        if (targetAlpha == mInfoAlpha) {
+            return;
+        }
 
         mAnim = new InfoAlphaAnimation(mInfoAlpha, shown ? 1.0f : 0.0f);
         mAnim.setDuration(mActivatedAnimDuration);
@@ -854,9 +895,23 @@
         }
     }
 
+    class AnimationBase extends Animation {
+
+        @VisibleForTesting
+        final void mockStart() {
+            getTransformation(0, null);
+        }
+
+        @VisibleForTesting
+        final void mockEnd() {
+            applyTransformation(1f, null);
+            cancelAnimations();
+        }
+    }
+
     // Helper animation class used in the animation of the info and extra
     // fields vertically within the card
-    private class InfoOffsetAnimation extends Animation {
+    final class InfoOffsetAnimation extends AnimationBase {
         private float mStartValue;
         private float mDelta;
 
@@ -874,7 +929,7 @@
 
     // Helper animation class used in the animation of the visible height
     // for the info fields.
-    private class InfoHeightAnimation extends Animation {
+    final class InfoHeightAnimation extends AnimationBase {
         private float mStartValue;
         private float mDelta;
 
@@ -892,7 +947,7 @@
 
     // Helper animation class used to animate the alpha for the info views
     // when they are fading in or out of view.
-    private class InfoAlphaAnimation extends Animation {
+    final class InfoAlphaAnimation extends AnimationBase {
         private float mStartValue;
         private float mDelta;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
index 82f5cfb..3602ff3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
@@ -13,20 +13,21 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SimpleItemAnimator;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.support.v7.widget.SimpleItemAnimator;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import android.view.animation.AccelerateDecelerateInterpolator;
 
 /**
  * An abstract base class for vertically and horizontally scrolling lists. The items come
@@ -226,10 +227,12 @@
         boolean throughSideStart = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideStart, true);
         boolean throughSideEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideEnd, true);
         mLayoutManager.setFocusOutSideAllowed(throughSideStart, throughSideEnd);
-        mLayoutManager.setVerticalMargin(
-                a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0));
-        mLayoutManager.setHorizontalMargin(
-                a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0));
+        mLayoutManager.setVerticalSpacing(
+                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_verticalSpacing,
+                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)));
+        mLayoutManager.setHorizontalSpacing(
+                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_horizontalSpacing,
+                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)));
         if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
             setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
         }
@@ -430,41 +433,86 @@
     }
 
     /**
-     * Sets the margin in pixels between two child items.
+     * Sets the spacing in pixels between two child items.
+     * @deprecated use {@link #setItemSpacing(int)}
      */
+    @Deprecated
     public void setItemMargin(int margin) {
-        mLayoutManager.setItemMargin(margin);
+        setItemSpacing(margin);
+    }
+
+    /**
+     * Sets the spacing in pixels between two child items.
+     */
+    public void setItemSpacing(int spacing) {
+        mLayoutManager.setItemSpacing(spacing);
         requestLayout();
     }
 
     /**
-     * Sets the margin in pixels between two child items vertically.
+     * Sets the spacing in pixels between two child items vertically.
+     * @deprecated Use {@link #setVerticalSpacing(int)}
      */
+    @Deprecated
     public void setVerticalMargin(int margin) {
-        mLayoutManager.setVerticalMargin(margin);
-        requestLayout();
+        setVerticalSpacing(margin);
     }
 
     /**
-     * Returns the margin in pixels between two child items vertically.
+     * Returns the spacing in pixels between two child items vertically.
+     * @deprecated Use {@link #getVerticalSpacing()}
      */
+    @Deprecated
     public int getVerticalMargin() {
-        return mLayoutManager.getVerticalMargin();
+        return mLayoutManager.getVerticalSpacing();
     }
 
     /**
-     * Sets the margin in pixels between two child items horizontally.
+     * Sets the spacing in pixels between two child items horizontally.
+     * @deprecated Use {@link #setHorizontalSpacing(int)}
      */
+    @Deprecated
     public void setHorizontalMargin(int margin) {
-        mLayoutManager.setHorizontalMargin(margin);
+        setHorizontalSpacing(margin);
+    }
+
+    /**
+     * Returns the spacing in pixels between two child items horizontally.
+     * @deprecated Use {@link #getHorizontalSpacing()}
+     */
+    @Deprecated
+    public int getHorizontalMargin() {
+        return mLayoutManager.getHorizontalSpacing();
+    }
+
+    /**
+     * Sets the spacing in pixels between two child items vertically.
+     */
+    public void setVerticalSpacing(int spacing) {
+        mLayoutManager.setVerticalSpacing(spacing);
         requestLayout();
     }
 
     /**
-     * Returns the margin in pixels between two child items horizontally.
+     * Returns the spacing in pixels between two child items vertically.
      */
-    public int getHorizontalMargin() {
-        return mLayoutManager.getHorizontalMargin();
+    public int getVerticalSpacing() {
+        return mLayoutManager.getVerticalSpacing();
+    }
+
+    /**
+     * Sets the spacing in pixels between two child items horizontally.
+     */
+    public void setHorizontalSpacing(int spacing) {
+        mLayoutManager.setHorizontalSpacing(spacing);
+        requestLayout();
+    }
+
+    /**
+     * Returns the spacing in pixels between two child items horizontally.
+     */
+    public int getHorizontalSpacing() {
+        return mLayoutManager.getHorizontalSpacing();
     }
 
     /**
@@ -610,7 +658,7 @@
             if (vh == null || hasPendingAdapterUpdates()) {
                 addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
                     @Override
-                    public void onChildViewHolderSelected(RecyclerView parent,
+                    public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
                             RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
                         if (selectedPosition == position) {
                             removeOnChildViewHolderSelectedListener(this);
@@ -951,4 +999,21 @@
         return mLayoutManager.getExtraLayoutSpace();
     }
 
+
+    public void animateOut() {
+        ((GridLayoutManager) getLayoutManager()).setIsSlidingChildViews(true);
+        smoothScrollBy(0, -600, new AccelerateDecelerateInterpolator());
+    }
+
+    public void animateIn() {
+        addOnScrollListener(new OnScrollListener() {
+            @Override
+            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+                    ((GridLayoutManager) getLayoutManager()).setIsSlidingChildViews(false);
+                }
+            }
+        });
+        smoothScrollBy(0, 600, new AccelerateDecelerateInterpolator());
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java
index 0613667..738d0e9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java
@@ -25,7 +25,6 @@
      * @param rowViewHolder The view holder of the row which the clicked item belongs to.
      * @param row The row which the clicked item belongs to.
      */
-    public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+    void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
                               RowPresenter.ViewHolder rowViewHolder, T row);
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
index c19c390..7f90cf6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BrowseFrameLayout.java
@@ -16,6 +16,7 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -67,6 +68,7 @@
 
     private OnFocusSearchListener mListener;
     private OnChildFocusListener mOnChildFocusListener;
+    private OnKeyListener mOnDispatchKeyListener;
 
     /**
      * Sets a {@link OnFocusSearchListener}.
@@ -124,4 +126,25 @@
             mOnChildFocusListener.onRequestChildFocus(child, focused);
         }
     }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        boolean consumed = super.dispatchKeyEvent(event);
+        if (mOnDispatchKeyListener != null) {
+            if (!consumed) {
+                return mOnDispatchKeyListener.onKey(getRootView(), event.getKeyCode(), event);
+            }
+        }
+        return consumed;
+    }
+
+    /**
+     * Sets the {@link android.view.View.OnKeyListener} on this view. This listener would fire
+     * only for unhandled {@link KeyEvent}s. We need to provide an external key listener to handle
+     * back button clicks when we are in full screen video mode because
+     * {@link View#setOnKeyListener(OnKeyListener)} doesn't fire as the focus is not on this view.
+     */
+    public void setOnDispatchKeyListener(OnKeyListener listener) {
+        this.mOnDispatchKeyListener = listener;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
index f79f02f..4314fce 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlBarPresenter.java
@@ -19,7 +19,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 
 /**
  * A presenter that assumes a LinearLayout container for a series
@@ -127,8 +126,8 @@
             int adapterSize = adapter == null ? 0 : adapter.size();
             // Shrink the number of attached views
             View focusedView = mControlBar.getFocusedChild();
-            if (focusedView != null && adapterSize > 0 &&
-                    mControlBar.indexOfChild(focusedView) >= adapterSize) {
+            if (focusedView != null && adapterSize > 0
+                    && mControlBar.indexOfChild(focusedView) >= adapterSize) {
                 mControlBar.getChildAt(adapter.size() - 1).requestFocus();
             }
             for (int i = mControlBar.getChildCount() - 1; i >= adapterSize; i--) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java b/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
index 35a36b1..eef6c9c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
@@ -101,8 +101,8 @@
                     vh.mLabel.setText(null);
                 }
             }
-            CharSequence contentDescription = TextUtils.isEmpty(action.getLabel2()) ?
-                action.getLabel1() : action.getLabel2();
+            CharSequence contentDescription = TextUtils.isEmpty(action.getLabel2())
+                    ? action.getLabel1() : action.getLabel2();
             if (!TextUtils.equals(vh.mFocusableView.getContentDescription(), contentDescription)) {
                 vh.mFocusableView.setContentDescription(contentDescription);
                 vh.mFocusableView.sendAccessibilityEvent(
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
index 6ae9995..22dba9c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
@@ -1,12 +1,9 @@
 package android.support.v17.leanback.widget;
 
 import android.support.v17.leanback.R;
-
-import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.ImageView;
 
 /**
@@ -96,8 +93,8 @@
         View view = onCreateView(parent);
         ViewHolder vh = new ViewHolder(view);
         ViewGroup.LayoutParams lp = view.getLayoutParams();
-        vh.setSizeFromDrawableIntrinsic(lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
-                lp.width == ViewGroup.LayoutParams.WRAP_CONTENT);
+        vh.setSizeFromDrawableIntrinsic(lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+                && lp.width == ViewGroup.LayoutParams.WRAP_CONTENT);
         return vh;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
index 88d7195..d484bcc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
@@ -15,11 +15,9 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
@@ -33,8 +31,6 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
-import java.util.Collection;
-
 /**
  * Renders a {@link DetailsOverviewRow} to display an overview of an item.
  * Typically this row will be the first row in a fragment
@@ -80,8 +76,8 @@
 
         @Override
         public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null ||
-                    mActionClickedListener != null) {
+            if (mViewHolder.getOnItemViewClickedListener() != null
+                    || mActionClickedListener != null) {
                 ibvh.getPresenter().setOnClickListener(
                         ibvh.getViewHolder(), new View.OnClickListener() {
                             @Override
@@ -100,8 +96,8 @@
         }
         @Override
         public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null ||
-                    mActionClickedListener != null) {
+            if (mViewHolder.getOnItemViewClickedListener() != null
+                    || mActionClickedListener != null) {
                 ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
             }
         }
@@ -195,9 +191,9 @@
             if (!isSelected()) {
                 return;
             }
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null ?
-                    mActionsRow.getChildViewHolder(view) :
-                    mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
+            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null
+                    ? mActionsRow.getChildViewHolder(view)
+                    : mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
             if (ibvh == null) {
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(null, null,
@@ -231,14 +227,16 @@
             RecyclerView.ViewHolder viewHolder;
 
             viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
-            boolean showRight = (viewHolder == null ||
-                    viewHolder.itemView.getRight() > mActionsRow.getWidth());
+            boolean showRight = (viewHolder == null
+                    || viewHolder.itemView.getRight() > mActionsRow.getWidth());
 
             viewHolder = mActionsRow.findViewHolderForPosition(0);
             boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
 
-            if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
-                    " showRight " + showRight + " showLeft " + showLeft);
+            if (DEBUG) {
+                Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll
+                        + " showRight " + showRight + " showLeft " + showLeft);
+            }
 
             showMoreRight(showRight);
             showMoreLeft(showLeft);
@@ -487,8 +485,8 @@
                 }
             }
             // If long dimension bigger than the card height we scale down.
-            if ((landscape && drawableWidth > cardHeight) ||
-                    (!landscape && drawableHeight > cardHeight)) {
+            if ((landscape && drawableWidth > cardHeight)
+                    || (!landscape && drawableHeight > cardHeight)) {
                 scaleImage = true;
             }
             // If we're not scaling to fit the card height then we always use margin.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
index e182763..0bd03f1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -13,22 +13,20 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.app.Activity;
+import android.graphics.Matrix;
 import android.os.Handler;
+import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.transition.TransitionListener;
+import android.support.v17.leanback.widget.DetailsOverviewRowPresenter.ViewHolder;
 import android.support.v4.app.ActivityCompat;
 import android.support.v4.app.SharedElementCallback;
 import android.support.v4.view.ViewCompat;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.widget.DetailsOverviewRowPresenter.ViewHolder;
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Matrix;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 
@@ -184,12 +182,12 @@
 
     void setSharedElementEnterTransition(Activity activity, String sharedElementName,
             long timeoutMs) {
-        if (activity == null && !TextUtils.isEmpty(sharedElementName) ||
-                activity != null && TextUtils.isEmpty(sharedElementName)) {
+        if (activity == null && !TextUtils.isEmpty(sharedElementName)
+                || activity != null && TextUtils.isEmpty(sharedElementName)) {
             throw new IllegalArgumentException();
         }
-        if (activity == mActivityToRunTransition &&
-                TextUtils.equals(sharedElementName, mSharedElementName)) {
+        if (activity == mActivityToRunTransition
+                && TextUtils.equals(sharedElementName, mSharedElementName)) {
             return;
         }
         if (mActivityToRunTransition != null) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
index 4b02418..189dfe6 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
@@ -13,30 +13,21 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.app.Activity;
-import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.ListRowPresenter.ViewHolder;
 import android.support.v7.widget.RecyclerView;
 import android.util.Log;
-import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import java.util.Collection;
 
 /**
  * Renders a {@link DetailsOverviewRow} to display an overview of an item. Typically this row will
@@ -126,8 +117,8 @@
 
         @Override
         public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null ||
-                    mActionClickedListener != null) {
+            if (mViewHolder.getOnItemViewClickedListener() != null
+                    || mActionClickedListener != null) {
                 ibvh.getPresenter().setOnClickListener(
                         ibvh.getViewHolder(), new View.OnClickListener() {
                             @Override
@@ -146,8 +137,8 @@
         }
         @Override
         public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null ||
-                    mActionClickedListener != null) {
+            if (mViewHolder.getOnItemViewClickedListener() != null
+                    || mActionClickedListener != null) {
                 ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
             }
         }
@@ -258,9 +249,9 @@
             if (!isSelected()) {
                 return;
             }
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null ?
-                    mActionsRow.getChildViewHolder(view) :
-                    mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
+            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null
+                    ? mActionsRow.getChildViewHolder(view)
+                    : mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
             if (ibvh == null) {
                 if (getOnItemViewSelectedListener() != null) {
                     getOnItemViewSelectedListener().onItemSelected(null, null,
@@ -294,14 +285,16 @@
             RecyclerView.ViewHolder viewHolder;
 
             viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
-            boolean showRight = (viewHolder == null ||
-                    viewHolder.itemView.getRight() > mActionsRow.getWidth());
+            boolean showRight = (viewHolder == null
+                    || viewHolder.itemView.getRight() > mActionsRow.getWidth());
 
             viewHolder = mActionsRow.findViewHolderForPosition(0);
             boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
 
-            if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
-                    " showRight " + showRight + " showLeft " + showLeft);
+            if (DEBUG) {
+                Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll
+                        + " showRight " + showRight + " showLeft " + showLeft);
+            }
 
         }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
index 7fbb0e6..570a7f2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
@@ -13,26 +13,15 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.os.Handler;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.SharedElementCallback;
-import android.support.v4.view.ViewCompat;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder;
 import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Matrix;
+import android.os.Handler;
+import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.transition.TransitionListener;
+import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.view.ViewCompat;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import java.util.List;
 
 import java.lang.ref.WeakReference;
 
@@ -85,12 +74,12 @@
 
     public void setSharedElementEnterTransition(Activity activity, String sharedElementName,
             long timeoutMs) {
-        if (activity == null && !TextUtils.isEmpty(sharedElementName) ||
-                activity != null && TextUtils.isEmpty(sharedElementName)) {
+        if (activity == null && !TextUtils.isEmpty(sharedElementName)
+                || activity != null && TextUtils.isEmpty(sharedElementName)) {
             throw new IllegalArgumentException();
         }
-        if (activity == mActivityToRunTransition &&
-                TextUtils.equals(sharedElementName, mSharedElementName)) {
+        if (activity == mActivityToRunTransition
+                && TextUtils.equals(sharedElementName, mSharedElementName)) {
             return;
         }
         mActivityToRunTransition = activity;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
index 5df965d..64d151f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
@@ -107,7 +107,7 @@
 
     protected Provider mProvider;
     protected boolean mReversedFlow;
-    protected int mMargin;
+    protected int mSpacing;
     protected int mNumRows;
     protected int mFirstVisibleIndex = -1;
     protected int mLastVisibleIndex = -1;
@@ -133,10 +133,10 @@
     }
 
     /**
-     * Sets the margin between items in a row
+     * Sets the space between items in a row
      */
-    public final void setMargin(int margin) {
-        mMargin = margin;
+    public final void setSpacing(int spacing) {
+        mSpacing = spacing;
     }
 
     /**
@@ -292,8 +292,8 @@
         if (mLastVisibleIndex < 0) {
             return false;
         }
-        return mReversedFlow ? findRowMin(true, null) <= toLimit + mMargin :
-                    findRowMax(false, null) >= toLimit - mMargin;
+        return mReversedFlow ? findRowMin(true, null) <= toLimit + mSpacing :
+                    findRowMax(false, null) >= toLimit - mSpacing;
     }
 
     /**
@@ -303,8 +303,8 @@
         if (mLastVisibleIndex < 0) {
             return false;
         }
-        return mReversedFlow ? findRowMax(false, null) >= toLimit - mMargin :
-                    findRowMin(true, null) <= toLimit + mMargin;
+        return mReversedFlow ? findRowMax(false, null) >= toLimit - mSpacing :
+                    findRowMin(true, null) <= toLimit + mSpacing;
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 0a21ddd..580f01b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -13,12 +13,19 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.v7.widget.RecyclerView.HORIZONTAL;
+import static android.support.v7.widget.RecyclerView.NO_ID;
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+import static android.support.v7.widget.RecyclerView.VERTICAL;
+
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.os.TraceCompat;
 import android.support.v4.util.CircularIntArray;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -27,21 +34,14 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Recycler;
 import android.support.v7.widget.RecyclerView.State;
-import android.support.v17.leanback.os.TraceHelper;
-
-import static android.support.v7.widget.RecyclerView.NO_ID;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-import static android.support.v7.widget.RecyclerView.HORIZONTAL;
-import static android.support.v7.widget.RecyclerView.VERTICAL;
-
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.FocusFinder;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.MeasureSpec;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -49,12 +49,12 @@
 
 final class GridLayoutManager extends RecyclerView.LayoutManager {
 
-     /*
-      * LayoutParams for {@link HorizontalGridView} and {@link VerticalGridView}.
-      * The class currently does two internal jobs:
-      * - Saves optical bounds insets.
-      * - Caches focus align view center.
-      */
+    /*
+     * LayoutParams for {@link HorizontalGridView} and {@link VerticalGridView}.
+     * The class currently does two internal jobs:
+     * - Saves optical bounds insets.
+     * - Caches focus align view center.
+     */
     final static class LayoutParams extends RecyclerView.LayoutParams {
 
         // For placement
@@ -213,6 +213,7 @@
                 mInSelection = false;
             }
             dispatchChildSelected();
+            dispatchChildSelectedAndPositioned();
             super.onStop();
         }
 
@@ -220,8 +221,8 @@
         protected int calculateTimeForScrolling(int dx) {
             int ms = super.calculateTimeForScrolling(dx);
             if (mWindowAlignment.mainAxis().getSize() > 0) {
-                float minMs = (float) MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN /
-                        mWindowAlignment.mainAxis().getSize() * dx;
+                float minMs = (float) MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN
+                        / mWindowAlignment.mainAxis().getSize() * dx;
                 if (ms < minMs) {
                     ms = (int) minMs;
                 }
@@ -345,8 +346,8 @@
             if (mPendingMoves == 0) {
                 return null;
             }
-            int direction = (mReverseFlowPrimary ? mPendingMoves > 0 : mPendingMoves < 0) ?
-                    -1 : 1;
+            int direction = (mReverseFlowPrimary ? mPendingMoves > 0 : mPendingMoves < 0)
+                    ? -1 : 1;
             if (mOrientation == HORIZONTAL) {
                 return new PointF(direction, 0);
             } else {
@@ -375,6 +376,9 @@
     // effect smooth scrolling too over to bind an item view then drag the item view back.
     final static int MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN = 30;
 
+    // Represents whether child views are sliding in or out.
+    private boolean mIsSlidingChildViews;
+
     String getTag() {
         return TAG + ":" + mBaseGridView.getId();
     }
@@ -516,19 +520,19 @@
     /**
      * Margin between items.
      */
-    private int mHorizontalMargin;
+    private int mHorizontalSpacing;
     /**
      * Margin between items vertically.
      */
-    private int mVerticalMargin;
+    private int mVerticalSpacing;
     /**
      * Margin in main direction.
      */
-    private int mMarginPrimary;
+    private int mSpacingPrimary;
     /**
      * Margin in second direction.
      */
-    private int mMarginSecondary;
+    private int mSpacingSecondary;
     /**
      * How to position child in secondary direction.
      */
@@ -758,33 +762,33 @@
         }
     }
 
-    public void setItemMargin(int margin) {
-        mVerticalMargin = mHorizontalMargin = margin;
-        mMarginPrimary = mMarginSecondary = margin;
+    public void setItemSpacing(int space) {
+        mVerticalSpacing = mHorizontalSpacing = space;
+        mSpacingPrimary = mSpacingSecondary = space;
     }
 
-    public void setVerticalMargin(int margin) {
-        if (mOrientation == HORIZONTAL) {
-            mMarginSecondary = mVerticalMargin = margin;
+    public void setVerticalSpacing(int space) {
+        if (mOrientation == VERTICAL) {
+            mSpacingPrimary = mVerticalSpacing = space;
         } else {
-            mMarginPrimary = mVerticalMargin = margin;
+            mSpacingSecondary = mVerticalSpacing = space;
         }
     }
 
-    public void setHorizontalMargin(int margin) {
+    public void setHorizontalSpacing(int space) {
         if (mOrientation == HORIZONTAL) {
-            mMarginPrimary = mHorizontalMargin = margin;
+            mSpacingPrimary = mHorizontalSpacing = space;
         } else {
-            mMarginSecondary = mHorizontalMargin = margin;
+            mSpacingSecondary = mHorizontalSpacing = space;
         }
     }
 
-    public int getVerticalMargin() {
-        return mVerticalMargin;
+    public int getVerticalSpacing() {
+        return mVerticalSpacing;
     }
 
-    public int getHorizontalMargin() {
-        return mHorizontalMargin;
+    public int getHorizontalSpacing() {
+        return mHorizontalSpacing;
     }
 
     public void setGravity(int gravity) {
@@ -827,8 +831,8 @@
     }
 
     boolean hasOnChildViewHolderSelectedListener() {
-        return mChildViewHolderSelectedListeners != null &&
-                mChildViewHolderSelectedListeners.size() > 0;
+        return mChildViewHolderSelectedListeners != null
+                && mChildViewHolderSelectedListeners.size() > 0;
     }
 
     void fireOnChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child,
@@ -842,6 +846,17 @@
         }
     }
 
+    void fireOnChildViewHolderSelectedAndPositioned(RecyclerView parent, RecyclerView.ViewHolder
+            child, int position, int subposition) {
+        if (mChildViewHolderSelectedListeners == null) {
+            return;
+        }
+        for (int i = mChildViewHolderSelectedListeners.size() - 1; i >= 0 ; i--) {
+            mChildViewHolderSelectedListeners.get(i).onChildViewHolderSelectedAndPositioned(parent,
+                    child, position, subposition);
+        }
+    }
+
     void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
         mChildLaidOutListener = listener;
     }
@@ -892,7 +907,7 @@
             return;
         }
 
-        if (TRACE) TraceHelper.beginSection("onChildSelected");
+        if (TRACE) TraceCompat.beginSection("onChildSelected");
         View view = mFocusPosition == NO_POSITION ? null : findViewByPosition(mFocusPosition);
         if (view != null) {
             RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
@@ -907,7 +922,7 @@
             }
             fireOnChildViewHolderSelected(mBaseGridView, null, NO_POSITION, 0);
         }
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
 
         // Children may request layout when a child selection event occurs (such as a change of
         // padding on the current and previously selected rows).
@@ -929,6 +944,27 @@
         }
     }
 
+    private void dispatchChildSelectedAndPositioned() {
+        if (!hasOnChildViewHolderSelectedListener()) {
+            return;
+        }
+
+        if (TRACE) TraceCompat.beginSection("onChildSelectedAndPositioned");
+        View view = mFocusPosition == NO_POSITION ? null : findViewByPosition(mFocusPosition);
+        if (view != null) {
+            RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
+            fireOnChildViewHolderSelectedAndPositioned(mBaseGridView, vh, mFocusPosition,
+                    mSubFocusPosition);
+        } else {
+            if (mChildSelectedListener != null) {
+                mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
+            }
+            fireOnChildViewHolderSelectedAndPositioned(mBaseGridView, null, NO_POSITION, 0);
+        }
+        if (TRACE) TraceCompat.endSection();
+
+    }
+
     @Override
     public boolean canScrollHorizontally() {
         // We can scroll horizontally if we have horizontal orientation, or if
@@ -999,14 +1035,14 @@
 
     @Override
     public int getDecoratedRight(View child) {
-        return super.getDecoratedRight(child) -
-                ((LayoutParams) child.getLayoutParams()).mRightInset;
+        return super.getDecoratedRight(child)
+                - ((LayoutParams) child.getLayoutParams()).mRightInset;
     }
 
     @Override
     public int getDecoratedBottom(View child) {
-        return super.getDecoratedBottom(child) -
-                ((LayoutParams) child.getLayoutParams()).mBottomInset;
+        return super.getDecoratedBottom(child)
+                - ((LayoutParams) child.getLayoutParams()).mBottomInset;
     }
 
     @Override
@@ -1091,11 +1127,11 @@
             mFocusPosition = 0;
             mSubFocusPosition = 0;
         }
-        if (!mState.didStructureChange() && mGrid.getFirstVisibleIndex() >= 0 &&
-                !mForceFullLayout && mGrid != null && mGrid.getNumRows() == mNumRows) {
+        if (!mState.didStructureChange() && mGrid.getFirstVisibleIndex() >= 0
+                && !mForceFullLayout && mGrid != null && mGrid.getNumRows() == mNumRows) {
             updateScrollController();
             updateScrollSecondAxis();
-            mGrid.setMargin(mMarginPrimary);
+            mGrid.setSpacing(mSpacingPrimary);
             if (!focusViewWasInTree && mFocusPosition != NO_POSITION) {
                 mGrid.setStart(mFocusPosition);
             }
@@ -1104,20 +1140,17 @@
             mForceFullLayout = false;
             int firstVisibleIndex = focusViewWasInTree ? mGrid.getFirstVisibleIndex() : 0;
 
-            if (mGrid == null || mNumRows != mGrid.getNumRows() ||
-                    mReverseFlowPrimary != mGrid.isReversedFlow()) {
+            if (mGrid == null || mNumRows != mGrid.getNumRows()
+                    || mReverseFlowPrimary != mGrid.isReversedFlow()) {
                 mGrid = Grid.createGrid(mNumRows);
                 mGrid.setProvider(mGridProvider);
                 mGrid.setReversedFlow(mReverseFlowPrimary);
             }
             initScrollController();
             updateScrollSecondAxis();
-            mGrid.setMargin(mMarginPrimary);
+            mGrid.setSpacing(mSpacingPrimary);
             detachAndScrapAttachedViews(mRecycler);
             mGrid.resetVisibleIndex();
-            if (mFocusPosition == NO_POSITION) {
-                mBaseGridView.clearFocus();
-            }
             mWindowAlignment.mainAxis().invalidateScrollMin();
             mWindowAlignment.mainAxis().invalidateScrollMax();
             if (focusViewWasInTree && firstVisibleIndex <= mFocusPosition) {
@@ -1148,11 +1181,11 @@
         // in RTL flow
         if (mReverseFlowSecondary) {
             for (int i = mNumRows-1; i > rowIndex; i--) {
-                start += getRowSizeSecondary(i) + mMarginSecondary;
+                start += getRowSizeSecondary(i) + mSpacingSecondary;
             }
         } else {
             for (int i = 0; i < rowIndex; i++) {
-                start += getRowSizeSecondary(i) + mMarginSecondary;
+                start += getRowSizeSecondary(i) + mSpacingSecondary;
             }
         }
         return start;
@@ -1199,7 +1232,7 @@
             return false;
         }
 
-        if (TRACE) TraceHelper.beginSection("processRowSizeSecondary");
+        if (TRACE) TraceCompat.beginSection("processRowSizeSecondary");
         CircularIntArray[] rows = mGrid == null ? null : mGrid.getItemPositionsInRows();
         boolean changed = false;
         int scrapChildWidth = -1;
@@ -1221,8 +1254,8 @@
                     if (measure) {
                         measureChild(view);
                     }
-                    final int secondarySize = mOrientation == HORIZONTAL ?
-                            getDecoratedMeasuredHeightWithMargin(view)
+                    final int secondarySize = mOrientation == HORIZONTAL
+                            ? getDecoratedMeasuredHeightWithMargin(view)
                             : getDecoratedMeasuredWidthWithMargin(view);
                     if (secondarySize > rowSize) {
                         rowSize = secondarySize;
@@ -1247,8 +1280,10 @@
                             mMeasuredDimension);
                     scrapChildWidth = mMeasuredDimension[0];
                     scrapChildHeight = mMeasuredDimension[1];
-                    if (DEBUG) Log.v(TAG, "measured scrap child: " + scrapChildWidth +
-                            " " + scrapChildHeight);
+                    if (DEBUG) {
+                        Log.v(TAG, "measured scrap child: " + scrapChildWidth + " "
+                                + scrapChildHeight);
+                    }
                 }
                 rowSize = mOrientation == HORIZONTAL ? scrapChildHeight : scrapChildWidth;
             }
@@ -1256,14 +1291,16 @@
                 rowSize = 0;
             }
             if (mRowSizeSecondary[rowIndex] != rowSize) {
-                if (DEBUG) Log.v(getTag(), "row size secondary changed: " + mRowSizeSecondary[rowIndex] +
-                        ", " + rowSize);
+                if (DEBUG) {
+                    Log.v(getTag(), "row size secondary changed: " + mRowSizeSecondary[rowIndex]
+                            + ", " + rowSize);
+                }
                 mRowSizeSecondary[rowIndex] = rowSize;
                 changed = true;
             }
         }
 
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
         return changed;
     }
 
@@ -1316,10 +1353,12 @@
             modeSecondary = MeasureSpec.getMode(widthSpec);
             paddingSecondary = getPaddingLeft() + getPaddingRight();
         }
-        if (DEBUG) Log.v(getTag(), "onMeasure widthSpec " + Integer.toHexString(widthSpec) +
-                " heightSpec " + Integer.toHexString(heightSpec) +
-                " modeSecondary " + Integer.toHexString(modeSecondary) +
-                " sizeSecondary " + sizeSecondary + " " + this);
+        if (DEBUG) {
+            Log.v(getTag(), "onMeasure widthSpec " + Integer.toHexString(widthSpec)
+                    + " heightSpec " + Integer.toHexString(heightSpec)
+                    + " modeSecondary " + Integer.toHexString(modeSecondary)
+                    + " sizeSecondary " + sizeSecondary + " " + this);
+        }
 
         mMaxSizeSecondary = sizeSecondary;
 
@@ -1351,41 +1390,41 @@
 
         } else {
             switch (modeSecondary) {
-            case MeasureSpec.UNSPECIFIED:
-                mFixedRowSizeSecondary = mRowSizeSecondaryRequested == 0 ?
-                        sizeSecondary - paddingSecondary: mRowSizeSecondaryRequested;
-                mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
-                measuredSizeSecondary = mFixedRowSizeSecondary * mNumRows + mMarginSecondary
-                    * (mNumRows - 1) + paddingSecondary;
-                break;
-            case MeasureSpec.AT_MOST:
-            case MeasureSpec.EXACTLY:
-                if (mNumRowsRequested == 0 && mRowSizeSecondaryRequested == 0) {
-                    mNumRows = 1;
-                    mFixedRowSizeSecondary = sizeSecondary - paddingSecondary;
-                } else if (mNumRowsRequested == 0) {
-                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
-                    mNumRows = (sizeSecondary + mMarginSecondary)
-                        / (mRowSizeSecondaryRequested + mMarginSecondary);
-                } else if (mRowSizeSecondaryRequested == 0) {
-                    mNumRows = mNumRowsRequested;
-                    mFixedRowSizeSecondary = (sizeSecondary - paddingSecondary - mMarginSecondary
-                            * (mNumRows - 1)) / mNumRows;
-                } else {
-                    mNumRows = mNumRowsRequested;
-                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
-                }
-                measuredSizeSecondary = sizeSecondary;
-                if (modeSecondary == MeasureSpec.AT_MOST) {
-                    int childrenSize = mFixedRowSizeSecondary * mNumRows + mMarginSecondary
+                case MeasureSpec.UNSPECIFIED:
+                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested == 0
+                            ? sizeSecondary - paddingSecondary : mRowSizeSecondaryRequested;
+                    mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
+                    measuredSizeSecondary = mFixedRowSizeSecondary * mNumRows + mSpacingSecondary
                         * (mNumRows - 1) + paddingSecondary;
-                    if (childrenSize < measuredSizeSecondary) {
-                        measuredSizeSecondary = childrenSize;
+                    break;
+                case MeasureSpec.AT_MOST:
+                case MeasureSpec.EXACTLY:
+                    if (mNumRowsRequested == 0 && mRowSizeSecondaryRequested == 0) {
+                        mNumRows = 1;
+                        mFixedRowSizeSecondary = sizeSecondary - paddingSecondary;
+                    } else if (mNumRowsRequested == 0) {
+                        mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
+                        mNumRows = (sizeSecondary + mSpacingSecondary)
+                            / (mRowSizeSecondaryRequested + mSpacingSecondary);
+                    } else if (mRowSizeSecondaryRequested == 0) {
+                        mNumRows = mNumRowsRequested;
+                        mFixedRowSizeSecondary = (sizeSecondary - paddingSecondary
+                                - mSpacingSecondary * (mNumRows - 1)) / mNumRows;
+                    } else {
+                        mNumRows = mNumRowsRequested;
+                        mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
                     }
-                }
-                break;
-            default:
-                throw new IllegalStateException("wrong spec");
+                    measuredSizeSecondary = sizeSecondary;
+                    if (modeSecondary == MeasureSpec.AT_MOST) {
+                        int childrenSize = mFixedRowSizeSecondary * mNumRows + mSpacingSecondary
+                                * (mNumRows - 1) + paddingSecondary;
+                        if (childrenSize < measuredSizeSecondary) {
+                            measuredSizeSecondary = childrenSize;
+                        }
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("wrong spec");
             }
         }
         if (mOrientation == HORIZONTAL) {
@@ -1394,24 +1433,25 @@
             setMeasuredDimension(measuredSizeSecondary, sizePrimary);
         }
         if (DEBUG) {
-            Log.v(getTag(), "onMeasure sizePrimary " + sizePrimary +
-                    " measuredSizeSecondary " + measuredSizeSecondary +
-                    " mFixedRowSizeSecondary " + mFixedRowSizeSecondary +
-                    " mNumRows " + mNumRows);
+            Log.v(getTag(), "onMeasure sizePrimary " + sizePrimary
+                    + " measuredSizeSecondary " + measuredSizeSecondary
+                    + " mFixedRowSizeSecondary " + mFixedRowSizeSecondary
+                    + " mNumRows " + mNumRows);
         }
         leaveContext();
     }
 
     void measureChild(View child) {
-        if (TRACE) TraceHelper.beginSection("measureChild");
+        if (TRACE) TraceCompat.beginSection("measureChild");
         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
         calculateItemDecorationsForChild(child, sTempRect);
         int widthUsed = lp.leftMargin + lp.rightMargin + sTempRect.left + sTempRect.right;
         int heightUsed = lp.topMargin + lp.bottomMargin + sTempRect.top + sTempRect.bottom;
 
-        final int secondarySpec = (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) ?
-                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) :
-                MeasureSpec.makeMeasureSpec(mFixedRowSizeSecondary, MeasureSpec.EXACTLY);
+        final int secondarySpec =
+                (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT)
+                        ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+                        : MeasureSpec.makeMeasureSpec(mFixedRowSizeSecondary, MeasureSpec.EXACTLY);
         int widthSpec, heightSpec;
 
         if (mOrientation == HORIZONTAL) {
@@ -1424,13 +1464,15 @@
             widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, widthUsed, lp.width);
         }
         child.measure(widthSpec, heightSpec);
-        if (DEBUG) Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec) +
-                " widthSpec " + Integer.toHexString(widthSpec) +
-                " heightSpec " + Integer.toHexString(heightSpec) +
-                " measuredWidth " + child.getMeasuredWidth() +
-                " measuredHeight " + child.getMeasuredHeight());
+        if (DEBUG) {
+            Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec)
+                    + " widthSpec " + Integer.toHexString(widthSpec)
+                    + " heightSpec " + Integer.toHexString(heightSpec)
+                    + " measuredWidth " + child.getMeasuredWidth()
+                    + " measuredHeight " + child.getMeasuredHeight());
+        }
         if (DEBUG) Log.v(getTag(), "child lp width " + lp.width + " height " + lp.height);
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
     }
 
     /**
@@ -1459,22 +1501,22 @@
 
         @Override
         public int createItem(int index, boolean append, Object[] item) {
-            if (TRACE) TraceHelper.beginSection("createItem");
-            if (TRACE) TraceHelper.beginSection("getview");
+            if (TRACE) TraceCompat.beginSection("createItem");
+            if (TRACE) TraceCompat.beginSection("getview");
             View v = getViewForPosition(index);
-            if (TRACE) TraceHelper.endSection();
+            if (TRACE) TraceCompat.endSection();
             LayoutParams lp = (LayoutParams) v.getLayoutParams();
             RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(v);
             lp.setItemAlignmentFacet((ItemAlignmentFacet)getFacet(vh, ItemAlignmentFacet.class));
             // See recyclerView docs:  we don't need re-add scraped view if it was removed.
             if (!lp.isItemRemoved()) {
-                if (TRACE) TraceHelper.beginSection("addView");
+                if (TRACE) TraceCompat.beginSection("addView");
                 if (append) {
                     addView(v);
                 } else {
                     addView(v, 0);
                 }
-                if (TRACE) TraceHelper.endSection();
+                if (TRACE) TraceCompat.endSection();
                 if (mChildVisibility != -1) {
                     v.setVisibility(mChildVisibility);
                 }
@@ -1541,7 +1583,7 @@
             if (DEBUG) {
                 Log.d(getTag(), "addView " + index + " " + v);
             }
-            if (TRACE) TraceHelper.endSection();
+            if (TRACE) TraceCompat.endSection();
 
             if (index == mGrid.getFirstVisibleIndex()) {
                 if (!mGrid.isReversedFlow()) {
@@ -1569,14 +1611,14 @@
 
         @Override
         public void removeItem(int index) {
-            if (TRACE) TraceHelper.beginSection("removeItem");
+            if (TRACE) TraceCompat.beginSection("removeItem");
             View v = findViewByPosition(index);
             if (mInLayout) {
                 detachAndScrapView(v, mRecycler);
             } else {
                 removeAndRecycleView(v, mRecycler);
             }
-            if (TRACE) TraceHelper.endSection();
+            if (TRACE) TraceCompat.endSection();
         }
 
         @Override
@@ -1595,16 +1637,17 @@
     };
 
     void layoutChild(int rowIndex, View v, int start, int end, int startSecondary) {
-        if (TRACE) TraceHelper.beginSection("layoutChild");
+        if (TRACE) TraceCompat.beginSection("layoutChild");
         int sizeSecondary = mOrientation == HORIZONTAL ? getDecoratedMeasuredHeightWithMargin(v)
                 : getDecoratedMeasuredWidthWithMargin(v);
         if (mFixedRowSizeSecondary > 0) {
             sizeSecondary = Math.min(sizeSecondary, mFixedRowSizeSecondary);
         }
         final int verticalGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int horizontalGravity = (mReverseFlowPrimary || mReverseFlowSecondary) ?
-                Gravity.getAbsoluteGravity(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK, View.LAYOUT_DIRECTION_RTL) :
-                mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int horizontalGravity = (mReverseFlowPrimary || mReverseFlowSecondary)
+                ? Gravity.getAbsoluteGravity(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK,
+                        View.LAYOUT_DIRECTION_RTL)
+                : mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
         if (mOrientation == HORIZONTAL && verticalGravity == Gravity.TOP
                 || mOrientation == VERTICAL && horizontalGravity == Gravity.LEFT) {
             // do nothing
@@ -1636,7 +1679,7 @@
         params.setOpticalInsets(left - sTempRect.left, top - sTempRect.top,
                 sTempRect.right - right, sTempRect.bottom - bottom);
         updateChildAlignments(v);
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
     }
 
     private void updateChildAlignments(View v) {
@@ -1677,14 +1720,14 @@
     }
 
     private void removeInvisibleViewsAtEnd() {
-        if (mPruneChild) {
+        if (mPruneChild && !mIsSlidingChildViews) {
             mGrid.removeInvisibleItemsAtEnd(mFocusPosition,
                     mReverseFlowPrimary ? -mExtraLayoutSpace : mSizePrimary + mExtraLayoutSpace);
         }
     }
 
     private void removeInvisibleViewsAtFront() {
-        if (mPruneChild) {
+        if (mPruneChild && !mIsSlidingChildViews) {
             mGrid.removeInvisibleItemsAtFront(mFocusPosition,
                     mReverseFlowPrimary ? mSizePrimary + mExtraLayoutSpace: -mExtraLayoutSpace);
         }
@@ -1694,6 +1737,10 @@
         return mGrid.appendOneColumnVisibleItems();
     }
 
+    public void setIsSlidingChildViews(boolean animatingChildViews) {
+        this.mIsSlidingChildViews = animatingChildViews;
+    }
+
     private boolean prependOneColumnVisibleItems() {
         return mGrid.prependOneColumnVisibleItems();
     }
@@ -1782,12 +1829,12 @@
 
     @Override
     public void removeAndRecycleAllViews(RecyclerView.Recycler recycler) {
-        if (TRACE) TraceHelper.beginSection("removeAndRecycleAllViews");
+        if (TRACE) TraceCompat.beginSection("removeAndRecycleAllViews");
         if (DEBUG) Log.v(TAG, "removeAndRecycleAllViews " + getChildCount());
         for (int i = getChildCount() - 1; i >= 0; i--) {
             removeAndRecycleViewAt(i, recycler);
         }
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
     }
 
     // called by onLayoutChildren, either focus to FocusPosition or declare focusViewAvailable
@@ -1818,6 +1865,22 @@
         }
     }
 
+    @VisibleForTesting
+    public static class OnLayoutCompleteListener {
+        public void onLayoutCompleted(RecyclerView.State state) {
+        }
+    }
+
+    @VisibleForTesting
+    OnLayoutCompleteListener mLayoutCompleteListener;
+
+    @Override
+    public void onLayoutCompleted(State state) {
+        if (mLayoutCompleteListener != null) {
+            mLayoutCompleteListener.onLayoutCompleted(state);
+        }
+    }
+
     // Lays out items based on the current scroll position
     @Override
     public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
@@ -1911,8 +1974,8 @@
                 prependVisibleItems();
                 removeInvisibleViewsAtFront();
                 removeInvisibleViewsAtEnd();
-            } while (mGrid.getFirstVisibleIndex() != oldFirstVisible ||
-                    mGrid.getLastVisibleIndex() != oldLastVisible);
+            } while (mGrid.getFirstVisibleIndex() != oldFirstVisible
+                    || mGrid.getLastVisibleIndex() != oldLastVisible);
         }
 
         if (scrollToFocus) {
@@ -1938,14 +2001,15 @@
         }
 
         // For fastRelayout, only dispatch event when focus position changes.
-        if (mInFastRelayout && (mFocusPosition != savedFocusPos || mSubFocusPosition !=
-                savedSubFocusPos || findViewByPosition(mFocusPosition) != savedFocusView)) {
+        if (mInFastRelayout && (mFocusPosition != savedFocusPos || mSubFocusPosition
+                != savedSubFocusPos || findViewByPosition(mFocusPosition) != savedFocusView)) {
             dispatchChildSelected();
         } else if (!mInFastRelayout && mInLayoutSearchFocus) {
             // For full layout we dispatchChildSelected() in createItem() unless searched all
             // children and found none is focusable then dispatchChildSelected() here.
             dispatchChildSelected();
         }
+        dispatchChildSelectedAndPositioned();
 
         mInLayout = false;
         leaveContext();
@@ -2018,34 +2082,36 @@
 
     // scroll in main direction may add/prune views
     private int scrollDirectionPrimary(int da) {
-        if (TRACE) TraceHelper.beginSection("scrollPrimary");
+        if (TRACE) TraceCompat.beginSection("scrollPrimary");
         boolean isMaxUnknown = false, isMinUnknown = false;
         int minScroll = 0, maxScroll = 0;
-        if (da > 0) {
-            isMaxUnknown = mWindowAlignment.mainAxis().isMaxUnknown();
-            if (!isMaxUnknown) {
-                maxScroll = mWindowAlignment.mainAxis().getMaxScroll();
-                if (mScrollOffsetPrimary + da > maxScroll) {
-                    da = maxScroll - mScrollOffsetPrimary;
+        if (!mIsSlidingChildViews) {
+            if (da > 0) {
+                isMaxUnknown = mWindowAlignment.mainAxis().isMaxUnknown();
+                if (!isMaxUnknown) {
+                    maxScroll = mWindowAlignment.mainAxis().getMaxScroll();
+                    if (mScrollOffsetPrimary + da > maxScroll) {
+                        da = maxScroll - mScrollOffsetPrimary;
+                    }
                 }
-            }
-        } else if (da < 0) {
-            isMinUnknown = mWindowAlignment.mainAxis().isMinUnknown();
-            if (!isMinUnknown) {
-                minScroll = mWindowAlignment.mainAxis().getMinScroll();
-                if (mScrollOffsetPrimary + da < minScroll) {
-                    da = minScroll - mScrollOffsetPrimary;
+            } else if (da < 0) {
+                isMinUnknown = mWindowAlignment.mainAxis().isMinUnknown();
+                if (!isMinUnknown) {
+                    minScroll = mWindowAlignment.mainAxis().getMinScroll();
+                    if (mScrollOffsetPrimary + da < minScroll) {
+                        da = minScroll - mScrollOffsetPrimary;
+                    }
                 }
             }
         }
         if (da == 0) {
-            if (TRACE) TraceHelper.endSection();
+            if (TRACE) TraceCompat.endSection();
             return 0;
         }
         offsetChildrenPrimary(-da);
         mScrollOffsetPrimary += da;
         if (mInLayout) {
-            if (TRACE) TraceHelper.endSection();
+            if (TRACE) TraceCompat.endSection();
             return da;
         }
 
@@ -2060,20 +2126,20 @@
         updated = getChildCount() > childCount;
         childCount = getChildCount();
 
-        if (TRACE) TraceHelper.beginSection("remove");
+        if (TRACE) TraceCompat.beginSection("remove");
         if (mReverseFlowPrimary ? da > 0 : da < 0) {
             removeInvisibleViewsAtEnd();
         } else {
             removeInvisibleViewsAtFront();
         }
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
         updated |= getChildCount() < childCount;
         if (updated) {
             updateRowSecondarySizeRefresh();
         }
 
         mBaseGridView.invalidate();
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
         return da;
     }
 
@@ -2111,12 +2177,16 @@
         if (highAvailable) {
             mWindowAlignment.mainAxis().setMaxEdge(maxEdge);
             mWindowAlignment.mainAxis().setMaxScroll(maxScroll);
-            if (DEBUG) Log.v(getTag(), "updating scroll maxEdge to " + maxEdge +
-                    " scrollMax to " + maxScroll);
+            if (DEBUG) {
+                Log.v(getTag(), "updating scroll maxEdge to " + maxEdge
+                        + " scrollMax to " + maxScroll);
+            }
         } else {
             mWindowAlignment.mainAxis().invalidateScrollMax();
-            if (DEBUG) Log.v(getTag(), "Invalidate scrollMax since it should be "
-                    + "greater than " + maxScroll);
+            if (DEBUG) {
+                Log.v(getTag(), "Invalidate scrollMax since it should be "
+                        + "greater than " + maxScroll);
+            }
         }
     }
 
@@ -2143,12 +2213,16 @@
         if (lowAvailable) {
             mWindowAlignment.mainAxis().setMinEdge(minEdge);
             mWindowAlignment.mainAxis().setMinScroll(minScroll);
-            if (DEBUG) Log.v(getTag(), "updating scroll minEdge to " + minEdge +
-                    " scrollMin to " + minScroll);
+            if (DEBUG) {
+                Log.v(getTag(), "updating scroll minEdge to " + minEdge
+                        + " scrollMin to " + minScroll);
+            }
         } else {
             mWindowAlignment.mainAxis().invalidateScrollMin();
-            if (DEBUG) Log.v(getTag(), "Invalidate scrollMin, since it should be "
-                    + "less than " + minScroll);
+            if (DEBUG) {
+                Log.v(getTag(), "Invalidate scrollMin, since it should be "
+                        + "less than " + minScroll);
+            }
         }
     }
 
@@ -2245,7 +2319,7 @@
 
     void scrollToSelection(int position, int subposition,
             boolean smooth, int primaryScrollExtra) {
-        if (TRACE) TraceHelper.beginSection("scrollToSelection");
+        if (TRACE) TraceCompat.beginSection("scrollToSelection");
         mPrimaryScrollExtra = primaryScrollExtra;
         View view = findViewByPosition(position);
         if (view != null) {
@@ -2261,8 +2335,8 @@
             }
             if (smooth) {
                 if (!hasDoneFirstLayout()) {
-                    Log.w(getTag(), "setSelectionSmooth should " +
-                            "not be called before first layout pass");
+                    Log.w(getTag(), "setSelectionSmooth should "
+                            + "not be called before first layout pass");
                     return;
                 }
                 startPositionSmoothScroller(position);
@@ -2271,7 +2345,7 @@
                 requestLayout();
             }
         }
-        if (TRACE) TraceHelper.endSection();
+        if (TRACE) TraceCompat.endSection();
     }
 
     void startPositionSmoothScroller(int position) {
@@ -2441,12 +2515,12 @@
         boolean isMin, isMax;
         if (!mReverseFlowPrimary) {
             isMin = mGrid.getFirstVisibleIndex() == 0;
-            isMax = mGrid.getLastVisibleIndex() == (mState == null ?
-                    getItemCount() : mState.getItemCount()) - 1;
+            isMax = mGrid.getLastVisibleIndex() == (mState == null
+                    ? getItemCount() : mState.getItemCount()) - 1;
         } else {
             isMax = mGrid.getFirstVisibleIndex() == 0;
-            isMin = mGrid.getLastVisibleIndex() == (mState == null ?
-                    getItemCount() : mState.getItemCount()) - 1;
+            isMin = mGrid.getLastVisibleIndex() == (mState == null
+                    ? getItemCount() : mState.getItemCount()) - 1;
         }
         for (int i = getChildCount() - 1; (isMin || isMax) && i >= 0; i--) {
             View v = getChildAt(i);
@@ -2669,6 +2743,7 @@
                 mBaseGridView.smoothScrollBy(scrollX, scrollY);
             } else {
                 mBaseGridView.scrollBy(scrollX, scrollY);
+                dispatchChildSelectedAndPositioned();
             }
         }
     }
@@ -2863,8 +2938,8 @@
                 return true;
             }
             // Add focusables of neighbor depending on the focus search direction.
-            final int focusedRow = mGrid != null && focusedPos != NO_POSITION ?
-                    mGrid.getLocation(focusedPos).row : NO_POSITION;
+            final int focusedRow = mGrid != null && focusedPos != NO_POSITION
+                    ? mGrid.getLocation(focusedPos).row : NO_POSITION;
             final int focusableCount = views.size();
             int inc = movement == NEXT_ITEM || movement == NEXT_ROW ? 1 : -1;
             int loop_end = inc > 0 ? getChildCount() - 1 : 0;
@@ -3290,8 +3365,8 @@
             return moves;
         }
         int focusPosition = mFocusPosition;
-        int focusedRow = focusPosition != NO_POSITION ?
-                mGrid.getRowIndex(focusPosition) : NO_POSITION;
+        int focusedRow = focusPosition != NO_POSITION
+                ? mGrid.getRowIndex(focusPosition) : NO_POSITION;
         View newSelected = null;
         for (int i = 0, count = getChildCount(); i < count && moves != 0; i++) {
             int index = moves > 0 ? i : count - 1 - i;
@@ -3346,12 +3421,12 @@
             info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
             info.setScrollable(true);
         }
-        final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
-                = AccessibilityNodeInfoCompat.CollectionInfoCompat
-                .obtain(getRowCountForAccessibility(recycler, state),
-                        getColumnCountForAccessibility(recycler, state),
-                        isLayoutHierarchical(recycler, state),
-                        getSelectionModeForAccessibility(recycler, state));
+        final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
+                AccessibilityNodeInfoCompat.CollectionInfoCompat
+                        .obtain(getRowCountForAccessibility(recycler, state),
+                                getColumnCountForAccessibility(recycler, state),
+                                isLayoutHierarchical(recycler, state),
+                                getSelectionModeForAccessibility(recycler, state));
         info.setCollectionInfo(collectionInfo);
         leaveContext();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
index 2bd7d28..2600470 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
@@ -30,15 +30,15 @@
 
     public GuidanceStylingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        init();
+        mTitleKeylinePercent = getKeyLinePercent(context);
     }
 
-    private void init() {
-        TypedArray ta = getContext().getTheme().obtainStyledAttributes(
+    public static float getKeyLinePercent(Context context) {
+        TypedArray ta = context.getTheme().obtainStyledAttributes(
                 R.styleable.LeanbackGuidedStepTheme);
-        mTitleKeylinePercent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline,
-                40);
+        float percent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline, 40);
         ta.recycle();
+        return percent;
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
index f0cc699..66335f4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
@@ -13,32 +13,24 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.media.AudioManager;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.EditText;
-import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * GuidedActionAdapter instantiates views for guided actions, and manages their interactions.
  * Presentation (view creation and state animation) is delegated to a {@link
@@ -122,8 +114,7 @@
                     mGroup.openIme(GuidedActionAdapter.this, avh);
                 } else if (action.hasEditableActivatorView()) {
                     if (DEBUG_EDIT) Log.v(TAG_EDIT, "toggle editing mode by click");
-                    getGuidedActionsStylist().setEditingMode(avh, avh.getAction(),
-                            !avh.isInEditingActivatorView());
+                    performOnActionClick(avh);
                 } else {
                     handleCheckedActions(avh);
                     if (action.isEnabled() && !action.infoOnly()) {
@@ -469,8 +460,8 @@
         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
             if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME action: " + actionId);
             boolean handled = false;
-            if (actionId == EditorInfo.IME_ACTION_NEXT ||
-                actionId == EditorInfo.IME_ACTION_DONE) {
+            if (actionId == EditorInfo.IME_ACTION_NEXT
+                    || actionId == EditorInfo.IME_ACTION_DONE) {
                 mGroup.fillAndGoNext(GuidedActionAdapter.this, v);
                 handled = true;
             } else if (actionId == EditorInfo.IME_ACTION_NONE) {
@@ -488,8 +479,8 @@
             if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                 mGroup.fillAndStay(GuidedActionAdapter.this, editText);
                 return true;
-            } else if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() ==
-                    KeyEvent.ACTION_UP) {
+            } else if (keyCode == KeyEvent.KEYCODE_ENTER
+                    && event.getAction() == KeyEvent.ACTION_UP) {
                 mGroup.fillAndGoNext(GuidedActionAdapter.this, editText);
                 return true;
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java
index dafa49b..075232a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java
@@ -124,7 +124,7 @@
     }
 
     public void openIme(GuidedActionAdapter adapter, GuidedActionsStylist.ViewHolder avh) {
-        adapter.getGuidedActionsStylist().setEditingMode(avh, avh.getAction(), true);
+        adapter.getGuidedActionsStylist().setEditingMode(avh, true);
         View v = avh.getEditingView();
         if (v == null || !avh.isInEditingText()) {
             return;
@@ -156,7 +156,7 @@
         GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v);
         updateTextIntoAction(avh, v);
         mEditListener.onGuidedActionEditCanceled(avh.getAction());
-        adapter.getGuidedActionsStylist().setEditingMode(avh, avh.getAction(), false);
+        adapter.getGuidedActionsStylist().setEditingMode(avh, false);
         closeIme(v);
         avh.itemView.requestFocus();
     }
@@ -167,7 +167,7 @@
         updateTextIntoAction(avh, v);
         adapter.performOnActionClick(avh);
         long nextActionId = mEditListener.onGuidedActionEditedAndProceed(avh.getAction());
-        adapter.getGuidedActionsStylist().setEditingMode(avh, avh.getAction(), false);
+        adapter.getGuidedActionsStylist().setEditingMode(avh, false);
         if (nextActionId != GuidedAction.ACTION_ID_CURRENT
                 && nextActionId != avh.getAction().getId()) {
             handled = focusToNextAction(adapter, avh.getAction(), nextActionId);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java
new file mode 100644
index 0000000..6870b48
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java
@@ -0,0 +1,85 @@
+package android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v17.leanback.R;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+/**
+ * Relative layout implementation that assign subactions list topMargin based on a percentage
+ * given by "guidedStepKeyline" theme attribute when the topMargin is set to a negative value.
+ */
+class GuidedActionsRelativeLayout extends RelativeLayout {
+
+    interface InterceptKeyEventListener {
+        public boolean onInterceptKeyEvent(KeyEvent event);
+    }
+
+    private float mKeyLinePercent;
+    private boolean mInOverride = false;
+    private InterceptKeyEventListener mInterceptKeyEventListener;
+
+    public GuidedActionsRelativeLayout(Context context) {
+        this(context, null);
+    }
+
+    public GuidedActionsRelativeLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GuidedActionsRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mKeyLinePercent = GuidanceStylingRelativeLayout.getKeyLinePercent(context);
+    }
+
+    private void init() {
+        TypedArray ta = getContext().getTheme().obtainStyledAttributes(
+                R.styleable.LeanbackGuidedStepTheme);
+        mKeyLinePercent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline,
+                40);
+        ta.recycle();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+        if (heightSize > 0) {
+            View view = findViewById(R.id.guidedactions_sub_list);
+            if (view != null) {
+                ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
+                        view.getLayoutParams();
+                if (lp.topMargin < 0 && !mInOverride) {
+                    mInOverride = true;
+                }
+                if (mInOverride) {
+                    lp.topMargin = (int) (mKeyLinePercent * heightSize / 100);
+                }
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        mInOverride = false;
+    }
+
+    public void setInterceptKeyEventListener(InterceptKeyEventListener l) {
+        mInterceptKeyEventListener = l;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mInterceptKeyEventListener != null) {
+            if (mInterceptKeyEventListener.onInterceptKeyEvent(event)) {
+                return true;
+            }
+        }
+        return super.dispatchKeyEvent(event);
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index 9c0b43e..faf537d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -13,15 +13,24 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import static android.support.v17.leanback.widget.GuidedAction.EDITING_ACTIVATOR_VIEW;
+import static android.support.v17.leanback.widget.GuidedAction.EDITING_DESCRIPTION;
+import static android.support.v17.leanback.widget.GuidedAction.EDITING_NONE;
+import static android.support.v17.leanback.widget.GuidedAction.EDITING_TITLE;
+
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build.VERSION;
+import android.support.annotation.CallSuper;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionEpicenterCallback;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.transition.TransitionListener;
 import android.support.v17.leanback.widget.GuidedActionAdapter.EditListener;
@@ -31,6 +40,7 @@
 import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
@@ -48,12 +58,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_ACTIVATOR_VIEW;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_DESCRIPTION;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_NONE;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_TITLE;
-
 /**
  * GuidedActionsStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
  * to supply the right-side panel where users can take actions. It consists of a container for the
@@ -389,6 +393,10 @@
 
     private GuidedAction mExpandedAction = null;
     Object mExpandTransition;
+    private boolean mBackToCollapseSubActions = true;
+    private boolean mBackToCollapseActivatorView = true;
+
+    private float mKeyLinePercent;
 
     /**
      * Creates a view appropriate for displaying a list of GuidedActions, using the provided
@@ -414,8 +422,8 @@
         if (mMainView instanceof VerticalGridView) {
             mActionsGridView = (VerticalGridView) mMainView;
         } else {
-            mActionsGridView = (VerticalGridView) mMainView.findViewById(mButtonActions ?
-                    R.id.guidedactions_list2 : R.id.guidedactions_list);
+            mActionsGridView = (VerticalGridView) mMainView.findViewById(mButtonActions
+                    ? R.id.guidedactions_list2 : R.id.guidedactions_list);
             if (mActionsGridView == null) {
                 throw new IllegalStateException("No ListView exists.");
             }
@@ -451,6 +459,29 @@
                 .lb_guidedactions_item_unselected_description_text_alpha));
         mDisabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
                 .lb_guidedactions_item_disabled_description_text_alpha));
+
+        mKeyLinePercent = GuidanceStylingRelativeLayout.getKeyLinePercent(ctx);
+        if (mContentView instanceof GuidedActionsRelativeLayout) {
+            ((GuidedActionsRelativeLayout) mContentView).setInterceptKeyEventListener(
+                    new GuidedActionsRelativeLayout.InterceptKeyEventListener() {
+                        @Override
+                        public boolean onInterceptKeyEvent(KeyEvent event) {
+                            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+                                    && event.getAction() == KeyEvent.ACTION_UP
+                                    && mExpandedAction != null) {
+                                if ((mExpandedAction.hasSubActions()
+                                        && isBackKeyToCollapseSubActions())
+                                        || (mExpandedAction.hasEditableActivatorView()
+                                        && isBackKeyToCollapseActivatorView())) {
+                                    collapseAction(true);
+                                    return true;
+                                }
+                            }
+                            return false;
+                        }
+                    }
+            );
+        }
         return mMainView;
     }
 
@@ -571,8 +602,8 @@
         } else if (viewType == VIEW_TYPE_DATE_PICKER) {
             return R.layout.lb_guidedactions_datepicker_item;
         } else {
-            throw new RuntimeException("ViewType " + viewType +
-                    " not supported in GuidedActionsStylist");
+            throw new RuntimeException("ViewType " + viewType
+                    + " not supported in GuidedActionsStylist");
         }
     }
 
@@ -629,8 +660,8 @@
         }
         if (vh.mDescriptionView != null) {
             vh.mDescriptionView.setText(action.getDescription());
-            vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
-                    View.GONE : View.VISIBLE);
+            vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
+                    ? View.GONE : View.VISIBLE);
             vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha :
                 mDisabledDescriptionAlpha);
             vh.mDescriptionView.setFocusable(false);
@@ -662,7 +693,7 @@
         if (vh.mActivatorView != null) {
             onBindActivatorView(vh, action);
         }
-        setEditingMode(vh, action, false);
+        setEditingMode(vh, false /*editing*/, false /*withTransition*/);
         if (action.isFocusable()) {
             vh.itemView.setFocusable(true);
             ((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
@@ -703,14 +734,34 @@
         }
     }
 
-    public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) {
+    void setEditingMode(ViewHolder vh, boolean editing) {
+        setEditingMode(vh, editing, true /*withTransition*/);
+    }
+
+    void setEditingMode(ViewHolder vh, boolean editing, boolean withTransition) {
         if (editing != vh.isInEditing() && !isInExpandTransition()) {
-            onEditingModeChange(vh, action, editing);
+            onEditingModeChange(vh, editing, withTransition);
         }
     }
 
+    /**
+     * @deprecated Use {@link #onEditingModeChange(ViewHolder, boolean, boolean)}.
+     */
+    @Deprecated
     protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) {
-        action = vh.getAction();
+    }
+
+    /**
+     * Called when editing mode of an ViewHolder is changed.  Subclass must call
+     * <code>super.onEditingModeChange(vh,editing,withTransition)</code>.
+     *
+     * @param vh                ViewHolder to change editing mode.
+     * @param editing           True to enable editing, false to stop editing
+     * @param withTransition    True to run expand transiiton, false otherwise.
+     */
+    @CallSuper
+    protected void onEditingModeChange(ViewHolder vh, boolean editing, boolean withTransition) {
+        GuidedAction action = vh.getAction();
         TextView titleView = vh.getTitleView();
         TextView descriptionView = vh.getDescriptionView();
         if (editing) {
@@ -734,7 +785,7 @@
                 }
                 vh.mEditingMode = EDITING_TITLE;
             } else if (vh.mActivatorView != null) {
-                onEditActivatorView(vh, action, editing);
+                onEditActivatorView(vh, editing, withTransition);
                 vh.mEditingMode = EDITING_ACTIVATOR_VIEW;
             }
         } else {
@@ -746,8 +797,8 @@
             }
             if (vh.mEditingMode == EDITING_DESCRIPTION) {
                 if (descriptionView != null) {
-                    descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
-                            View.GONE : View.VISIBLE);
+                    descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
+                            ? View.GONE : View.VISIBLE);
                     descriptionView.setInputType(action.getDescriptionInputType());
                 }
             } else if (vh.mEditingMode == EDITING_TITLE) {
@@ -756,11 +807,13 @@
                 }
             } else if (vh.mEditingMode == EDITING_ACTIVATOR_VIEW) {
                 if (vh.mActivatorView != null) {
-                    onEditActivatorView(vh, action, editing);
+                    onEditActivatorView(vh, editing, withTransition);
                 }
             }
             vh.mEditingMode = EDITING_NONE;
         }
+        // call deprecated method for backward compatible
+        onEditingModeChange(vh, action, editing);
     }
 
     /**
@@ -827,9 +880,9 @@
     public void onBindCheckMarkView(ViewHolder vh, GuidedAction action) {
         if (action.getCheckSetId() != GuidedAction.NO_CHECK_SET) {
             vh.mCheckmarkView.setVisibility(View.VISIBLE);
-            int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID ?
-                    android.R.attr.listChoiceIndicatorMultiple :
-                    android.R.attr.listChoiceIndicatorSingle;
+            int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
+                    ? android.R.attr.listChoiceIndicatorMultiple
+                    : android.R.attr.listChoiceIndicatorSingle;
             final Context context = vh.mCheckmarkView.getContext();
             Drawable drawable = null;
             TypedValue typedValue = new TypedValue();
@@ -897,29 +950,29 @@
         mEditListener = listener;
     }
 
-    void onEditActivatorView(final ViewHolder vh, final GuidedAction action,
-            boolean editing) {
+    void onEditActivatorView(final ViewHolder vh, boolean editing, final boolean withTransition) {
         if (editing) {
             vh.itemView.setFocusable(false);
             vh.mActivatorView.requestFocus();
-            setExpandedViewHolder(vh);
+            startExpanded(vh, withTransition);
             vh.mActivatorView.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     if (!isInExpandTransition()) {
-                        setEditingMode(vh, action, false);
+                        ((GuidedActionAdapter) getActionsGridView().getAdapter())
+                                .performOnActionClick(vh);
                     }
                 }
             });
         } else {
-            if (onUpdateActivatorView(vh, action)) {
+            if (onUpdateActivatorView(vh, vh.getAction())) {
                 if (mEditListener != null) {
-                    mEditListener.onGuidedActionEditedAndProceed(action);
+                    mEditListener.onGuidedActionEditedAndProceed(vh.getAction());
                 }
             }
             vh.itemView.setFocusable(true);
             vh.itemView.requestFocus();
-            setExpandedViewHolder(null);
+            startExpanded(null, withTransition);
             vh.mActivatorView.setOnClickListener(null);
             vh.mActivatorView.setClickable(false);
         }
@@ -955,19 +1008,15 @@
     }
 
     /**
-     * Expands or collapse the sub actions list view.
+     * Expands or collapse the sub actions list view with transition animation
      * @param avh When not null, fill sub actions list of this ViewHolder into sub actions list and
      * hide the other items in main list.  When null, collapse the sub actions list.
+     * @deprecated use {@link #expandAction(GuidedAction, boolean)} and
+     * {@link #collapseAction(boolean)}
      */
+    @Deprecated
     public void setExpandedViewHolder(ViewHolder avh) {
-        if (isInExpandTransition()) {
-            return;
-        }
-        if (isExpandTransitionSupported()) {
-            startExpandedTransition(avh);
-        } else {
-            onUpdateExpandedViewHolder(avh);
-        }
+        expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
     }
 
     /**
@@ -992,8 +1041,144 @@
      * Start transition to expand or collapse GuidedActionStylist.
      * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
      * the GuidedActionStylist will collapse sub actions.
+     * @deprecated use {@link #expandAction(GuidedAction, boolean)} and
+     * {@link #collapseAction(boolean)}
      */
+    @Deprecated
     public void startExpandedTransition(ViewHolder avh) {
+        expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
+    }
+
+    /**
+     * Enable or disable using BACK key to collapse sub actions list. Default is enabled.
+     *
+     * @param backToCollapse True to enable using BACK key to collapse sub actions list, false
+     *                       to disable.
+     * @see GuidedAction#hasSubActions
+     * @see GuidedAction#getSubActions
+     */
+    public final void setBackKeyToCollapseSubActions(boolean backToCollapse) {
+        mBackToCollapseSubActions = backToCollapse;
+    }
+
+    /**
+     * @return True if using BACK key to collapse sub actions list, false otherwise. Default value
+     * is true.
+     *
+     * @see GuidedAction#hasSubActions
+     * @see GuidedAction#getSubActions
+     */
+    public final boolean isBackKeyToCollapseSubActions() {
+        return mBackToCollapseSubActions;
+    }
+
+    /**
+     * Enable or disable using BACK key to collapse {@link GuidedAction} with editable activator
+     * view. Default is enabled.
+     *
+     * @param backToCollapse True to enable using BACK key to collapse {@link GuidedAction} with
+     *                       editable activator view.
+     * @see GuidedAction#hasEditableActivatorView
+     */
+    public final void setBackKeyToCollapseActivatorView(boolean backToCollapse) {
+        mBackToCollapseActivatorView = backToCollapse;
+    }
+
+    /**
+     * @return True if using BACK key to collapse {@link GuidedAction} with editable activator
+     * view, false otherwise. Default value is true.
+     *
+     * @see GuidedAction#hasEditableActivatorView
+     */
+    public final boolean isBackKeyToCollapseActivatorView() {
+        return mBackToCollapseActivatorView;
+    }
+
+    /**
+     * Expand an action. Do nothing if it is in animation or there is action expanded.
+     *
+     * @param action         Action to expand.
+     * @param withTransition True to run transition animation, false otherwsie.
+     */
+    public void expandAction(GuidedAction action, final boolean withTransition) {
+        if (isInExpandTransition() || mExpandedAction != null) {
+            return;
+        }
+        int actionPosition =
+                ((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(action);
+        if (actionPosition < 0) {
+            return;
+        }
+        boolean runTransition = isExpandTransitionSupported() && withTransition;
+        if (!runTransition) {
+            getActionsGridView().setSelectedPosition(actionPosition,
+                    new ViewHolderTask() {
+                        @Override
+                        public void run(RecyclerView.ViewHolder vh) {
+                            GuidedActionsStylist.ViewHolder avh =
+                                    (GuidedActionsStylist.ViewHolder)vh;
+                            if (avh.getAction().hasEditableActivatorView()) {
+                                setEditingMode(avh, true /*editing*/, false /*withTransition*/);
+                            } else {
+                                onUpdateExpandedViewHolder(avh);
+                            }
+                        }
+                    });
+            if (action.hasSubActions()) {
+                onUpdateSubActionsGridView(action, true);
+            }
+        } else {
+            getActionsGridView().setSelectedPosition(actionPosition,
+                    new ViewHolderTask() {
+                        @Override
+                        public void run(RecyclerView.ViewHolder vh) {
+                            GuidedActionsStylist.ViewHolder avh =
+                                    (GuidedActionsStylist.ViewHolder)vh;
+                            if (avh.getAction().hasEditableActivatorView()) {
+                                setEditingMode(avh, true /*editing*/, true /*withTransition*/);
+                            } else {
+                                startExpanded(avh, true);
+                            }
+                        }
+                    });
+        }
+
+    }
+
+    /**
+     * Collapse expanded action. Do nothing if it is in animation or there is no action expanded.
+     *
+     * @param withTransition True to run transition animation, false otherwsie.
+     */
+    public void collapseAction(boolean withTransition) {
+        if (isInExpandTransition() || mExpandedAction == null) {
+            return;
+        }
+        boolean runTransition = isExpandTransitionSupported() && withTransition;
+        int actionPosition =
+                ((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(mExpandedAction);
+        if (actionPosition < 0) {
+            return;
+        }
+        if (mExpandedAction.hasEditableActivatorView()) {
+            setEditingMode(
+                    ((ViewHolder) getActionsGridView().findViewHolderForPosition(actionPosition)),
+                    false /*editing*/,
+                    runTransition);
+        } else {
+            startExpanded(null, runTransition);
+        }
+    }
+
+    int getKeyLine() {
+        return (int) (mKeyLinePercent * mActionsGridView.getHeight() / 100);
+    }
+
+    /**
+     * Internal method with assumption we already scroll to the new ViewHolder or is currently
+     * expanded.
+     */
+    void startExpanded(ViewHolder avh, final boolean withTransition) {
         ViewHolder focusAvh = null; // expand / collapse view holder
         final int count = mActionsGridView.getChildCount();
         for (int i = 0; i < count; i++) {
@@ -1011,102 +1196,101 @@
         }
         if (focusAvh == null) {
             // huh?
-            onUpdateExpandedViewHolder(avh);
             return;
         }
+        boolean isExpand = avh != null;
         boolean isSubActionTransition = focusAvh.getAction().hasSubActions();
-        Object set = TransitionHelper.createTransitionSet(false);
-        float slideDistance = isSubActionTransition ? focusAvh.itemView.getHeight() :
-                focusAvh.itemView.getHeight() * 0.5f;
-        Object slideAndFade = TransitionHelper.createFadeAndShortSlide(Gravity.TOP | Gravity.BOTTOM,
-                slideDistance);
-        Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
-        Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
-        Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
-                TransitionHelper.FADE_OUT);
-        Object changeGridBounds = TransitionHelper.createChangeBounds(false);
-        if (avh == null) {
-            TransitionHelper.setStartDelay(slideAndFade, 150);
-            TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
-            TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
-        } else {
-            TransitionHelper.setStartDelay(fade, 100);
-            TransitionHelper.setStartDelay(changeGridBounds, 100);
-            TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
-            TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
-        }
-        for (int i = 0; i < count; i++) {
-            ViewHolder vh = (ViewHolder) mActionsGridView
-                    .getChildViewHolder(mActionsGridView.getChildAt(i));
-            if (vh == focusAvh) {
-                // going to expand/collapse this one.
-                if (isSubActionTransition) {
-                    TransitionHelper.include(changeFocusItemTransform, vh.itemView);
-                    TransitionHelper.include(changeFocusItemBounds, vh.itemView);
-                }
-            } else {
-                // going to slide this item to top / bottom.
-                TransitionHelper.include(slideAndFade, vh.itemView);
-                TransitionHelper.exclude(fade, vh.itemView, true);
-            }
-        }
-        TransitionHelper.include(changeGridBounds, mSubActionsGridView);
-        TransitionHelper.include(changeGridBounds, mSubActionsBackground);
-        TransitionHelper.addTransition(set, slideAndFade);
-        // note that we don't run ChangeBounds for activating view due to the rounding problem
-        // of multiple level views ChangeBounds animation causing vertical jittering.
-        if (isSubActionTransition) {
-            TransitionHelper.addTransition(set, changeFocusItemTransform);
-            TransitionHelper.addTransition(set, changeFocusItemBounds);
-        }
-        TransitionHelper.addTransition(set, fade);
-        TransitionHelper.addTransition(set, changeGridBounds);
-        mExpandTransition = set;
-        TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
-            @Override
-            public void onTransitionEnd(Object transition) {
-                mExpandTransition = null;
-            }
-        });
-        if (avh != null && mSubActionsGridView.getTop() != avh.itemView.getTop()) {
-            // For expanding, set the initial position of subActionsGridView before running
-            // a ChangeBounds on it.
-            final ViewHolder toUpdate = avh;
-            mSubActionsGridView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+        if (withTransition) {
+            Object set = TransitionHelper.createTransitionSet(false);
+            float slideDistance = isSubActionTransition ? focusAvh.itemView.getHeight()
+                    : focusAvh.itemView.getHeight() * 0.5f;
+            Object slideAndFade = TransitionHelper.createFadeAndShortSlide(
+                    Gravity.TOP | Gravity.BOTTOM,
+                    slideDistance);
+            TransitionHelper.setEpicenterCallback(slideAndFade, new TransitionEpicenterCallback() {
+                Rect mRect = new Rect();
                 @Override
-                public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                    if (mSubActionsGridView == null) {
-                        return;
-                    }
-                    mSubActionsGridView.removeOnLayoutChangeListener(this);
-                    mMainView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (mMainView == null) {
-                                return;
-                            }
-                            TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
-                            onUpdateExpandedViewHolder(toUpdate);
-                        }
-                    });
+                public Rect onGetEpicenter(Object transition) {
+                    int centerY = getKeyLine();
+                    int centerX = 0;
+                    mRect.set(centerX, centerY, centerX, centerY);
+                    return mRect;
                 }
             });
-            ViewGroup.MarginLayoutParams lp =
-                    (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
-            lp.topMargin = avh.itemView.getTop();
-            lp.height = 0;
-            mSubActionsGridView.setLayoutParams(lp);
-            return;
+            Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
+            Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
+            Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN
+                    | TransitionHelper.FADE_OUT);
+            Object changeGridBounds = TransitionHelper.createChangeBounds(false);
+            if (avh == null) {
+                TransitionHelper.setStartDelay(slideAndFade, 150);
+                TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
+                TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
+                TransitionHelper.setStartDelay(changeGridBounds, 100);
+            } else {
+                TransitionHelper.setStartDelay(fade, 100);
+                TransitionHelper.setStartDelay(changeGridBounds, 50);
+                TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
+                TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
+            }
+            for (int i = 0; i < count; i++) {
+                ViewHolder vh = (ViewHolder) mActionsGridView
+                        .getChildViewHolder(mActionsGridView.getChildAt(i));
+                if (vh == focusAvh) {
+                    // going to expand/collapse this one.
+                    if (isSubActionTransition) {
+                        TransitionHelper.include(changeFocusItemTransform, vh.itemView);
+                        TransitionHelper.include(changeFocusItemBounds, vh.itemView);
+                    }
+                } else {
+                    // going to slide this item to top / bottom.
+                    TransitionHelper.include(slideAndFade, vh.itemView);
+                    TransitionHelper.exclude(fade, vh.itemView, true);
+                }
+            }
+            TransitionHelper.include(changeGridBounds, mSubActionsGridView);
+            TransitionHelper.include(changeGridBounds, mSubActionsBackground);
+            TransitionHelper.addTransition(set, slideAndFade);
+            // note that we don't run ChangeBounds for activating view due to the rounding problem
+            // of multiple level views ChangeBounds animation causing vertical jittering.
+            if (isSubActionTransition) {
+                TransitionHelper.addTransition(set, changeFocusItemTransform);
+                TransitionHelper.addTransition(set, changeFocusItemBounds);
+            }
+            TransitionHelper.addTransition(set, fade);
+            TransitionHelper.addTransition(set, changeGridBounds);
+            mExpandTransition = set;
+            TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
+                @Override
+                public void onTransitionEnd(Object transition) {
+                    mExpandTransition = null;
+                }
+            });
+            if (isExpand && isSubActionTransition) {
+                // To expand sub actions, move original position of sub actions to bottom of item
+                int startY = avh.itemView.getBottom();
+                mSubActionsGridView.offsetTopAndBottom(startY - mSubActionsGridView.getTop());
+                mSubActionsBackground.offsetTopAndBottom(startY - mSubActionsBackground.getTop());
+            }
+            TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
         }
-        TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
         onUpdateExpandedViewHolder(avh);
+        if (isSubActionTransition) {
+            onUpdateSubActionsGridView(focusAvh.getAction(), isExpand);
+        }
     }
 
     /**
      * @return True if sub actions list is expanded.
      */
     public boolean isSubActionsExpanded() {
+        return mExpandedAction != null && mExpandedAction.hasSubActions();
+    }
+
+    /**
+     * @return True if there is {@link #getExpandedAction()} is not null, false otherwise.
+     */
+    public boolean isExpanded() {
         return mExpandedAction != null;
     }
 
@@ -1147,28 +1331,35 @@
                     .getChildViewHolder(mActionsGridView.getChildAt(i));
             updateChevronAndVisibility(vh);
         }
+    }
+
+    void onUpdateSubActionsGridView(GuidedAction action, boolean expand) {
         if (mSubActionsGridView != null) {
-            if (avh != null && avh.getAction().hasSubActions()) {
-                ViewGroup.MarginLayoutParams lp =
-                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
-                lp.topMargin = avh.itemView.getTop();
+            ViewGroup.MarginLayoutParams lp =
+                    (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+            GuidedActionAdapter adapter = (GuidedActionAdapter) mSubActionsGridView.getAdapter();
+            if (expand) {
+                // set to negative value so GuidedActionRelativeLayout will override with
+                // keyLine percentage.
+                lp.topMargin = -2;
                 lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
                 mSubActionsGridView.setLayoutParams(lp);
                 mSubActionsGridView.setVisibility(View.VISIBLE);
                 mSubActionsBackground.setVisibility(View.VISIBLE);
                 mSubActionsGridView.requestFocus();
-                mSubActionsGridView.setSelectedPosition(0);
-                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
-                        .setActions(avh.getAction().getSubActions());
-            } else if (mSubActionsGridView.getVisibility() == View.VISIBLE) {
+                adapter.setActions(action.getSubActions());
+            } else {
+                // set to explicit value, which will disable the keyLine percentage calculation
+                // in GuidedRelativeLayout.
+                int actionPosition = ((GuidedActionAdapter) mActionsGridView.getAdapter())
+                        .indexOf(action);
+                lp.topMargin = mActionsGridView.getLayoutManager()
+                        .findViewByPosition(actionPosition).getBottom();
+                lp.height = 0;
                 mSubActionsGridView.setVisibility(View.INVISIBLE);
                 mSubActionsBackground.setVisibility(View.INVISIBLE);
-                ViewGroup.MarginLayoutParams lp =
-                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
-                lp.height = 0;
                 mSubActionsGridView.setLayoutParams(lp);
-                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
-                        .setActions(Collections.EMPTY_LIST);
+                adapter.setActions(Collections.EMPTY_LIST);
                 mActionsGridView.requestFocus();
             }
         }
@@ -1185,7 +1376,7 @@
             } else if (vh.getAction() == mExpandedAction) {
                 vh.itemView.setVisibility(View.VISIBLE);
                 if (vh.getAction().hasSubActions()) {
-                    vh.itemView.setTranslationY(- vh.itemView.getHeight());
+                    vh.itemView.setTranslationY(getKeyLine() - vh.itemView.getBottom());
                 } else if (vh.mActivatorView != null) {
                     vh.itemView.setTranslationY(0);
                     vh.setActivated(true);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HeaderItem.java b/v17/leanback/src/android/support/v17/leanback/widget/HeaderItem.java
index f4e5954..00c4660 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HeaderItem.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HeaderItem.java
@@ -23,6 +23,7 @@
 
     private final long mId;
     private final String mName;
+    private CharSequence mDescription;
     private CharSequence mContentDescription;
 
     /**
@@ -70,4 +71,19 @@
     public void setContentDescription(CharSequence contentDescription) {
         mContentDescription = contentDescription;
     }
+
+    /**
+     * Sets the description for the current header item. This will be visible when
+     * the row receives focus.
+     */
+    public void setDescription(CharSequence description) {
+        this.mDescription = description;
+    }
+
+    /**
+     * Returns the description for the current row.
+     */
+    public CharSequence getDescription() {
+        return mDescription;
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
index 6bdae61..4d6a7dc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
@@ -255,8 +255,7 @@
         final int c = getChildCount();
         for (int i = 0; i < c; i++) {
             View view = getChildAt(i);
-            if (mLayoutManager.getOpticalLeft(view) <
-                    getPaddingLeft() - mLowFadeShaderOffset) {
+            if (mLayoutManager.getOpticalLeft(view) < getPaddingLeft() - mLowFadeShaderOffset) {
                 return true;
             }
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
index f85af1e..eff822b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
@@ -16,8 +16,8 @@
 import android.graphics.Rect;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
 
 /**
@@ -41,8 +41,7 @@
 
     @Override
     protected void onViewSelected(View view) {
-        int rightLimit = getParentViewGroup().getWidth() -
-                getParentViewGroup().getPaddingRight();
+        int rightLimit = getParentViewGroup().getWidth() - getParentViewGroup().getPaddingRight();
         int leftLimit = getParentViewGroup().getPaddingLeft();
         // measure the hover card width; if it's too large, align hover card
         // end edge with row view's end edge, otherwise align start edges.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
index cc51d54..f1d2b95 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignmentFacet.java
@@ -13,11 +13,8 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.view.View;
-
-import static android.support.v17.leanback.widget.BaseGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
-
 import android.support.v7.widget.RecyclerView;
+import android.view.View;
 
 /**
  * Optional facet provided by {@link RecyclerView.Adapter} or {@link RecyclerView.ViewHolder} for
@@ -92,8 +89,8 @@
          * to disable.
          */
         public final void setItemAlignmentOffsetPercent(float percent) {
-            if ( (percent < 0 || percent > 100) &&
-                    percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
+            if ((percent < 0 || percent > 100)
+                    && percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
                 throw new IllegalArgumentException();
             }
             mOffsetPercent = percent;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
index d2d3356..eba207e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapter.java
@@ -243,8 +243,8 @@
 
     @Override
     public int getItemViewType(int position) {
-        PresenterSelector presenterSelector = mPresenterSelector != null ?
-                mPresenterSelector : mAdapter.getPresenterSelector();
+        PresenterSelector presenterSelector = mPresenterSelector != null
+                ? mPresenterSelector : mAdapter.getPresenterSelector();
         Object item = mAdapter.get(position);
         Presenter presenter = presenterSelector.getPresenter(item);
         int type = mPresenters.indexOf(presenter);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index 200b6d9..790cee3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -435,16 +435,16 @@
         });
         rowViewHolder.mGridView.setOnUnhandledKeyListener(
                 new BaseGridView.OnUnhandledKeyListener() {
-            @Override
-            public boolean onUnhandledKey(KeyEvent event) {
-                if (rowViewHolder.getOnKeyListener() != null &&
-                        rowViewHolder.getOnKeyListener().onKey(
-                                rowViewHolder.view, event.getKeyCode(), event)) {
-                    return true;
-                }
-                return false;
-            }
-        });
+                    @Override
+                    public boolean onUnhandledKey(KeyEvent event) {
+                        if (rowViewHolder.getOnKeyListener() != null
+                                && rowViewHolder.getOnKeyListener().onKey(
+                                        rowViewHolder.view, event.getKeyCode(), event)) {
+                            return true;
+                        }
+                        return false;
+                    }
+                });
         rowViewHolder.mGridView.setNumRows(mNumRows);
     }
 
@@ -538,10 +538,10 @@
         if (vh.isExpanded()) {
             int headerSpaceUnderBaseline = getSpaceUnderBaseline(vh);
             if (DEBUG) Log.v(TAG, "headerSpaceUnderBaseline " + headerSpaceUnderBaseline);
-            paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop) -
-                    headerSpaceUnderBaseline;
-            paddingBottom = mHoverCardPresenterSelector == null ?
-                    sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom;
+            paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop)
+                    - headerSpaceUnderBaseline;
+            paddingBottom = mHoverCardPresenterSelector == null
+                    ? sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom;
         } else if (vh.isSelected()) {
             paddingTop = sSelectedRowTopPadding - vh.mPaddingBottom;
             paddingBottom = sSelectedRowTopPadding;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/MediaItemActionPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/MediaItemActionPresenter.java
index 2cb8174..1a98059 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/MediaItemActionPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/MediaItemActionPresenter.java
@@ -48,9 +48,8 @@
     @Override
     public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
         Context context = parent.getContext();
-        View actionView = LayoutInflater.from(context).
-                inflate(R.layout.lb_row_media_item_action, parent,
-                        false);
+        View actionView = LayoutInflater.from(context)
+                .inflate(R.layout.lb_row_media_item_action, parent, false);
         return new ViewHolder(actionView);
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/MediaRowFocusView.java b/v17/leanback/src/android/support/v17/leanback/widget/MediaRowFocusView.java
index 6ec93f2..1418a2a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/MediaRowFocusView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/MediaRowFocusView.java
@@ -23,6 +23,7 @@
 
 /**
  * Creates a view for a media item row in a playlist
+ * @hide
  */
 class MediaRowFocusView extends View {
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ObjectAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ObjectAdapter.java
index 026d9a0..ff10028 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ObjectAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ObjectAdapter.java
@@ -179,18 +179,39 @@
         mObservable.unregisterAll();
     }
 
-    final protected void notifyItemRangeChanged(int positionStart, int itemCount) {
+    /**
+     * Notifies UI that some items has changed.
+     *
+     * @param positionStart Starting position of the changed items.
+     * @param itemCount Total number of items that changed.
+     */
+    public final void notifyItemRangeChanged(int positionStart, int itemCount) {
         mObservable.notifyItemRangeChanged(positionStart, itemCount);
     }
 
+    /**
+     * Notifies UI that new items has been inserted.
+     *
+     * @param positionStart Position where new items has been inserted.
+     * @param itemCount Count of the new items has been inserted.
+     */
     final protected void notifyItemRangeInserted(int positionStart, int itemCount) {
         mObservable.notifyItemRangeInserted(positionStart, itemCount);
     }
 
+    /**
+     * Notifies UI that some items that has been removed.
+     *
+     * @param positionStart Starting position of the removed items.
+     * @param itemCount Total number of items that has been removed.
+     */
     final protected void notifyItemRangeRemoved(int positionStart, int itemCount) {
         mObservable.notifyItemRangeRemoved(positionStart, itemCount);
     }
 
+    /**
+     * Notifies UI that the underlying data has changed.
+     */
     final protected void notifyChanged() {
         mObservable.notifyChanged();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnActionClickedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnActionClickedListener.java
index 2d59f3b..d065119 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnActionClickedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnActionClickedListener.java
@@ -18,6 +18,8 @@
  */
 public interface OnActionClickedListener {
 
-    public void onActionClicked(Action action);
-
+    /**
+     * Callback fired when the host fragment receives an action.
+     */
+    void onActionClicked(Action action);
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
index f94bc11..ae9d436 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
@@ -19,13 +19,25 @@
 import android.view.ViewGroup;
 
 /**
- * Interface for receiving notification when a child of this
- * ViewGroup has been selected.
+ * Interface for receiving notification when a child of this ViewGroup has been selected.
+ * There are two methods:
+ * <li>
+ *     {link {@link #onChildViewHolderSelected(RecyclerView, RecyclerView.ViewHolder, int, int)}}
+ *     is called when the view holder is about to be selected.  The listener could change size
+ *     of the view holder in this callback.
+ * </li>
+ * <li>
+ *     {link {@link #onChildViewHolderSelectedAndPositioned(RecyclerView, RecyclerView.ViewHolder,
+ *     int, int)} is called when view holder has been selected and laid out in RecyclerView.
+ *
+ * </li>
  */
 public abstract class OnChildViewHolderSelectedListener {
     /**
-     * Callback method to be invoked when a child of this ViewGroup has been
-     * selected.
+     * Callback method to be invoked when a child of this ViewGroup has been selected. Listener
+     * might change the size of the child and the position of the child is not finalized. To get
+     * the final layout position of child, overide {@link #onChildViewHolderSelectedAndPositioned(
+     * RecyclerView, RecyclerView.ViewHolder, int, int)}.
      *
      * @param parent The RecyclerView where the selection happened.
      * @param child The ViewHolder within the RecyclerView that is selected, or null if no
@@ -38,4 +50,21 @@
     public void onChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child,
             int position, int subposition) {
     }
+
+    /**
+     * Callback method to be invoked when a child of this ViewGroup has been selected and
+     * positioned.
+     *
+     * @param parent The RecyclerView where the selection happened.
+     * @param child The ViewHolder within the RecyclerView that is selected, or null if no
+     *        view is selected.
+     * @param position The position of the view in the adapter, or NO_POSITION
+     *        if no view is selected.
+     * @param subposition The index of which {@link ItemAlignmentDef} being used,
+     *                    0 if there is no ItemAlignmentDef defined for the item.
+     */
+    public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
+            RecyclerView.ViewHolder child, int position, int subposition) {
+    }
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java b/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
index 78afa37..ea39399 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PagingIndicator.java
@@ -16,6 +16,8 @@
 
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
@@ -39,8 +41,6 @@
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * A page indicator with dots.
  * @hide
@@ -52,8 +52,8 @@
     private static final long DURATION_TRANSLATION_X = DURATION_DIAMETER;
     private static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
 
-    private static final Property<Dot, Float> DOT_ALPHA
-            = new Property<Dot, Float>(Float.class, "alpha") {
+    private static final Property<Dot, Float> DOT_ALPHA =
+            new Property<Dot, Float>(Float.class, "alpha") {
         @Override
         public Float get(Dot dot) {
             return dot.getAlpha();
@@ -65,8 +65,8 @@
         }
     };
 
-    private static final Property<Dot, Float> DOT_DIAMETER
-            = new Property<Dot, Float>(Float.class, "diameter") {
+    private static final Property<Dot, Float> DOT_DIAMETER =
+            new Property<Dot, Float>(Float.class, "diameter") {
         @Override
         public Float get(Dot dot) {
             return dot.getDiameter();
@@ -78,8 +78,8 @@
         }
     };
 
-    private static final Property<Dot, Float> DOT_TRANSLATION_X
-            = new Property<Dot, Float>(Float.class, "translation_x") {
+    private static final Property<Dot, Float> DOT_TRANSLATION_X =
+            new Property<Dot, Float>(Float.class, "translation_x") {
         @Override
         public Float get(Dot dot) {
             return dot.getTranslationX();
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java b/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java
new file mode 100644
index 0000000..58cfe63
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue;
+import android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue;
+import android.support.v17.leanback.widget.ParallaxEffect.FloatEffect;
+import android.support.v17.leanback.widget.ParallaxEffect.IntEffect;
+
+/**
+ * Parallax listens to {@link ParallaxSource} changes and invokes performMapping on each
+ * {@link ParallaxEffect} object.
+ */
+public final class Parallax {
+
+    private ParallaxSource mSource;
+    private final List<ParallaxEffect> mEffects = new ArrayList<ParallaxEffect>(4);
+
+    private final ParallaxSource.Listener mSourceListener = new ParallaxSource.Listener() {
+        @Override
+        public void onPropertiesChanged(ParallaxSource source) {
+            for (int i = 0; i < mEffects.size(); i++) {
+                mEffects.get(i).performMapping(source);
+            }
+        }
+    };
+
+    /**
+     * Sets a {@link ParallaxSource} object and starts listening on it. Stops listening to the
+     * previous {@link ParallaxSource} object if it exists.
+     *
+     * @param source New {@link ParallaxSource} object.
+     */
+    public void setSource(ParallaxSource source) {
+        if (mSource != null) {
+            mSource.setListener(null);
+        }
+        mSource = source;
+        if (mSource != null) {
+            mSource.setListener(mSourceListener);
+        }
+    }
+
+    /**
+     * Gets the current {@link ParallaxSource} object.
+     *
+     * @return The current {@link ParallaxSource} Object.
+     */
+    public ParallaxSource getSource() {
+        return mSource;
+    }
+
+    /**
+     * Adds a {@link ParallaxEffect} object which defines rules to perform mapping from
+     * {@link ParallaxSource} to multiple {@link ParallaxTarget}s.
+     *
+     * @param effect A {@link ParallaxEffect} object.
+     */
+    public void addEffect(ParallaxEffect effect) {
+        mEffects.add(effect);
+    }
+
+    /**
+     * Returns a list of {@link ParallaxEffect} object which defines rules to perform mapping from
+     * {@link ParallaxSource} to multiple {@link ParallaxTarget}s.
+     *
+     * @return A list of {@link ParallaxEffect} object.
+     */
+    public List<ParallaxEffect> getEffects() {
+        return mEffects;
+    }
+
+    /**
+     * Remove the {@link ParallaxEffect} object.
+     *
+     * @param effect The {@link ParallaxEffect} object to remove.
+     */
+    public void removeEffect(ParallaxEffect effect) {
+        mEffects.remove(effect);
+    }
+
+    /**
+     * Remove all {@link ParallaxEffect} objects.
+     */
+    public void removeAllEffects() {
+        mEffects.clear();
+    }
+
+    /**
+     * Create a {@link ParallaxEffect} object that will track source variable changes within a
+     * provided set of ranges.
+     *
+     * @param ranges A list of key values that defines the ranges.
+     * @return Newly created ParallaxEffect object.
+     */
+    public ParallaxEffect addEffect(IntPropertyKeyValue... ranges) {
+        IntEffect effect = new IntEffect();
+        effect.setPropertyRanges(ranges);
+        addEffect(effect);
+        return effect;
+    }
+
+    /**
+     * Create a {@link ParallaxEffect} object that will track source variable changes within a
+     * provided set of ranges.
+     *
+     * @param ranges A list of key values that defines the ranges.
+     * @return Newly created ParallaxEffect object.
+     */
+    public ParallaxEffect addEffect(FloatPropertyKeyValue... ranges) {
+        FloatEffect effect = new FloatEffect();
+        effect.setPropertyRanges(ranges);
+        addEffect(effect);
+        return effect;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java
new file mode 100644
index 0000000..615c11e
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.animation.PropertyValuesHolder;
+import android.support.v17.leanback.widget.ParallaxSource.FloatProperty;
+import android.support.v17.leanback.widget.ParallaxSource.FloatPropertyKeyValue;
+import android.support.v17.leanback.widget.ParallaxSource.IntProperty;
+import android.support.v17.leanback.widget.ParallaxSource.IntPropertyKeyValue;
+import android.support.v17.leanback.widget.ParallaxSource.PropertyKeyValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ParallaxEffect class drives changes in {@link ParallaxTarget} in response to changes in
+ * variables defined in {@link ParallaxSource}.
+ * <p>
+ * ParallaxEffect has a list of {@link PropertyKeyValue}s which represents the range of values that
+ * source variables can take. The main function is
+ * {@link ParallaxEffect#performMapping(ParallaxSource)} which computes a fraction between 0 and 1
+ * based on the current values of variables in {@link ParallaxSource}. As the parallax effect goes
+ * on, the fraction increases from 0 at beginning to 1 at the end. Then the fraction is passed on
+ * to {@link ParallaxTarget#update(float)}.
+ * <p>
+ * ParallaxEffect has two concrete subclasses, {@link IntEffect} and {@link FloatEffect}.
+ */
+public abstract class ParallaxEffect<ParallaxEffectT extends ParallaxEffect,
+        PropertyKeyValueT extends ParallaxSource.PropertyKeyValue> {
+
+    final List<PropertyKeyValueT> mKeyValues = new ArrayList<PropertyKeyValueT>(2);
+    final List<Float> mWeights = new ArrayList<Float>(2);
+    final List<Float> mTotalWeights = new ArrayList<Float>(2);
+    final List<ParallaxTarget> mTargets = new ArrayList<ParallaxTarget>(4);
+
+    /**
+     * Returns the list of {@link PropertyKeyValue}s, which represents the range of values that
+     * source variables can take.
+     *
+     * @return A list of {@link PropertyKeyValue}s.
+     * @see #performMapping(ParallaxSource)
+     */
+    public final List<PropertyKeyValueT> getPropertyRanges() {
+        return mKeyValues;
+    }
+
+    /**
+     * Returns a list of Float objects that represents weight associated with each variable range.
+     * Weights are used when there are three or more key values.
+     *
+     * @return A list of Float objects that represents weight associated with each variable range.
+     * @hide
+     */
+    public final List<Float> getWeights() {
+        return mWeights;
+    }
+
+    /**
+     * Sets the list of {@link PropertyKeyValue}s, which represents the range of values that
+     * source variables can take.
+     *
+     * @param keyValues A list of {@link PropertyKeyValue}s.
+     * @see #performMapping(ParallaxSource)
+     */
+    public final void setPropertyRanges(PropertyKeyValueT... keyValues) {
+        mKeyValues.clear();
+        for (PropertyKeyValueT keyValue : keyValues) {
+            mKeyValues.add(keyValue);
+        }
+    }
+
+    /**
+     * Sets a list of Float objects that represents weight associated with each variable range.
+     * Weights are used when there are three or more key values.
+     *
+     * @param weights A list of Float objects that represents weight associated with each variable
+     *                range.
+     * @hide
+     */
+    public final void setWeights(float... weights) {
+        for (float weight : weights) {
+            if (weight <= 0) {
+                throw new IllegalArgumentException();
+            }
+        }
+        mWeights.clear();
+        mTotalWeights.clear();
+        float totalWeight = 0f;
+        for (float weight : weights) {
+            mWeights.add(weight);
+            totalWeight += weight;
+            mTotalWeights.add(totalWeight);
+        }
+    }
+
+    /**
+     * Sets a list of Float objects that represents weight associated with each variable range.
+     * Weights are used when there are three or more key values.
+     *
+     * @param weights A list of Float objects that represents weight associated with each variable
+     *                range.
+     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
+     * @hide
+     */
+    public final ParallaxEffect weights(float... weights) {
+        setWeights(weights);
+        return this;
+    }
+
+    /**
+     * Add a ParallaxTarget to run parallax effect.
+     *
+     * @param target ParallaxTarget to add.
+     */
+    public final void addTarget(ParallaxTarget target) {
+        mTargets.add(target);
+    }
+
+    /**
+     * Add a ParallaxTarget to run parallax effect.
+     *
+     * @param target ParallaxTarget to add.
+     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
+     */
+    public final ParallaxEffect target(ParallaxTarget target) {
+        mTargets.add(target);
+        return this;
+    }
+
+    /**
+     * Creates a {@link ParallaxTarget} from {@link PropertyValuesHolder} and adds it to the list
+     * of targets.
+     *
+     * @param targetObject Target object for PropertyValuesHolderTarget.
+     * @param values       PropertyValuesHolder for PropertyValuesHolderTarget.
+     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
+     */
+    public final ParallaxEffect target(Object targetObject, PropertyValuesHolder values) {
+        mTargets.add(new ParallaxTarget.PropertyValuesHolderTarget(targetObject, values));
+        return this;
+    }
+
+    /**
+     * Returns the list of {@link ParallaxTarget} objects.
+     *
+     * @return The list of {@link ParallaxTarget} objects.
+     */
+    public final List<ParallaxTarget> getTargets() {
+        return mTargets;
+    }
+
+    /**
+     * Remove a {@link ParallaxTarget} object from the list.
+     * @param target The {@link ParallaxTarget} object to be removed.
+     */
+    public final void removeTarget(ParallaxTarget target) {
+        mTargets.remove(target);
+    }
+
+    /**
+     * Perform mapping from {@link ParallaxSource} to list of {@link ParallaxTarget}.
+     */
+    public final void performMapping(ParallaxSource source) {
+        if (mKeyValues.size() < 2) {
+            return;
+        }
+        source.verifyProperties();
+        float fraction = calculateFraction(source);
+        for (int i = 0; i < mTargets.size(); i++) {
+            mTargets.get(i).update(fraction);
+        }
+    }
+
+    /**
+     * This method is expected to compute a fraction between 0 and 1 based on the current values of
+     * variables in {@link ParallaxSource}. As the parallax effect goes on, the fraction increases
+     * from 0 at beginning to 1 at the end.
+     *
+     * @return Float value between 0 and 1.
+     */
+    protected abstract float calculateFraction(ParallaxSource source);
+
+    /**
+     * When there are multiple ranges (aka three or more keyvalues),  this method adjust the
+     * fraction inside a range to fraction of whole range.
+     * e.g. four key values, three weight values: 6, 2, 2.  totalWeights are 6, 8, 10
+     * When keyValueIndex is 3, the fraction is inside last range.
+     * adjusted_fraction = 8 / 10 + 2 / 10 * fraction.
+     */
+    final float getFractionWithWeightAdjusted(float fraction, int keyValueIndex) {
+        // when there are three or more KeyValues, take weight into consideration.
+        if (mKeyValues.size() >= 3) {
+            final boolean hasWeightsDefined = mWeights.size() == mKeyValues.size() - 1;
+            if (hasWeightsDefined) {
+                // use weights user defined
+                final float allWeights = mTotalWeights.get(mTotalWeights.size() - 1);
+                fraction = fraction * mWeights.get(keyValueIndex - 1) / allWeights;
+                if (keyValueIndex >= 2) {
+                    fraction += mTotalWeights.get(keyValueIndex - 2) / allWeights;
+                }
+            } else {
+                // assume each range has same weight.
+                final float allWeights = mKeyValues.size() - 1;
+                fraction = fraction / allWeights;
+                if (keyValueIndex >= 2) {
+                    fraction += (float)(keyValueIndex - 1) / allWeights;
+                }
+            }
+        }
+        return fraction;
+    }
+
+    /**
+     * Implementation of {@link ParallaxEffect} for integer type.
+     */
+    public static final class IntEffect extends ParallaxEffect<IntEffect, IntPropertyKeyValue> {
+
+        @Override
+        protected float calculateFraction(ParallaxSource s) {
+            ParallaxSource.IntSource source = (ParallaxSource.IntSource) s;
+            int lastIndex = 0;
+            int lastValue = 0;
+            int lastKeyValue = 0;
+            // go through all KeyValues, find first KeyValue that current value is less than.
+            for (int i = 0; i < mKeyValues.size(); i++) {
+                IntPropertyKeyValue k = mKeyValues.get(i);
+                int index = k.getProperty().getIndex();
+                int keyValue = k.getKeyValue(source);
+                int currentValue = source.getPropertyValue(index);
+
+                float fraction;
+                if (i == 0) {
+                    if (currentValue >= keyValue) {
+                        return 0f;
+                    }
+                } else {
+                    if (lastIndex == index && lastKeyValue < keyValue) {
+                        throw new IllegalStateException("KeyValue of same variable must be "
+                                + "descendant order");
+                    }
+                    if (currentValue == IntProperty.UNKNOWN_AFTER) {
+                        // Implies lastValue is less than lastKeyValue and lastValue is not
+                        // UNKNWON_AFTER.  Estimates based on distance of two variables is screen
+                        // size.
+                        fraction = (float) (lastKeyValue - lastValue)
+                                / source.getMaxParentVisibleSize();
+                        return getFractionWithWeightAdjusted(fraction, i);
+                    } else if (currentValue >= keyValue) {
+                        if (lastIndex == index) {
+                            // same variable index,  same UI element at two different KeyValues,
+                            // e.g. UI element moves from lastkeyValue=500 to keyValue=0,
+                            // fraction moves from 0 to 1.
+                            fraction = (float) (lastKeyValue - currentValue)
+                                    / (lastKeyValue - keyValue);
+                        } else if (lastValue != IntProperty.UNKNOWN_BEFORE) {
+                            // e.g. UIElement_1 at 300 scroll to UIElement_2 at 400, figure out when
+                            // UIElement_1 is at keyValue=300,  keyValue of UIElement_2 by adding
+                            // delta of values to keyValue of UIElement_2.
+                            lastKeyValue = lastKeyValue + (currentValue - lastValue);
+                            fraction = (float) (lastKeyValue - currentValue)
+                                    / (lastKeyValue - keyValue);
+                        } else {
+                            // Last variable is UNKNOWN_BEFORE.  Estimates based on assumption total
+                            // travel distance from last variable to this variable is screen visible
+                            // size.
+                            fraction = 1f - (float) (currentValue - keyValue)
+                                    / source.getMaxParentVisibleSize();
+                        }
+                        return getFractionWithWeightAdjusted(fraction, i);
+                    }
+                }
+                lastValue = currentValue;
+                lastIndex = index;
+                lastKeyValue = keyValue;
+            }
+            return 1f;
+        }
+    }
+
+    /**
+     * Implementation of {@link ParallaxEffect} for float type.
+     */
+    public static final class FloatEffect extends ParallaxEffect<FloatEffect,
+            FloatPropertyKeyValue> {
+
+        @Override
+        protected float calculateFraction(ParallaxSource s) {
+            ParallaxSource.FloatSource source = (ParallaxSource.FloatSource) s;
+            int lastIndex = 0;
+            float lastValue = 0;
+            float lastKeyValue = 0;
+            // go through all KeyValues, find first KeyValue that current value is less than.
+            for (int i = 0; i < mKeyValues.size(); i++) {
+                FloatPropertyKeyValue k = mKeyValues.get(i);
+                int index = k.getProperty().getIndex();
+                float keyValue = k.getKeyValue(source);
+                float currentValue = source.getPropertyValue(index);
+
+                float fraction;
+                if (i == 0) {
+                    if (currentValue >= keyValue) {
+                        return 0f;
+                    }
+                } else {
+                    if (lastIndex == index && lastKeyValue < keyValue) {
+                        throw new IllegalStateException("KeyValue of same variable must be "
+                                + "descendant order");
+                    }
+                    if (currentValue == FloatProperty.UNKNOWN_AFTER) {
+                        // Implies lastValue is less than lastKeyValue and lastValue is not
+                        // UNKNOWN_AFTER.  Estimates based on distance of two variables is screen
+                        // size.
+                        fraction = (float) (lastKeyValue - lastValue)
+                                / source.getMaxParentVisibleSize();
+                        return getFractionWithWeightAdjusted(fraction, i);
+                    } else if (currentValue >= keyValue) {
+                        if (lastIndex == index) {
+                            // same variable index,  same UI element at two different KeyValues,
+                            // e.g. UI element moves from lastkeyValue=500 to keyValue=0,
+                            // fraction moves from 0 to 1.
+                            fraction = (float) (lastKeyValue - currentValue)
+                                    / (lastKeyValue - keyValue);
+                        } else if (lastValue != FloatProperty.UNKNOWN_BEFORE) {
+                            // e.g. UIElement_1 at 300 scroll to UIElement_2 at 400, figure out when
+                            // UIElement_1 is at keyValue=300,  keyValue of UIElement_2 by adding
+                            // delta of values to keyValue of UIElement_2.
+                            lastKeyValue = lastKeyValue + (currentValue - lastValue);
+                            fraction = (float) (lastKeyValue - currentValue)
+                                    / (lastKeyValue - keyValue);
+                        } else {
+                            // Last variable is UNKNOWN_BEFORE.  Estimates based on assumption total
+                            // travel distance from last variable to this variable is screen visible
+                            // size.
+                            fraction = 1f - (float) (currentValue - keyValue)
+                                    / source.getMaxParentVisibleSize();
+                        }
+                        return getFractionWithWeightAdjusted(fraction, i);
+                    }
+                }
+                lastValue = currentValue;
+                lastIndex = index;
+                lastKeyValue = keyValue;
+            }
+            return 1f;
+        }
+    }
+
+}
+
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxRecyclerViewSource.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxRecyclerViewSource.java
new file mode 100644
index 0000000..7f1fd41
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxRecyclerViewSource.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import static android.support.v7.widget.RecyclerView.LayoutManager;
+import static android.support.v7.widget.RecyclerView.OnScrollListener;
+import static android.support.v7.widget.RecyclerView.ViewHolder;
+
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * Implementation of {@link ParallaxSource} class for {@link RecyclerView}. This class
+ * allows users to track position of specific views inside {@link RecyclerView} relative to
+ * itself. @see {@link ChildPositionProperty} for details.
+ */
+public class ParallaxRecyclerViewSource extends
+        ParallaxSource.IntSource<ParallaxRecyclerViewSource.ChildPositionProperty> {
+    private final RecyclerView mRecylerView;
+    private Listener mListener;
+    private final boolean mIsVertical;
+
+    /**
+     * Subclass of {@link ParallaxSource.IntProperty}. Using this Property, users can track a
+     * RecylerView child's position inside recyclerview. i.e.
+     *
+     * tracking_pos = view.top + fraction * view.height() + offset
+     *
+     * This way we can track top using fraction 0 and bottom using fraction 1.
+     */
+    public static final class ChildPositionProperty extends ParallaxSource.IntProperty {
+        int mAdapterPosition;
+        int mViewId;
+        int mOffset;
+        float mFraction;
+
+        ChildPositionProperty(String name, int index) {
+            super(name, index);
+        }
+
+        /**
+         * Sets adapter position of the recyclerview child to track.
+         *
+         * @param adapterPosition Zero based position in adapter.
+         * @return This ChildPositionProperty object.
+         */
+        public ChildPositionProperty adapterPosition(int adapterPosition) {
+            mAdapterPosition = adapterPosition;
+            return this;
+        };
+
+        /**
+         * Sets view Id of a descendant of recyclerview child to track.
+         *
+         * @param viewId Id of a descendant of recyclerview child.
+         * @return This ChildPositionProperty object.
+         */
+        public ChildPositionProperty viewId(int viewId) {
+            mViewId = viewId;
+            return this;
+        }
+
+        /**
+         * Sets offset in pixels added to the view's start position.
+         *
+         * @param offset Offset in pixels added to the view's start position.
+         * @return This ChildPositionProperty object.
+         */
+        public ChildPositionProperty offset(int offset) {
+            mOffset = offset;
+            return this;
+        }
+
+        /**
+         * Sets fraction of size to be added to view's start position.  e.g. to track the
+         * center position of the view, use fraction 0.5; to track the end position of the view
+         * use fraction 1.
+         *
+         * @param fraction Fraction of size of the view.
+         * @return This ChildPositionProperty object.
+         */
+        public ChildPositionProperty fraction(float fraction) {
+            mFraction = fraction;
+            return this;
+        }
+
+        /**
+         * Returns adapter position of the recyclerview child to track.
+         */
+        public int getAdapterPosition() {
+            return mAdapterPosition;
+        }
+
+        /**
+         * Returns view Id of a descendant of recyclerview child to track.
+         */
+        public int getViewId() {
+            return mViewId;
+        }
+
+        /**
+         * Returns offset in pixels added to the view's start position.
+         */
+        public int getOffset() {
+            return mOffset;
+        }
+
+        /**
+         * Returns fraction of size to be added to view's start position.  e.g. to track the
+         * center position of the view, use fraction 0.5; to track the end position of the view
+         * use fraction 1.
+         */
+        public float getFraction() {
+            return mFraction;
+        }
+
+        void updateValue(ParallaxRecyclerViewSource source) {
+            RecyclerView recyclerView = source.mRecylerView;
+            ViewHolder viewHolder =
+                    recyclerView.findViewHolderForAdapterPosition(mAdapterPosition);
+            if (viewHolder == null) {
+                if (recyclerView.getLayoutManager().getChildCount() == 0) {
+                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
+                    return;
+                }
+                View firstChild = recyclerView.getLayoutManager().getChildAt(0);
+                ViewHolder vh = recyclerView.findContainingViewHolder(firstChild);
+                int firstPosition = vh.getAdapterPosition();
+                if (firstPosition < mAdapterPosition) {
+                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
+                } else {
+                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_BEFORE);
+                }
+            } else {
+                View trackingView = viewHolder.itemView.findViewById(mViewId);
+                if (trackingView == null) {
+                    return;
+                }
+
+                Rect rect = new Rect(
+                        0, 0, trackingView.getWidth(), trackingView.getHeight());
+                recyclerView.offsetDescendantRectToMyCoords(trackingView, rect);
+                if (source.mIsVertical) {
+                    source.setPropertyValue(getIndex(), rect.top + mOffset
+                            + (int) (mFraction * rect.height()));
+                } else {
+                    source.setPropertyValue(getIndex(), rect.left + mOffset
+                            + (int) (mFraction * rect.width()));
+                }
+            }
+        }
+    }
+
+    @Override
+    public ChildPositionProperty createProperty(String name, int index) {
+        return new ChildPositionProperty(name, index);
+    }
+
+    @Override
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public int getMaxParentVisibleSize() {
+        if (mRecylerView == null) {
+            return 0;
+        }
+        return mIsVertical ? mRecylerView.getHeight() : mRecylerView.getWidth();
+    }
+
+    /**
+     * Constructor.
+     */
+    public ParallaxRecyclerViewSource(RecyclerView parent) {
+        this.mRecylerView = parent;
+        LayoutManager.Properties properties = mRecylerView.getLayoutManager()
+                .getProperties(mRecylerView.getContext(), null, 0, 0);
+        mIsVertical = properties.orientation == RecyclerView.VERTICAL;
+        setupScrollListener();
+    }
+
+    private void setupScrollListener() {
+        mRecylerView.addOnScrollListener(new OnScrollListener() {
+            @Override
+            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+                for (ChildPositionProperty prop: getProperties()) {
+                    prop.updateValue(ParallaxRecyclerViewSource.this);
+                }
+                mListener.onPropertiesChanged(ParallaxRecyclerViewSource.this);
+            }
+        });
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxSource.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxSource.java
new file mode 100644
index 0000000..02809af
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxSource.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.util.Property;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * ParallaxSource tracks a list of dynamic {@link Property}s typically representing foreground UI
+ * element positions on screen. App should subclass either {@link ParallaxSource.IntSource} or
+ * {@link ParallaxSource.FloatSource}. App may subclass {@link ParallaxSource.IntProperty} or
+ * {@link ParallaxSource.FloatProperty} to supply additional information about how to retrieve
+ * Property value.  For reference implementation, see {@link ParallaxRecyclerViewSource}.
+ *
+ * <p>
+ * <ul>Restrictions
+ * <li>Values must be in ascending order.</li>
+ * <li>If the UI element is unknown above screen, use UNKNOWN_BEFORE.</li>
+ * <li>if the UI element is unknown below screen, use UNKNOWN_AFTER.</li>
+ * <li>UNKNOWN_BEFORE and USE_UNKNOWN_AFTER are not allowed to be next to each other.</li>
+ * </ul>
+ *
+ * These rules can be verified by {@link #verifyProperties()}.
+ * </p>
+ */
+public abstract class ParallaxSource<PropertyT extends Property> {
+
+    /**
+     * Listener for tracking Property value changes.
+     */
+    public static abstract class Listener {
+        /**
+         * Called when the value for any of the property in ParallaxSource changes.
+         */
+        public void onPropertiesChanged(ParallaxSource source) {
+        }
+    }
+
+    /**
+     * Class holding a fixed key value for a Property in {@link ParallaxSource}.
+     * Base class for {@link IntPropertyKeyValue} and {@link FloatPropertyKeyValue}.
+     */
+    public static class PropertyKeyValue<PropertyT extends Property> {
+        private final PropertyT mProperty;
+
+        public PropertyKeyValue(PropertyT property) {
+            mProperty = property;
+        }
+
+        /**
+         * @return Associated property.
+         */
+        public PropertyT getProperty() {
+            return mProperty;
+        }
+    }
+
+    /**
+     * IntProperty provide access to an index based integer type property inside {@link IntSource}.
+     * The IntProperty typically represents UI element position inside {@link IntSource}.
+     */
+    public static class IntProperty extends Property<IntSource, Integer> {
+
+        /**
+         * Property value is unknown and it's above screen.
+         */
+        public static final int UNKNOWN_BEFORE = Integer.MIN_VALUE;
+
+        /**
+         * Property value is unknown and it's bellow screen.
+         */
+        public static final int UNKNOWN_AFTER = Integer.MAX_VALUE;
+
+        private final int mIndex;
+
+        /**
+         * Constructor.
+         *
+         * @param name Name of this Property.
+         * @param index Index of this Property inside {@link IntSource}.
+         */
+        public IntProperty(String name, int index) {
+            super(Integer.class, name);
+            mIndex = index;
+        }
+
+        @Override
+        public final Integer get(IntSource object) {
+            return getIntValue(object);
+        }
+
+        @Override
+        public final void set(IntSource object, Integer value) {
+            setIntValue(object, value);
+        }
+
+        final int getIntValue(IntSource source) {
+            return source.getPropertyValue(mIndex);
+        }
+
+        final void setIntValue(IntSource source, int value) {
+            source.setPropertyValue(mIndex, value);
+        }
+
+        /**
+         * @return Index of this Property in {@link IntSource}.
+         */
+        public final int getIndex() {
+            return mIndex;
+        }
+
+        /**
+         * Creates an {@link IntPropertyKeyValue} object for the absolute keyValue.
+         *
+         * @param keyValue The integer key value.
+         * @return A new {@link IntPropertyKeyValue} object.
+         */
+        public final IntPropertyKeyValue atAbsolute(int keyValue) {
+            return new IntPropertyKeyValue(this, keyValue, 0f);
+        }
+
+        /**
+         * Creates an {@link IntPropertyKeyValue} object for a fraction of
+         * {@link IntSource#getMaxParentVisibleSize()}.
+         *
+         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
+         *                                       {@link IntSource#getMaxParentVisibleSize()} for
+         *                                       the key value.
+         * @return A new {@link IntPropertyKeyValue} object.
+         */
+        public final IntPropertyKeyValue atFraction(float fractionOfMaxParentVisibleSize) {
+            return new IntPropertyKeyValue(this, 0, fractionOfMaxParentVisibleSize);
+        }
+
+        /**
+         * Create an {@link IntPropertyKeyValue} object by multiplying the fraction with
+         * {@link IntSource#getMaxParentVisibleSize()} and adding offsetValue to it.
+         *
+         * @param offsetValue                    An offset integer value to be added to key value.
+         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
+         *                                       {@link IntSource#getMaxParentVisibleSize()} for
+         *                                       the key value.
+         * @return A new {@link IntPropertyKeyValue} object.
+         */
+        public final IntPropertyKeyValue at(int offsetValue,
+                float fractionOfMaxParentVisibleSize) {
+            return new IntPropertyKeyValue(this, offsetValue, fractionOfMaxParentVisibleSize);
+        }
+    }
+
+    /**
+     * Manages a list of {@link IntProperty}. App should override this class with a specific
+     * {@link IntProperty} subclass.
+     *
+     * @param <IntPropertyT> Type of {@link IntProperty} or subclass.
+     */
+    public abstract static class IntSource<IntPropertyT extends IntProperty>
+            extends ParallaxSource<IntPropertyT> {
+
+        private int[] mValues = new int[4];
+
+        /**
+         * Get index based property value.
+         *
+         * @param index Index of the property.
+         * @return Value of the property.
+         */
+        public final int getPropertyValue(int index) {
+            return mValues[index];
+        }
+
+        /**
+         * Set index based property value.
+         *
+         * @param index Index of the property.
+         * @param value Value of the property.
+         */
+        public final void setPropertyValue(int index, int value) {
+            mValues[index] = value;
+        }
+
+        /**
+         * Return the size of parent visible area, e.g. parent view's height if we are tracking Y
+         * position of a child. The size can be used to calculate key value using the provided
+         * fraction.
+         *
+         * @return Size of parent visible area.
+         * @see IntPropertyKeyValue
+         */
+        public abstract int getMaxParentVisibleSize();
+
+        @Override
+        public final IntPropertyT addProperty(String name) {
+            int newPropertyIndex = mProperties.size();
+            IntPropertyT property = createProperty(name, newPropertyIndex);
+            mProperties.add(property);
+            int size = mValues.length;
+            if (size == newPropertyIndex) {
+                int[] newValues = new int[size * 2];
+                for (int i = 0; i < size; i++) {
+                    newValues[i] = mValues[i];
+                }
+                mValues = newValues;
+            }
+            mValues[newPropertyIndex] = IntProperty.UNKNOWN_AFTER;
+            return property;
+        }
+
+        @Override
+        public final void verifyProperties() throws IllegalStateException {
+            if (mProperties.size() < 2) {
+                return;
+            }
+            int last = mProperties.get(0).getIntValue(this);
+            for (int i = 1; i < mProperties.size(); i++) {
+                int v = mProperties.get(i).getIntValue(this);
+                if (v < last) {
+                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                    + " smaller than Property[%d]\"%s\"",
+                            i, mProperties.get(i).getName(),
+                            i - 1, mProperties.get(i - 1).getName()));
+                } else if (last == IntProperty.UNKNOWN_BEFORE && v == IntProperty.UNKNOWN_AFTER) {
+                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                    + " UNKNOW_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
+                            i - 1, mProperties.get(i - 1).getName(),
+                            i, mProperties.get(i).getName()));
+                }
+                last = v;
+            }
+        }
+
+    }
+
+    /**
+     * Implementation of {@link PropertyKeyValue} for {@link IntProperty}.
+     */
+    public static class IntPropertyKeyValue extends PropertyKeyValue<IntProperty> {
+        private final int mValue;
+        private final float mFactionOfMax;
+
+        public IntPropertyKeyValue(IntProperty property, int value) {
+            this(property, value, 0f);
+        }
+
+        public IntPropertyKeyValue(IntProperty property, int value, float fractionOfMax) {
+            super(property);
+            mValue = value;
+            mFactionOfMax = fractionOfMax;
+        }
+
+        /**
+         * @return The key value of integer type.
+         */
+        public final int getKeyValue(IntSource source) {
+            return mFactionOfMax == 0 ? mValue : mValue + Math.round(source
+                    .getMaxParentVisibleSize() * mFactionOfMax);
+        }
+    }
+
+    /**
+     * FloatProperty provide access to an index based integer type property inside
+     * {@link FloatSource}. The FloatProperty typically represents UI element position inside
+     * {@link FloatSource}.
+     */
+    public static class FloatProperty extends Property<FloatSource, Float> {
+
+        /**
+         * Property value is unknown and it's above screen.
+         */
+        public static final float UNKNOWN_BEFORE = -Float.MAX_VALUE;
+        /**
+         * Property value is unknown and it's bellow screen.
+         */
+        public static final float UNKNOWN_AFTER = Float.MAX_VALUE;
+
+        private final int mIndex;
+
+        /**
+         * Constructor.
+         *
+         * @param name Name of this Property.
+         * @param index Index of this Property inside {@link FloatSource}.
+         */
+        public FloatProperty(String name, int index) {
+            super(Float.class, name);
+            mIndex = index;
+        }
+
+        @Override
+        public final Float get(FloatSource object) {
+            return getFloatValue(object);
+        }
+
+        @Override
+        public final void set(FloatSource object, Float value) {
+            setFloatValue(object, value);
+        }
+
+        final float getFloatValue(FloatSource source) {
+            return source.getPropertyValue(mIndex);
+        }
+
+        final void setFloatValue(FloatSource source, float value) {
+            source.setPropertyValue(mIndex, value);
+        }
+
+        /**
+         * @return Index of this Property in {@link FloatSource}.
+         */
+        public final int getIndex() {
+            return mIndex;
+        }
+
+        /**
+         * Creates an {@link FloatPropertyKeyValue} object for the absolute keyValue.
+         *
+         * @param keyValue The float key value.
+         * @return A new {@link FloatPropertyKeyValue} object.
+         */
+        public final FloatPropertyKeyValue atAbsolute(float keyValue) {
+            return new FloatPropertyKeyValue(this, keyValue, 0f);
+        }
+
+        /**
+         * Creates an {@link FloatPropertyKeyValue} object for a fraction of
+         * {@link FloatSource#getMaxParentVisibleSize()}.
+         *
+         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
+         *                                       {@link FloatSource#getMaxParentVisibleSize()} for
+         *                                       the key value.
+         * @return A new {@link FloatPropertyKeyValue} object.
+         */
+        public final FloatPropertyKeyValue atFraction(float fractionOfMaxParentVisibleSize) {
+            return new FloatPropertyKeyValue(this, 0, fractionOfMaxParentVisibleSize);
+        }
+
+        /**
+         * Create an {@link FloatPropertyKeyValue} object by multiplying the fraction with
+         * {@link FloatSource#getMaxParentVisibleSize()} and adding offsetValue to it.
+         *
+         * @param offsetValue                    An offset float value to be added to key value.
+         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
+         *                                       {@link FloatSource#getMaxParentVisibleSize()} for
+         *                                       the key value.
+         * @return A new {@link FloatPropertyKeyValue} object.
+         */
+        public final FloatPropertyKeyValue at(float offsetValue,
+                float fractionOfMaxParentVisibleSize) {
+            return new FloatPropertyKeyValue(this, offsetValue, fractionOfMaxParentVisibleSize);
+        }
+    }
+
+    /**
+     * Manages a list of {@link FloatProperty}. App should override this class with a specific
+     * {@link FloatProperty} subclass.
+     *
+     * @param <FloatPropertyT> Type of {@link FloatProperty} or subclass.
+     */
+    public abstract static class FloatSource<FloatPropertyT extends FloatProperty> extends
+            ParallaxSource<FloatPropertyT> {
+
+        private float[] mValues = new float[4];
+
+        /**
+         * Get index based property value.
+         *
+         * @param index Index of the property.
+         * @return Value of the property.
+         */
+        public final float getPropertyValue(int index) {
+            return mValues[index];
+        }
+
+        /**
+         * Set index based property value.
+         *
+         * @param index Index of the property.
+         * @param value Value of the property.
+         */
+        public final void setPropertyValue(int index, float value) {
+            mValues[index] = value;
+        }
+
+        /**
+         * Return the size of parent visible area, e.g. parent view's height if we are tracking Y
+         * position of a child. The size can be used to calculate key value using the provided
+         * fraction.
+         *
+         * @return Size of parent visible area.
+         * @see FloatPropertyKeyValue
+         */
+        public abstract float getMaxParentVisibleSize();
+
+        @Override
+        public final FloatPropertyT addProperty(String name) {
+            int newPropertyIndex = mProperties.size();
+            FloatPropertyT property = createProperty(name, newPropertyIndex);
+            mProperties.add(property);
+            int size = mValues.length;
+            if (size == newPropertyIndex) {
+                float[] newValues = new float[size * 2];
+                for (int i = 0; i < size; i++) {
+                    newValues[i] = mValues[i];
+                }
+                mValues = newValues;
+            }
+            mValues[newPropertyIndex] = FloatProperty.UNKNOWN_AFTER;
+            return property;
+        }
+
+        @Override
+        public final void verifyProperties() throws IllegalStateException {
+            if (mProperties.size() < 2) {
+                return;
+            }
+            float last = mProperties.get(0).getFloatValue(this);
+            for (int i = 1; i < mProperties.size(); i++) {
+                float v = mProperties.get(i).getFloatValue(this);
+                if (v < last) {
+                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                    + " smaller than Property[%d]\"%s\"",
+                            i, mProperties.get(i).getName(),
+                            i - 1, mProperties.get(i - 1).getName()));
+                } else if (last == FloatProperty.UNKNOWN_BEFORE && v
+                        == FloatProperty.UNKNOWN_AFTER) {
+                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                    + " UNKNOW_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
+                            i - 1, mProperties.get(i - 1).getName(),
+                            i, mProperties.get(i).getName()));
+                }
+                last = v;
+            }
+        }
+
+    }
+
+    /**
+     * Implementation of {@link PropertyKeyValue} for {@link FloatProperty}.
+     */
+    public static class FloatPropertyKeyValue extends PropertyKeyValue<FloatProperty> {
+        private final float mValue;
+        private final float mFactionOfMax;
+
+        public FloatPropertyKeyValue(FloatProperty property, float value) {
+            this(property, value, 0f);
+        }
+
+        public FloatPropertyKeyValue(FloatProperty property, float value, float fractionOfMax) {
+            super(property);
+            mValue = value;
+            mFactionOfMax = fractionOfMax;
+        }
+
+        /**
+         * @return The key value.
+         */
+        public final float getKeyValue(FloatSource source) {
+            return mFactionOfMax == 0 ? mValue : mValue + source.getMaxParentVisibleSize()
+                    * mFactionOfMax;
+        }
+    }
+
+    final List<PropertyT> mProperties = new ArrayList<PropertyT>();
+    final List<PropertyT> mPropertiesReadOnly = Collections.unmodifiableList(mProperties);
+
+    /**
+     * @return A unmodifiable list of properties.
+     */
+    public final List<PropertyT> getProperties() {
+        return mPropertiesReadOnly;
+    }
+
+    /**
+     * Add a new Property in the ParallaxSource.
+     *
+     * @return Newly created Property.
+     */
+    public abstract PropertyT addProperty(String name);
+
+    /**
+     * Create a new Property object. App does not directly call this method.  See
+     * {@link #addProperty(String)}.
+     *
+     * @param index  Index of the property in this ParallaxSource object.
+     * @return Newly created Property object.
+     */
+    public abstract PropertyT createProperty(String name, int index);
+
+    /**
+     * Verify sanity of property values, throws RuntimeException if fails. The property values
+     * must be in ascending order. UNKNOW_BEFORE and UNKNOWN_AFTER are not allowed to be next to
+     * each other.
+     */
+    public abstract void verifyProperties() throws IllegalStateException;
+
+    /**
+     * Sets listener to monitor property value changes.
+     *
+     * @param listener The listener to set on {@link ParallaxSource} object.
+     */
+    public abstract void setListener(Listener listener);
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java
new file mode 100644
index 0000000..1fbd54e
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.view.animation.LinearInterpolator;
+
+import java.util.List;
+
+/**
+ * ParallaxTarget is reponsible for updating the target through the {@link #update(float)} method.
+ * {@link ParallaxEffect} transforms the values of {@link ParallaxSource}, which represents the
+ * current state of UI, into a float value between 0 and 1. That float value is passed into
+ * {@link #update(float)} method.
+ */
+public abstract class ParallaxTarget {
+
+    /**
+     * Implementation class is supposed to update target with the provided fraction
+     * (between 0 and 1). The fraction represents percentage of completed change (e.g. scroll) on
+     * target.
+     *
+     * @param fraction Fraction between 0 to 1.
+     */
+    public abstract void update(float fraction);
+
+    /**
+     * Returns the current fraction (between 0 and 1). The fraction represents percentage of
+     * completed change (e.g. scroll) on target.
+     *
+     * @return Current fraction value.
+     */
+    public abstract float getFraction();
+
+    /**
+     * PropertyValuesHolderTarget is an implementation of {@link ParallaxTarget} that uses
+     * {@link PropertyValuesHolder} to update the target object.
+     */
+    public static final class PropertyValuesHolderTarget extends ParallaxTarget {
+
+        /**
+         * We simulate a parallax effect on target object using an ObjectAnimator. PSEUDO_DURATION
+         * is used on the ObjectAnimator.
+         */
+        private static final long PSEUDO_DURATION = 1000000;
+
+        private final ObjectAnimator mAnimator;
+        private float mFraction;
+
+        public PropertyValuesHolderTarget(Object targetObject, PropertyValuesHolder values) {
+            mAnimator = ObjectAnimator.ofPropertyValuesHolder(targetObject, values);
+            mAnimator.setInterpolator(new LinearInterpolator());
+            mAnimator.setDuration(PSEUDO_DURATION);
+        }
+
+        @Override
+        public void update(float fraction) {
+            mFraction = fraction;
+            mAnimator.setCurrentPlayTime((long) (PSEUDO_DURATION * fraction));
+        }
+
+        @Override
+        public float getFraction() {
+            return mFraction;
+        }
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PersistentFocusWrapper.java b/v17/leanback/src/android/support/v17/leanback/widget/PersistentFocusWrapper.java
index ed057de..8a3d886 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PersistentFocusWrapper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PersistentFocusWrapper.java
@@ -83,15 +83,16 @@
     }
 
     private boolean shouldPersistFocusFromDirection(int direction) {
-        return ((mPersistFocusVertical && (direction == FOCUS_UP || direction == FOCUS_DOWN)) ||
-                (!mPersistFocusVertical && (direction == FOCUS_LEFT || direction == FOCUS_RIGHT)));
+        return ((mPersistFocusVertical && (direction == FOCUS_UP || direction == FOCUS_DOWN))
+                || (!mPersistFocusVertical
+                && (direction == FOCUS_LEFT || direction == FOCUS_RIGHT)));
     }
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
         if (DEBUG) Log.v(TAG, "addFocusables");
-        if (hasFocus() || getGrandChildCount() == 0 ||
-                !shouldPersistFocusFromDirection(direction)) {
+        if (hasFocus() || getGrandChildCount() == 0
+                || !shouldPersistFocusFromDirection(direction)) {
             super.addFocusables(views, direction, focusableMode);
         } else {
             // Select a child in requestFocus
@@ -107,7 +108,10 @@
             view = (View) view.getParent();
         }
         mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view);
-        if (DEBUG) Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition " + mSelectedPosition);
+        if (DEBUG) {
+            Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition "
+                    + mSelectedPosition);
+        }
     }
 
     @Override
@@ -142,8 +146,8 @@
             dest.writeInt(mSelectedPosition);
         }
 
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
             @Override
             public SavedState createFromParcel(Parcel in) {
                 return new SavedState(in);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
index da6c98d..79d1407 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
@@ -13,21 +13,19 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.drawable.ClipDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.util.MathUtil;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
-import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -58,7 +56,9 @@
         final TextView mCurrentTime;
         final TextView mTotalTime;
         final ProgressBar mProgressBar;
-        int mCurrentTimeInSeconds = -1;
+        long mCurrentTimeInMs = -1;         // Hold current time in milliseconds
+        long mTotalTimeInMs = -1;           // Hold total time in milliseconds
+        long mSecondaryProgressInMs = -1;   // Hold secondary progress in milliseconds
         StringBuilder mTotalTimeStringBuilder = new StringBuilder();
         StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
         int mCurrentTimeMarginStart;
@@ -110,8 +110,8 @@
                 if (mMoreActionsViewHolder.view.getParent() == null) {
                     mMoreActionsDock.addView(mMoreActionsViewHolder.view);
                 }
-            } else if (mMoreActionsViewHolder != null &&
-                    mMoreActionsViewHolder.view.getParent() != null) {
+            } else if (mMoreActionsViewHolder != null
+                    && mMoreActionsViewHolder.view.getParent() != null) {
                 mMoreActionsDock.removeView(mMoreActionsViewHolder.view);
             }
         }
@@ -139,49 +139,57 @@
             return margin;
         }
 
-        void setTotalTime(int totalTimeMs) {
+        void setTotalTime(long totalTimeMs) {
             if (totalTimeMs <= 0) {
                 mTotalTime.setVisibility(View.GONE);
                 mProgressBar.setVisibility(View.GONE);
             } else {
                 mTotalTime.setVisibility(View.VISIBLE);
                 mProgressBar.setVisibility(View.VISIBLE);
+                mTotalTimeInMs = totalTimeMs;
                 formatTime(totalTimeMs / 1000, mTotalTimeStringBuilder);
                 mTotalTime.setText(mTotalTimeStringBuilder.toString());
-                mProgressBar.setMax(totalTimeMs);
+                mProgressBar.setMax(Integer.MAX_VALUE);//current progress will be a fraction of this
             }
         }
 
-        int getTotalTime() {
-            return mProgressBar.getMax();
+        long getTotalTime() {
+            return mTotalTimeInMs;
         }
 
-        void setCurrentTime(int currentTimeMs) {
-            int seconds = currentTimeMs / 1000;
-            if (seconds != mCurrentTimeInSeconds) {
-                mCurrentTimeInSeconds = seconds;
-                formatTime(mCurrentTimeInSeconds, mCurrentTimeStringBuilder);
+        void setCurrentTime(long currentTimeMs) {
+            long seconds = currentTimeMs / 1000;
+            if (currentTimeMs != mCurrentTimeInMs) {
+                mCurrentTimeInMs = currentTimeMs;
+                formatTime(seconds, mCurrentTimeStringBuilder);
                 mCurrentTime.setText(mCurrentTimeStringBuilder.toString());
             }
-            mProgressBar.setProgress(currentTimeMs);
+            // Use ratio to represent current progres
+            double ratio = (double) mCurrentTimeInMs / mTotalTimeInMs;     // Range: [0, 1]
+            double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
+            mProgressBar.setProgress((int)progressRatio);
         }
 
-        int getCurrentTime() {
-            return mProgressBar.getProgress();
+        long getCurrentTime() {
+            return mTotalTimeInMs;
         }
 
-        void setSecondaryProgress(int progressMs) {
-            mProgressBar.setSecondaryProgress(progressMs);
+        void setSecondaryProgress(long progressMs) {
+            mSecondaryProgressInMs = progressMs;
+            // Solve the progress bar by using ratio
+            double ratio = (double) progressMs / mTotalTimeInMs;           // Range: [0, 1]
+            double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
+            mProgressBar.setSecondaryProgress((int) progressRatio);
         }
 
-        int getSecondaryProgress() {
-            return mProgressBar.getSecondaryProgress();
+        long getSecondaryProgress() {
+            return mSecondaryProgressInMs;
         }
     }
 
-    static void formatTime(int seconds, StringBuilder sb) {
-        int minutes = seconds / 60;
-        int hours = minutes / 60;
+    static void formatTime(long seconds, StringBuilder sb) {
+        long minutes = seconds / 60;
+        long hours = minutes / 60;
         seconds -= minutes * 60;
         minutes -= hours * 60;
 
@@ -236,26 +244,50 @@
     }
 
     public void setTotalTime(ViewHolder vh, int ms) {
+        setTotalTimeLong(vh, (long) ms);
+    }
+
+    public void setTotalTimeLong(ViewHolder vh, long ms) {
         vh.setTotalTime(ms);
     }
 
     public int getTotalTime(ViewHolder vh) {
+        return MathUtil.safeLongToInt(getTotalTimeLong(vh));
+    }
+
+    public long getTotalTimeLong(ViewHolder vh) {
         return vh.getTotalTime();
     }
 
     public void setCurrentTime(ViewHolder vh, int ms) {
+        setCurrentTimeLong(vh, (long) ms);
+    }
+
+    public void setCurrentTimeLong(ViewHolder vh, long ms) {
         vh.setCurrentTime(ms);
     }
 
     public int getCurrentTime(ViewHolder vh) {
+        return MathUtil.safeLongToInt(getCurrentTimeLong(vh));
+    }
+
+    public long getCurrentTimeLong(ViewHolder vh) {
         return vh.getCurrentTime();
     }
 
     public void setSecondaryProgress(ViewHolder vh, int progressMs) {
+        setSecondaryProgressLong(vh, (long) progressMs);
+    }
+
+    public void setSecondaryProgressLong(ViewHolder vh, long progressMs) {
         vh.setSecondaryProgress(progressMs);
     }
 
     public int getSecondaryProgress(ViewHolder vh) {
+        return MathUtil.safeLongToInt(getSecondaryProgressLong(vh));
+    }
+
+    public long getSecondaryProgressLong(ViewHolder vh) {
         return vh.getSecondaryProgress();
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
index 098cf60..83aff47 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.widget;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.util.MathUtil;
 import android.util.TypedValue;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -622,9 +623,9 @@
     private Drawable mImageDrawable;
     private ObjectAdapter mPrimaryActionsAdapter;
     private ObjectAdapter mSecondaryActionsAdapter;
-    private int mTotalTimeMs;
-    private int mCurrentTimeMs;
-    private int mBufferedProgressMs;
+    private long mTotalTimeMs;
+    private long mCurrentTimeMs;
+    private long mBufferedProgressMs;
     private OnPlaybackStateChangedListener mListener;
 
     /**
@@ -721,13 +722,29 @@
      * this row has changed.</p>
      */
     public void setTotalTime(int ms) {
+        setTotalTimeLong((long) ms);
+    }
+
+    /**
+     * Sets the total time in milliseconds (long type) for the playback controls row.
+     * @param ms Total time in milliseconds of long type.
+     */
+    public void setTotalTimeLong(long ms) {
         mTotalTimeMs = ms;
     }
 
     /**
      * Returns the total time in milliseconds for the playback controls row.
+     * @throws ArithmeticException If total time in milliseconds overflows int.
      */
     public int getTotalTime() {
+        return MathUtil.safeLongToInt(getTotalTimeLong());
+    }
+
+    /**
+     * Returns the total time in milliseconds of long type for the playback controls row.
+     */
+    public long getTotalTimeLong() {
         return mTotalTimeMs;
     }
 
@@ -737,6 +754,14 @@
      * be updated to reflect the new value.
      */
     public void setCurrentTime(int ms) {
+        setCurrentTimeLong((long) ms);
+    }
+
+    /**
+     * Sets the current time in milliseconds for playback controls row in long type.
+     * @param ms Current time in milliseconds of long type.
+     */
+    public void setCurrentTimeLong(long ms) {
         if (mCurrentTimeMs != ms) {
             mCurrentTimeMs = ms;
             currentTimeChanged();
@@ -745,8 +770,16 @@
 
     /**
      * Returns the current time in milliseconds for the playback controls row.
+     * @throws ArithmeticException If current time in milliseconds overflows int.
      */
     public int getCurrentTime() {
+        return MathUtil.safeLongToInt(getCurrentTimeLong());
+    }
+
+    /**
+     * Returns the current time in milliseconds of long type for playback controls row.
+     */
+    public long getCurrentTimeLong() {
         return mCurrentTimeMs;
     }
 
@@ -756,6 +789,14 @@
      * be updated to reflect the new value.
      */
     public void setBufferedProgress(int ms) {
+        setBufferedProgressLong((long) ms);
+    }
+
+    /**
+     * Sets the buffered progress for the playback controls row.
+     * @param ms Buffered progress in milliseconds of long type.
+     */
+    public void setBufferedProgressLong(long ms) {
         if (mBufferedProgressMs != ms) {
             mBufferedProgressMs = ms;
             bufferedProgressChanged();
@@ -764,8 +805,16 @@
 
     /**
      * Returns the buffered progress for the playback controls row.
+     * @throws ArithmeticException If buffered progress in milliseconds overflows int.
      */
     public int getBufferedProgress() {
+        return MathUtil.safeLongToInt(getBufferedProgressLong());
+    }
+
+    /**
+     * Returns the buffered progress of long type for the playback controls row.
+     */
+    public long getBufferedProgressLong() {
         return mBufferedProgressMs;
     }
 
@@ -798,8 +847,8 @@
     }
 
     interface OnPlaybackStateChangedListener {
-        public void onCurrentTimeChanged(int currentTimeMs);
-        public void onBufferedProgressChanged(int bufferedProgressMs);
+        public void onCurrentTimeChanged(long currentTimeMs);
+        public void onBufferedProgressChanged(long bufferedProgressMs);
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
index fc4163e..ef49129 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
@@ -13,13 +13,12 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.graphics.drawable.Drawable;
+import android.content.Context;
+import android.graphics.Color;
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
 import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
-import android.content.Context;
-import android.graphics.Color;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -41,7 +40,7 @@
  * detailed description ViewHolder from {@link ViewHolder#mDescriptionViewHolder}.
  * </p>
  */
-public class PlaybackControlsRowPresenter extends RowPresenter {
+public class PlaybackControlsRowPresenter extends PlaybackRowPresenter {
 
     static class BoundData extends PlaybackControlsPresenter.BoundData {
         ViewHolder mRowViewHolder;
@@ -50,7 +49,7 @@
     /**
      * A ViewHolder for the PlaybackControlsRow.
      */
-    public class ViewHolder extends RowPresenter.ViewHolder {
+    public class ViewHolder extends PlaybackRowPresenter.ViewHolder {
         public final Presenter.ViewHolder mDescriptionViewHolder;
         final ViewGroup mCard;
         final ViewGroup mCardRightPanel;
@@ -72,12 +71,12 @@
         final PlaybackControlsRow.OnPlaybackStateChangedListener mListener =
                 new PlaybackControlsRow.OnPlaybackStateChangedListener() {
             @Override
-            public void onCurrentTimeChanged(int ms) {
-                mPlaybackControlsPresenter.setCurrentTime(mControlsVh, ms);
+            public void onCurrentTimeChanged(long ms) {
+                mPlaybackControlsPresenter.setCurrentTimeLong(mControlsVh, ms);
             }
             @Override
-            public void onBufferedProgressChanged(int ms) {
-                mPlaybackControlsPresenter.setSecondaryProgress(mControlsVh, ms);
+            public void onBufferedProgressChanged(long ms) {
+                mPlaybackControlsPresenter.setSecondaryProgressLong(mControlsVh, ms);
             }
         };
 
@@ -117,17 +116,17 @@
         };
 
         Presenter getPresenter(boolean primary) {
-            ObjectAdapter adapter = primary ?
-                    ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter() :
-                            ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
+            ObjectAdapter adapter = primary
+                    ? ((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter()
+                    : ((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
             if (adapter == null) {
                 return null;
             }
             if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
                 ControlButtonPresenterSelector selector =
                         (ControlButtonPresenterSelector) adapter.getPresenterSelector();
-                return primary ? selector.getPrimaryPresenter() :
-                    selector.getSecondaryPresenter();
+                return primary ? selector.getPrimaryPresenter()
+                        : selector.getSecondaryPresenter();
             }
             return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
         }
@@ -295,7 +294,14 @@
      */
     public void showPrimaryActions(ViewHolder vh) {
         mPlaybackControlsPresenter.showPrimaryActions(vh.mControlsVh);
-        mPlaybackControlsPresenter.resetFocus(vh.mControlsVh);
+        if (vh.view.hasFocus()) {
+            mPlaybackControlsPresenter.resetFocus(vh.mControlsVh);
+        }
+    }
+
+    @Override
+    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
+        showPrimaryActions((ViewHolder) rowViewHolder);
     }
 
     private int getDefaultBackgroundColor(Context context) {
@@ -331,10 +337,10 @@
 
         vh.mControlsVh = (PlaybackControlsPresenter.ViewHolder)
                 mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock);
-        mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh, mProgressColorSet ?
-                mProgressColor : getDefaultProgressColor(vh.mControlsDock.getContext()));
-        mPlaybackControlsPresenter.setBackgroundColor(vh.mControlsVh, mBackgroundColorSet ?
-                mBackgroundColor : getDefaultBackgroundColor(vh.view.getContext()));
+        mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh, mProgressColorSet
+                ? mProgressColor : getDefaultProgressColor(vh.mControlsDock.getContext()));
+        mPlaybackControlsPresenter.setBackgroundColor(vh.mControlsVh, mBackgroundColorSet
+                ? mBackgroundColor : getDefaultBackgroundColor(vh.view.getContext()));
         vh.mControlsDock.addView(vh.mControlsVh.view);
 
         vh.mSecondaryControlsVh =
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackRowPresenter.java
new file mode 100644
index 0000000..bb1edb3
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackRowPresenter.java
@@ -0,0 +1,27 @@
+package android.support.v17.leanback.widget;
+
+import android.view.View;
+
+/**
+ * Subclass of {@link RowPresenter} that can define the desired behavior when the view
+ * reappears. This is presently used by {@link PlaybackControlsRowPresenter} to update the UI
+ * after we show/hide the controls view.
+ */
+public abstract class PlaybackRowPresenter extends RowPresenter {
+
+    /**
+     * This container is used for trapping click events and passing them to the
+     * playback controls.
+     */
+    public static class ViewHolder extends RowPresenter.ViewHolder {
+        public ViewHolder(View view) {
+            super(view);
+        }
+    }
+
+    /**
+     * Provides hook to update the UI when the view reappears.
+     */
+    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ResizingTextView.java b/v17/leanback/src/android/support/v17/leanback/widget/ResizingTextView.java
index 6b4f875..5888552 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ResizingTextView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ResizingTextView.java
@@ -15,13 +15,12 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.support.v17.leanback.R;
 import android.text.Layout;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.widget.TextView;
 
-import android.support.v17.leanback.R;
-
 /**
  * <p>A {@link android.widget.TextView} that adjusts text size automatically in response
  * to certain trigger conditions, such as text that wraps over multiple lines.</p>
@@ -230,8 +229,8 @@
                 remeasure = true;
             }
             // Check for other desired adjustments in addition to the text size
-            final float targetLineSpacingExtra = mDefaultLineSpacingExtra +
-                    mDefaultTextSize - mResizedTextSize;
+            final float targetLineSpacingExtra = mDefaultLineSpacingExtra
+                    + mDefaultTextSize - mResizedTextSize;
             if (mMaintainLineSpacing && getLineSpacingExtra() != targetLineSpacingExtra) {
                 setLineSpacing(targetLineSpacingExtra, getLineSpacingMultiplier());
                 remeasure = true;
@@ -252,8 +251,8 @@
                 setLineSpacing(mDefaultLineSpacingExtra, getLineSpacingMultiplier());
                 remeasure = true;
             }
-            if (getPaddingTop() != mDefaultPaddingTop ||
-                    getPaddingBottom() != mDefaultPaddingBottom) {
+            if (getPaddingTop() != mDefaultPaddingTop
+                    || getPaddingBottom() != mDefaultPaddingBottom) {
                 setPaddingTopAndBottom(mDefaultPaddingTop, mDefaultPaddingBottom);
                 remeasure = true;
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
index 53a51a4..c3a2cce 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
@@ -13,6 +13,8 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.graphics.Paint;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.R;
@@ -21,8 +23,6 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * RowHeaderPresenter provides a default presentation for {@link HeaderItem} using a
  * {@link RowHeaderView}. If a subclass creates its own view, the subclass must also override
@@ -77,10 +77,15 @@
         float mSelectLevel;
         int mOriginalTextColor;
         float mUnselectAlpha;
+        RowHeaderView mTitleView;
+        TextView mDescriptionView;
 
         public ViewHolder(View view) {
             super(view);
+            mTitleView = (RowHeaderView)view.findViewById(R.id.row_header);
+            mDescriptionView = (TextView)view.findViewById(R.id.row_header_description);
         }
+
         public final float getSelectLevel() {
             return mSelectLevel;
         }
@@ -88,11 +93,12 @@
 
     @Override
     public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        RowHeaderView headerView = (RowHeaderView) LayoutInflater.from(parent.getContext())
+        View root = LayoutInflater.from(parent.getContext())
                 .inflate(mLayoutResourceId, parent, false);
 
-        ViewHolder viewHolder = new ViewHolder(headerView);
-        viewHolder.mOriginalTextColor = headerView.getCurrentTextColor();
+        ViewHolder viewHolder = new ViewHolder(root);
+        viewHolder.mOriginalTextColor = viewHolder.mTitleView.getCurrentTextColor();
+
         viewHolder.mUnselectAlpha = parent.getResources().getFraction(
                 R.fraction.lb_browse_header_unselect_alpha, 1, 1);
         if (mAnimateSelect) {
@@ -104,22 +110,37 @@
     @Override
     public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
         HeaderItem headerItem = item == null ? null : ((Row) item).getHeaderItem();
+        RowHeaderPresenter.ViewHolder vh = (RowHeaderPresenter.ViewHolder)viewHolder;
         if (headerItem == null) {
-            ((RowHeaderView) viewHolder.view).setText(null);
+            vh.mTitleView.setText(null);
+
+            if (vh.mDescriptionView != null) {
+                vh.mDescriptionView.setText(null);
+            }
+
             viewHolder.view.setContentDescription(null);
             if (mNullItemVisibilityGone) {
                 viewHolder.view.setVisibility(View.GONE);
             }
         } else {
-            viewHolder.view.setVisibility(View.VISIBLE);
-            ((RowHeaderView) viewHolder.view).setText(headerItem.getName());
+            vh.mTitleView.setText(headerItem.getName());
+            if (vh.mDescriptionView != null) {
+                vh.mDescriptionView.setText(headerItem.getDescription());
+            }
             viewHolder.view.setContentDescription(headerItem.getContentDescription());
+            viewHolder.view.setVisibility(View.VISIBLE);
         }
     }
 
     @Override
     public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-        ((RowHeaderView) viewHolder.view).setText(null);
+        RowHeaderPresenter.ViewHolder vh = (ViewHolder)viewHolder;
+        vh.mTitleView.setText(null);
+
+        if (vh.mDescriptionView != null) {
+            vh.mDescriptionView.setText(null);
+        }
+
         if (mAnimateSelect) {
             setSelectLevel((ViewHolder) viewHolder, 0);
         }
@@ -138,8 +159,8 @@
      */
     protected void onSelectLevelChanged(ViewHolder holder) {
         if (mAnimateSelect) {
-            holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel *
-                    (1f - holder.mUnselectAlpha));
+            holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel
+                    * (1f - holder.mUnselectAlpha));
         }
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
index 68f0787..8267145 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
@@ -663,10 +663,10 @@
      *                       should be set to visible, false otherwise.
      */
     public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) {
-        if (holder.mHeaderViewHolder != null &&
-                holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
-            holder.mHeaderViewHolder.view.setVisibility(afterEntrance ?
-                    View.VISIBLE : View.INVISIBLE);
+        if (holder.mHeaderViewHolder != null
+                && holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
+            holder.mHeaderViewHolder.view.setVisibility(afterEntrance
+                    ? View.VISIBLE : View.INVISIBLE);
         }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ScaleFrameLayout.java b/v17/leanback/src/android/support/v17/leanback/widget/ScaleFrameLayout.java
index 50fd737..047d83c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ScaleFrameLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ScaleFrameLayout.java
@@ -13,6 +13,8 @@
  */
 package android.support.v17.leanback.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.RestrictTo;
@@ -22,8 +24,6 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * Subclass of FrameLayout that support scale layout area size for children.
  * @hide
@@ -99,9 +99,9 @@
 
         final int parentLeft, parentRight;
         final int layoutDirection = getLayoutDirection();
-        final float pivotX = (layoutDirection == View.LAYOUT_DIRECTION_RTL) ?
-                getWidth() - getPivotX() :
-                getPivotX();
+        final float pivotX = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
+                ? getWidth() - getPivotX()
+                : getPivotX();
         if (mLayoutScaleX != 1f) {
             parentLeft = getPaddingLeft() + (int)(pivotX - pivotX / mLayoutScaleX + 0.5f);
             parentRight = (int)(pivotX + (right - left - pivotX) / mLayoutScaleX + 0.5f)
@@ -143,8 +143,8 @@
 
                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.CENTER_HORIZONTAL:
-                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
-                                lp.leftMargin - lp.rightMargin;
+                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2
+                                + lp.leftMargin - lp.rightMargin;
                         break;
                     case Gravity.RIGHT:
                         childLeft = parentRight - width - lp.rightMargin;
@@ -159,8 +159,8 @@
                         childTop = parentTop + lp.topMargin;
                         break;
                     case Gravity.CENTER_VERTICAL:
-                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
-                                lp.topMargin - lp.bottomMargin;
+                        childTop = parentTop + (parentBottom - parentTop - height) / 2
+                                + lp.topMargin - lp.bottomMargin;
                         break;
                     case Gravity.BOTTOM:
                         childTop = parentBottom - height - lp.bottomMargin;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
index 09706bb..6d627b3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
@@ -29,26 +29,25 @@
 import android.speech.RecognitionListener;
 import android.speech.RecognizerIntent;
 import android.speech.SpeechRecognizer;
+import android.support.v17.leanback.R;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseIntArray;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ImageView;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-import android.support.v17.leanback.R;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -261,8 +260,8 @@
             public boolean onEditorAction(TextView textView, int action, KeyEvent keyEvent) {
                 if (DEBUG) Log.v(TAG, "onEditorAction: " + action + " event: " + keyEvent);
                 boolean handled = true;
-                if ((EditorInfo.IME_ACTION_SEARCH == action ||
-                        EditorInfo.IME_NULL == action) && null != mSearchBarListener) {
+                if ((EditorInfo.IME_ACTION_SEARCH == action
+                        || EditorInfo.IME_NULL == action) && null != mSearchBarListener) {
                     if (DEBUG) Log.v(TAG, "Action or enter pressed");
                     hideNativeKeyboard();
                     mHandler.postDelayed(new Runnable() {
@@ -388,6 +387,28 @@
     }
 
     /**
+     * Sets background color of not-listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+        if (mSpeechOrbView != null) {
+            mSpeechOrbView.setNotListeningOrbColors(colors);
+        }
+    }
+
+    /**
+     * Sets background color of listening state search orb.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
+        if (mSpeechOrbView != null) {
+            mSpeechOrbView.setListeningOrbColors(colors);
+        }
+    }
+
+    /**
      * Returns the current title
      */
     public String getTitle() {
@@ -595,8 +616,8 @@
                 mPermissionListener.requestAudioPermission();
                 return;
             } else {
-                throw new IllegalStateException(Manifest.permission.RECORD_AUDIO +
-                        " required for search");
+                throw new IllegalStateException(Manifest.permission.RECORD_AUDIO
+                        + " required for search");
             }
         }
 
@@ -712,8 +733,10 @@
             public void onPartialResults(Bundle bundle) {
                 ArrayList<String> results = bundle.getStringArrayList(
                         SpeechRecognizer.RESULTS_RECOGNITION);
-                if (DEBUG) Log.v(TAG, "onPartialResults " + bundle + " results " +
-                        (results == null ? results : results.size()));
+                if (DEBUG) {
+                    Log.v(TAG, "onPartialResults " + bundle + " results "
+                            + (results == null ? results : results.size()));
+                }
                 if (results == null || results.size() == 0) {
                     return;
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
index 3e9f320..156d42c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowOverlayContainer.java
@@ -14,17 +14,16 @@
 package android.support.v17.leanback.widget;
 
 import android.content.Context;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.support.annotation.ColorInt;
+import android.support.v17.leanback.R;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 
 /**
  * Provides an SDK version-independent wrapper to support shadows, color overlays, and rounded
@@ -288,10 +287,10 @@
             ViewGroup.LayoutParams wrapped_lp = new FrameLayout.LayoutParams(lp.width, lp.height);
             // Uses MATCH_PARENT for MATCH_PARENT, WRAP_CONTENT for WRAP_CONTENT and fixed size,
             // App can still change wrapped view fixed width/height afterwards.
-            lp.width = lp.width == LayoutParams.MATCH_PARENT ?
-                    LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
-            lp.height = lp.height == LayoutParams.MATCH_PARENT ?
-                    LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
+            lp.width = lp.width == LayoutParams.MATCH_PARENT
+                    ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
+            lp.height = lp.height == LayoutParams.MATCH_PARENT
+                    ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
             this.setLayoutParams(lp);
             addView(view, wrapped_lp);
         } else {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
index 52666ac..0300c6f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
@@ -82,9 +82,9 @@
                 mLastVisibleIndex = mFirstVisibleIndex = index;
             } else {
                 if (mReversedFlow) {
-                    edge = mProvider.getEdge(index + 1) + mMargin + size;
+                    edge = mProvider.getEdge(index + 1) + mSpacing + size;
                 } else {
-                    edge = mProvider.getEdge(index + 1) - mMargin - size;
+                    edge = mProvider.getEdge(index + 1) - mSpacing - size;
                 }
                 mFirstVisibleIndex = index;
             }
@@ -115,9 +115,9 @@
                 mLastVisibleIndex = mFirstVisibleIndex = index;
             } else {
                 if (mReversedFlow) {
-                    edge = mProvider.getEdge(index - 1) - mProvider.getSize(index - 1) - mMargin;
+                    edge = mProvider.getEdge(index - 1) - mProvider.getSize(index - 1) - mSpacing;
                 } else {
-                    edge = mProvider.getEdge(index - 1) + mProvider.getSize(index - 1) + mMargin;
+                    edge = mProvider.getEdge(index - 1) + mProvider.getSize(index - 1) + mSpacing;
                 }
                 mLastVisibleIndex = index;
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java b/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java
index 67946d5..90df896 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java
@@ -11,8 +11,8 @@
  */
 public class SpeechOrbView extends SearchOrbView {
     private final float mSoundLevelMaxZoom;
-    private final Colors mListeningOrbColors;
-    private final Colors mNotListeningOrbColors;
+    private Colors mListeningOrbColors;
+    private Colors mNotListeningOrbColors;
 
     private int mCurrentLevel = 0;
     private boolean mListening = false;
@@ -48,6 +48,24 @@
     }
 
     /**
+     * Sets default listening state orb color.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setListeningOrbColors(Colors colors) {
+        mListeningOrbColors = colors;
+    }
+
+    /**
+     * Sets default not-listening state orb color.
+     *
+     * @param colors SearchOrbView.Colors.
+     */
+    public void setNotListeningOrbColors(Colors colors) {
+        mNotListeningOrbColors = colors;
+    }
+
+    /**
      * Sets the view to display listening state.
      */
     public void showListening() {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
index 2fef48e..1d731db 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
@@ -218,9 +218,9 @@
         }
         // Assuming the cachedIndex is next to item on the same row, so the
         // sum of offset of [cachedIndex + 1, itemIndex] should be size of the
-        // cached item plus margin.
-        int offset = isReversedFlow() ?  -getLocation(cachedIndex).size - mMargin:
-                getLocation(cachedIndex).size + mMargin;
+        // cached item plus spacing.
+        int offset = isReversedFlow() ?  -getLocation(cachedIndex).size - mSpacing:
+                getLocation(cachedIndex).size + mSpacing;
         for (int i = cachedIndex + 1; i <= getLastIndex(); i++) {
             offset -= getLocation(i).offset;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
index 975c43c..f4c0141 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGridDefault.java
@@ -289,13 +289,13 @@
                     if (rowIndex == 0) {
                         location = mReversedFlow ? getRowMin(mNumRows - 1) : getRowMax(mNumRows - 1);
                         if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
-                            location = location + (mReversedFlow ? -mMargin : mMargin);
+                            location = location + (mReversedFlow ? -mSpacing : mSpacing);
                         }
                     } else {
                         location = mReversedFlow ? getRowMax(rowIndex - 1) : getRowMin(rowIndex - 1);
                     }
                 } else {
-                    location = location + (mReversedFlow ? -mMargin : mMargin);
+                    location = location + (mReversedFlow ? -mSpacing : mSpacing);
                 }
                 int size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
                 filledOne = true;
@@ -307,7 +307,7 @@
                         if (itemIndex == count || (!oneColumnMode && checkAppendOverLimit(toLimit))) {
                             return filledOne;
                         }
-                        location = location + (mReversedFlow ? - size - mMargin : size + mMargin);
+                        location = location + (mReversedFlow ? - size - mSpacing : size + mSpacing);
                         size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
                     }
                 } else {
@@ -390,13 +390,13 @@
                     if (rowIndex == mNumRows - 1) {
                         location = mReversedFlow ? getRowMax(0) : getRowMin(0);
                         if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
-                            location = location + (mReversedFlow ? mMargin : -mMargin);
+                            location = location + (mReversedFlow ? mSpacing : -mSpacing);
                         }
                     } else {
                         location = mReversedFlow ? getRowMin(rowIndex + 1) : getRowMax(rowIndex + 1);
                     }
                 } else {
-                    location = location + (mReversedFlow ? mMargin : -mMargin);
+                    location = location + (mReversedFlow ? mSpacing : -mSpacing);
                 }
                 int size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
                 filledOne = true;
@@ -409,7 +409,7 @@
                         if (itemIndex < 0 || (!oneColumnMode && checkPrependOverLimit(toLimit))) {
                             return filledOne;
                         }
-                        location = location + (mReversedFlow ? size + mMargin : -size - mMargin);
+                        location = location + (mReversedFlow ? size + mSpacing : -size - mSpacing);
                         size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
                     }
                 } else {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
index c687e07..f12d8d0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
@@ -45,8 +45,8 @@
             if (focused != mTitleView && direction == View.FOCUS_UP) {
                 return mTitleView;
             }
-            final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
-                    View.LAYOUT_DIRECTION_RTL;
+            final boolean isRtl = ViewCompat.getLayoutDirection(focused)
+                    == View.LAYOUT_DIRECTION_RTL;
             final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
             if (mTitleView.hasFocus() && (direction == View.FOCUS_DOWN || direction == forward)) {
                 return mSceneRoot;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java b/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
index 0e8ce24..9a7d424 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/WindowAlignment.java
@@ -14,15 +14,12 @@
 
 package android.support.v17.leanback.widget;
 
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_LOW_EDGE;
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_HIGH_EDGE;
 import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_BOTH_EDGE;
+import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_HIGH_EDGE;
+import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_LOW_EDGE;
 import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED;
-
 import static android.support.v7.widget.RecyclerView.HORIZONTAL;
 
-import android.view.View;
-
 /**
  * Maintains Window Alignment information of two axis.
  */
@@ -227,8 +224,8 @@
             int afterMiddlePosition = clientSize - middlePosition;
             boolean isMinUnknown = isMinUnknown();
             boolean isMaxUnknown = isMaxUnknown();
-            if (!isMinUnknown && !isMaxUnknown &&
-                    (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
+            if (!isMinUnknown && !isMaxUnknown
+                    && (mWindowAlignment & WINDOW_ALIGN_BOTH_EDGE) == WINDOW_ALIGN_BOTH_EDGE) {
                 if (mMaxEdge - mMinEdge <= clientSize) {
                     // total children size is less than view port and we want to align
                     // both edge:  align first child to start edge of view port
@@ -264,8 +261,7 @@
 
         @Override
         public String toString() {
-            return "center: " + mScrollCenter + " min:" + mMinEdge +
-                    " max:" + mMaxEdge;
+            return "center: " + mScrollCenter + " min:" + mMinEdge + " max:" + mMaxEdge;
         }
 
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java b/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
index ca539a8..55ec6de 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
@@ -479,8 +479,9 @@
 
     private void updateColumnSize(VerticalGridView columnView) {
         ViewGroup.LayoutParams lp = columnView.getLayoutParams();
-        lp.height = (int) (getPickerItemHeightPixels() * (isActivated() ?
-                getActivatedVisibleItemCount() : getVisibleItemCount()));
+        float itemCount = isActivated() ? getActivatedVisibleItemCount() : getVisibleItemCount();
+        lp.height = (int) (getPickerItemHeightPixels() * itemCount
+                + columnView.getVerticalSpacing() * (itemCount - 1));
         columnView.setLayoutParams(lp);
     }
 
diff --git a/v17/leanback/tests/Android.mk b/v17/leanback/tests/Android.mk
index db64d88..6c1a709 100644
--- a/v17/leanback/tests/Android.mk
+++ b/v17/leanback/tests/Android.mk
@@ -36,7 +36,7 @@
         android-support-v7-recyclerview \
         android-support-v17-leanback \
         android-support-test \
-        mockito-target
+        mockito-target-minus-junit4
 
 LOCAL_PACKAGE_NAME := AndroidLeanbackTests
 
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index 5c421e3..e8b81b5 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -42,10 +42,39 @@
                   android:theme="@style/Theme.Leanback.Browse"
                   android:exported="true" />
 
+        <activity android:name="android.support.v17.leanback.app.DetailsFragmentTestActivity"
+                  android:theme="@style/Theme.Leanback"
+                  android:exported="true" />
+
         <activity android:name="android.support.v17.leanback.app.BrowseSupportFragmentTestActivity"
                   android:theme="@style/Theme.Leanback.Browse"
                   android:exported="true" />
 
+        <activity android:name="android.support.v17.leanback.app.GuidedStepFragmentTestActivity"
+                  android:theme="@style/Theme.Leanback.GuidedStep"
+                  android:exported="true" />
+
+        <activity android:name="android.support.v17.leanback.app.GuidedStepSupportFragmentTestActivity"
+                  android:theme="@style/Theme.Leanback.GuidedStep"
+                  android:exported="true" />
+
+        <activity android:name="android.support.v17.leanback.app.PlaybackOverlayTestActivity"
+                  android:theme="@style/Theme.Leanback"
+                  android:exported="true" />
+        <activity
+            android:name="android.support.v17.leanback.app.VerticalGridFragmentTest$ImmediateRemoveFragmentActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Leanback.VerticalGrid" />
+
+        <activity
+            android:name="android.support.v17.leanback.app.VerticalGridSupportFragmentTest$ImmediateRemoveFragmentActivity"
+            android:exported="true"
+            android:theme="@style/Theme.Leanback.VerticalGrid" />
+
+        <activity android:name="android.support.v17.leanback.app.VideoFragmentTestActivity"
+            android:theme="@style/Theme.Leanback"
+            android:exported="true" />
+
     </application>
 
 </manifest>
diff --git a/v17/leanback/tests/generatev4.py b/v17/leanback/tests/generatev4.py
index 6d72c8b..3e25503 100755
--- a/v17/leanback/tests/generatev4.py
+++ b/v17/leanback/tests/generatev4.py
@@ -19,10 +19,11 @@
 
 print "Generate v4 fragment related code for leanback"
 
-files = ['BrowseTest']
+files = ['BrowseTest', 'GuidedStepTest']
 
 cls = ['BrowseTest', 'Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers',
-      'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded']
+      'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded',
+      'GuidedStepTest', 'GuidedStep']
 
 for w in files:
     print "copy {}Fragment to {}SupportFragment".format(w, w)
@@ -30,6 +31,7 @@
     file = open('java/android/support/v17/leanback/app/{}Fragment.java'.format(w), 'r')
     outfile = open('java/android/support/v17/leanback/app/{}SupportFragment.java'.format(w), 'w')
 
+    outfile.write("// CHECKSTYLE:OFF Generated code\n")
     outfile.write("/* This file is auto-generated from {}Fragment.java.  DO NOT MODIFY. */\n\n".format(w))
 
     for line in file:
@@ -37,11 +39,36 @@
             line = line.replace('{}Fragment'.format(w), '{}SupportFragment'.format(w))
         line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
         line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+        line = line.replace('Activity getActivity()', 'FragmentActivity getActivity()')
         outfile.write(line)
     file.close()
     outfile.close()
 
-testcls = ['Browse']
+testcls = ['GuidedStep']
+
+for w in testcls:
+    print "copy {}FrgamentTestBase to {}SupportFragmentTestBase".format(w, w)
+
+    file = open('java/android/support/v17/leanback/app/{}FragmentTestBase.java'.format(w), 'r')
+    outfile = open('java/android/support/v17/leanback/app/{}SupportFragmentTestBase.java'.format(w), 'w')
+
+    outfile.write("// CHECKSTYLE:OFF Generated code\n")
+    outfile.write("/* This file is auto-generated from {}FrgamentTestBase.java.  DO NOT MODIFY. */\n\n".format(w))
+
+    for line in file:
+        for w in cls:
+            line = line.replace('{}Fragment'.format(w), '{}SupportFragment'.format(w))
+        for w in testcls:
+            line = line.replace('{}FragmentTestBase'.format(w), '{}SupportFragmentTestBase'.format(w))
+            line = line.replace('{}FragmentTestActivity'.format(w), '{}SupportFragmentTestActivity'.format(w))
+            line = line.replace('{}TestFragment'.format(w), '{}TestSupportFragment'.format(w))
+        line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
+        line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+        outfile.write(line)
+    file.close()
+    outfile.close()
+
+testcls = ['Browse', 'GuidedStep', 'VerticalGrid']
 
 for w in testcls:
     print "copy {}FrgamentTest to {}SupportFragmentTest".format(w, w)
@@ -49,38 +76,87 @@
     file = open('java/android/support/v17/leanback/app/{}FragmentTest.java'.format(w), 'r')
     outfile = open('java/android/support/v17/leanback/app/{}SupportFragmentTest.java'.format(w), 'w')
 
-    outfile.write("/* This file is auto-generated from {}FrgamentTest.java.  DO NOT MODIFY. */\n\n".format(w))
+    outfile.write("// CHECKSTYLE:OFF Generated code\n")
+    outfile.write("/* This file is auto-generated from {}FragmentTest.java.  DO NOT MODIFY. */\n\n".format(w))
 
     for line in file:
         for w in cls:
             line = line.replace('{}Fragment'.format(w), '{}SupportFragment'.format(w))
         for w in testcls:
+            line = line.replace('{}FragmentTestBase'.format(w), '{}SupportFragmentTestBase'.format(w))
             line = line.replace('{}FragmentTest'.format(w), '{}SupportFragmentTest'.format(w))
             line = line.replace('{}FragmentTestActivity'.format(w), '{}SupportFragmentTestActivity'.format(w))
             line = line.replace('{}TestFragment'.format(w), '{}TestSupportFragment'.format(w))
+        line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
+        line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+	line = line.replace('extends Activity', 'extends FragmentActivity')
+	line = line.replace('Activity.this.getFragmentManager', 'Activity.this.getSupportFragmentManager')
         outfile.write(line)
     file.close()
     outfile.close()
 
+testcls = ['Browse', 'GuidedStep']
 
-print "copy BrowseFragmentTestActivity to BrowseSupportFragmentTestActivity"
-file = open('java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java', 'r')
-outfile = open('java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java', 'w')
-outfile.write("/* This file is auto-generated from BrowseFragmentTestActivity.java.  DO NOT MODIFY. */\n\n")
+for w in testcls:
+    print "copy {}FragmentTestActivity to {}SupportFragmentTestActivity".format(w, w)
+    file = open('java/android/support/v17/leanback/app/{}FragmentTestActivity.java'.format(w), 'r')
+    outfile = open('java/android/support/v17/leanback/app/{}SupportFragmentTestActivity.java'.format(w), 'w')
+    outfile.write("// CHECKSTYLE:OFF Generated code\n")
+    outfile.write("/* This file is auto-generated from {}FragmentTestActivity.java.  DO NOT MODIFY. */\n\n".format(w))
+    for line in file:
+        line = line.replace('{}TestFragment'.format(w), '{}TestSupportFragment'.format(w))
+        line = line.replace('{}FragmentTestActivity'.format(w), '{}SupportFragmentTestActivity'.format(w))
+        line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
+        line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
+        line = line.replace('extends Activity', 'extends FragmentActivity')
+        line = line.replace('getFragmentManager', 'getSupportFragmentManager')
+        outfile.write(line)
+    file.close()
+    outfile.close()
+
+print "copy ParallaxIntEffectTest to ParallaxFloatEffectTest"
+file = open('java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java', 'r')
+outfile = open('java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java', 'w')
+outfile.write("// CHECKSTYLE:OFF Generated code\n")
+outfile.write("/* This file is auto-generated from ParallaxIntEffectTest.java.  DO NOT MODIFY. */\n\n")
 for line in file:
-    line = line.replace('BrowseTestFragment', 'BrowseTestSupportFragment')
-    line = line.replace('BrowseFragmentTestActivity', 'BrowseSupportFragmentTestActivity')
-    line = line.replace('android.app.Fragment', 'android.support.v4.app.Fragment')
-    line = line.replace('android.app.Activity', 'android.support.v4.app.FragmentActivity')
-    line = line.replace('extends Activity', 'extends FragmentActivity')
-    line = line.replace('getFragmentManager', 'getSupportFragmentManager')
+    line = line.replace('IntEffect', 'FloatEffect')
+    line = line.replace('IntSource', 'FloatSource')
+    line = line.replace('IntProperty', 'FloatProperty')
+    line = line.replace('IntValue', 'FloatValue')
+    line = line.replace('intValue()', 'floatValue()')
+    line = line.replace('int getMaxParentVisibleSize', 'float getMaxParentVisibleSize')
+    line = line.replace('int screenMax', 'float screenMax')
+    line = line.replace('assertEquals((int)', 'assertFloatEquals((float)')
+    line = line.replace('(int)', '(float)')
     outfile.write(line)
 file.close()
 outfile.close()
 
+
+print "copy ParallaxIntSourceTest to ParallaxFloatSourceTest"
+file = open('java/android/support/v17/leanback/widget/ParallaxIntSourceTest.java', 'r')
+outfile = open('java/android/support/v17/leanback/widget/ParallaxFloatSourceTest.java', 'w')
+outfile.write("// CHECKSTYLE:OFF Generated code\n")
+outfile.write("/* This file is auto-generated from ParallaxIntSourceTest.java.  DO NOT MODIFY. */\n\n")
+for line in file:
+    line = line.replace('IntSource', 'FloatSource')
+    line = line.replace('IntProperty', 'FloatProperty')
+    line = line.replace('IntValue', 'FloatValue')
+    line = line.replace('intValue()', 'floatValue()')
+    line = line.replace('int getMaxParentVisibleSize', 'float getMaxParentVisibleSize')
+    line = line.replace('int screenMax', 'float screenMax')
+    line = line.replace('assertEquals((int)', 'assertFloatEquals((float)')
+    line = line.replace('(int)', '(float)')
+    outfile.write(line)
+file.close()
+outfile.close()
+
+
 print "copy PlaybackControlGlueTest to PlaybackControlSupportGlueTest"
 file = open('java/android/support/v17/leanback/app/PlaybackControlGlueTest.java', 'r')
 outfile = open('java/android/support/v17/leanback/app/PlaybackControlSupportGlueTest.java', 'w')
+outfile.write("// CHECKSTYLE:OFF Generated code\n")
 outfile.write("/* This file is auto-generated from PlaybackControlGlueTest.java.  DO NOT MODIFY. */\n\n")
 for line in file:
     line = line.replace('PlaybackControlGlue', 'PlaybackControlSupportGlue')
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
index 4fd4168..0008f93 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
@@ -15,43 +15,56 @@
  */
 package android.support.v17.leanback.app;
 
-import android.support.v17.leanback.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.KeyEvent;
-
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.ListRowPresenter;
-import android.content.Intent;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.espresso.action.ViewActions;
-import org.mockito.Mockito;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.ItemBridgeAdapter;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v7.widget.RecyclerView;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BrowseFragmentTest {
 
+    static final String TAG = "BrowseFragmentTest";
     static final long TRANSITION_LENGTH = 1000;
     static final long HORIZONTAL_SCROLL_WAIT = 2000;
 
     @Rule
-    public ActivityTestRule<BrowseFragmentTestActivity> activityTestRule
-            = new ActivityTestRule<>(BrowseFragmentTestActivity.class, false, false);
+    public ActivityTestRule<BrowseFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(BrowseFragmentTestActivity.class, false, false);
     private BrowseFragmentTestActivity mActivity;
 
+    @After
+    public void afterTest() throws Throwable {
+        activityTestRule.runOnUiThread(new Runnable() {
+            public void run() {
+                if (mActivity != null) {
+                    mActivity.finish();
+                    mActivity = null;
+                }
+            }
+        });
+    }
+
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
         final long dataLoadingDelay = 1000;
@@ -62,6 +75,7 @@
 
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
+        assertNotNull(mActivity.getBrowseTestFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
         Thread.sleep(TRANSITION_LENGTH);
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
@@ -77,6 +91,7 @@
 
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
+        assertNotNull(mActivity.getBrowseTestFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
         Thread.sleep(TRANSITION_LENGTH);
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
@@ -90,6 +105,7 @@
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
         mActivity = activityTestRule.launchActivity(intent);
 
+        assertNull(mActivity.getBrowseTestFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
     }
 
@@ -121,13 +137,20 @@
 
         verify(itemTask, timeout(5000).times(1)).run(any(Presenter.ViewHolder.class));
 
-        ListRowPresenter.ViewHolder row = (ListRowPresenter.ViewHolder) mActivity
-                .getBrowseTestFragment().getRowsFragment().getRowViewHolder(selectRow);
-        assertEquals(selectItem, row.getGridView().getSelectedPosition());
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ListRowPresenter.ViewHolder row = (ListRowPresenter.ViewHolder) mActivity
+                        .getBrowseTestFragment().getRowsFragment().getRowViewHolder(selectRow);
+                assertNotNull(dumpRecyclerView(mActivity.getBrowseTestFragment().getGridView()), row);
+                assertNotNull(row.getGridView());
+                assertEquals(selectItem, row.getGridView().getSelectedPosition());
+            }
+        });
     }
 
     @Test
-    public void activityRecreate_notCrash() throws InterruptedException {
+    public void activityRecreate_notCrash() throws Throwable {
         final long dataLoadingDelay = 1000;
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
@@ -138,7 +161,7 @@
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
         InstrumentationRegistry.getInstrumentation().callActivityOnRestart(mActivity);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        activityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.recreate();
@@ -157,13 +180,31 @@
         private final BrowseFragmentTestActivity activity;
         private final int expectedRow;
 
-        ItemSelectionTask(BrowseFragmentTestActivity activity, int expectedRow) {
+        public ItemSelectionTask(BrowseFragmentTestActivity activity, int expectedRow) {
             this.activity = activity;
             this.expectedRow = expectedRow;
         }
 
         public void run(Presenter.ViewHolder holder) {
-            assertEquals(expectedRow, activity.getBrowseTestFragment().getSelectedPosition());
+            android.util.Log.d(TAG, dumpRecyclerView(activity.getBrowseTestFragment()
+                    .getGridView()));
+            android.util.Log.d(TAG, "Row " + expectedRow + " " + activity.getBrowseTestFragment()
+                    .getRowsFragment().getRowViewHolder(expectedRow), new Exception());
         }
     }
+
+    static String dumpRecyclerView(RecyclerView recyclerView) {
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < recyclerView.getChildCount(); i++) {
+            View child = recyclerView.getChildAt(i);
+            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                    recyclerView.getChildViewHolder(child);
+            b.append("child").append(i).append(":").append(vh);
+            if (vh != null) {
+                b.append(",").append(vh.getViewHolder());
+            }
+            b.append(";");
+        }
+        return b.toString();
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
index 5e52a22..e01a168 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTestActivity.java
@@ -36,25 +36,19 @@
         super.onCreate(savedInstanceState);
         Intent intent = getIntent();
 
-        BrowseTestFragment.NUM_ROWS = intent.getIntExtra(EXTRA_NUM_ROWS,
-                BrowseTestFragment.DEFAULT_NUM_ROWS);
-        BrowseTestFragment.REPEAT_PER_ROW = intent.getIntExtra(EXTRA_REPEAT_PER_ROW,
-                BrowseTestFragment.DEFAULT_REPEAT_PER_ROW);
-        BrowseTestFragment.LOAD_DATA_DELAY = intent.getLongExtra(EXTRA_LOAD_DATA_DELAY,
-                BrowseTestFragment.DEFAULT_LOAD_DATA_DELAY);
-        BrowseTestFragment.TEST_ENTRANCE_TRANSITION = intent.getBooleanExtra(
-                EXTRA_TEST_ENTRANCE_TRANSITION,
-                BrowseTestFragment.DEFAULT_TEST_ENTRANCE_TRANSITION);
-        BrowseTestFragment.SET_ADAPTER_AFTER_DATA_LOAD = intent.getBooleanExtra(
-                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
-                BrowseTestFragment.DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
         setContentView(R.layout.browse);
-        FragmentTransaction ft = getFragmentManager().beginTransaction();
-        ft.replace(R.id.main_frame, new BrowseTestFragment());
-        if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) {
-            ft.addToBackStack(null);
+        if (savedInstanceState == null) {
+            Bundle arguments = new Bundle();
+            arguments.putAll(intent.getExtras());
+            BrowseTestFragment fragment = new BrowseTestFragment();
+            fragment.setArguments(arguments);
+            FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.replace(R.id.main_frame, fragment);
+            if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) {
+                ft.addToBackStack(null);
+            }
+            ft.commit();
         }
-        ft.commit();
     }
 
     public BrowseTestFragment getBrowseTestFragment() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
index abb2dc9..c7862c9 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
@@ -1,4 +1,5 @@
-/* This file is auto-generated from BrowseFrgamentTest.java.  DO NOT MODIFY. */
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from BrowseFragmentTest.java.  DO NOT MODIFY. */
 
 /*
  * Copyright (C) 2015 The Android Open Source Project
@@ -17,43 +18,56 @@
  */
 package android.support.v17.leanback.app;
 
-import android.support.v17.leanback.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.KeyEvent;
-
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.ListRowPresenter;
-import android.content.Intent;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.espresso.action.ViewActions;
-import org.mockito.Mockito;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.ItemBridgeAdapter;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v7.widget.RecyclerView;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BrowseSupportFragmentTest {
 
+    static final String TAG = "BrowseSupportFragmentTest";
     static final long TRANSITION_LENGTH = 1000;
     static final long HORIZONTAL_SCROLL_WAIT = 2000;
 
     @Rule
-    public ActivityTestRule<BrowseSupportFragmentTestActivity> activityTestRule
-            = new ActivityTestRule<>(BrowseSupportFragmentTestActivity.class, false, false);
+    public ActivityTestRule<BrowseSupportFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(BrowseSupportFragmentTestActivity.class, false, false);
     private BrowseSupportFragmentTestActivity mActivity;
 
+    @After
+    public void afterTest() throws Throwable {
+        activityTestRule.runOnUiThread(new Runnable() {
+            public void run() {
+                if (mActivity != null) {
+                    mActivity.finish();
+                    mActivity = null;
+                }
+            }
+        });
+    }
+
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
         final long dataLoadingDelay = 1000;
@@ -64,6 +78,7 @@
 
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
+        assertNotNull(mActivity.getBrowseTestSupportFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
         Thread.sleep(TRANSITION_LENGTH);
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
@@ -79,6 +94,7 @@
 
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
+        assertNotNull(mActivity.getBrowseTestSupportFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
         Thread.sleep(TRANSITION_LENGTH);
         sendKeys(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BACK);
@@ -92,6 +108,7 @@
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
         mActivity = activityTestRule.launchActivity(intent);
 
+        assertNull(mActivity.getBrowseTestSupportFragment().getMainFragment());
         sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
     }
 
@@ -123,13 +140,20 @@
 
         verify(itemTask, timeout(5000).times(1)).run(any(Presenter.ViewHolder.class));
 
-        ListRowPresenter.ViewHolder row = (ListRowPresenter.ViewHolder) mActivity
-                .getBrowseTestSupportFragment().getRowsSupportFragment().getRowViewHolder(selectRow);
-        assertEquals(selectItem, row.getGridView().getSelectedPosition());
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ListRowPresenter.ViewHolder row = (ListRowPresenter.ViewHolder) mActivity
+                        .getBrowseTestSupportFragment().getRowsSupportFragment().getRowViewHolder(selectRow);
+                assertNotNull(dumpRecyclerView(mActivity.getBrowseTestSupportFragment().getGridView()), row);
+                assertNotNull(row.getGridView());
+                assertEquals(selectItem, row.getGridView().getSelectedPosition());
+            }
+        });
     }
 
     @Test
-    public void activityRecreate_notCrash() throws InterruptedException {
+    public void activityRecreate_notCrash() throws Throwable {
         final long dataLoadingDelay = 1000;
         Intent intent = new Intent();
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
@@ -140,7 +164,7 @@
         Thread.sleep(dataLoadingDelay + TRANSITION_LENGTH);
 
         InstrumentationRegistry.getInstrumentation().callActivityOnRestart(mActivity);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        activityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.recreate();
@@ -159,13 +183,31 @@
         private final BrowseSupportFragmentTestActivity activity;
         private final int expectedRow;
 
-        ItemSelectionTask(BrowseSupportFragmentTestActivity activity, int expectedRow) {
+        public ItemSelectionTask(BrowseSupportFragmentTestActivity activity, int expectedRow) {
             this.activity = activity;
             this.expectedRow = expectedRow;
         }
 
         public void run(Presenter.ViewHolder holder) {
-            assertEquals(expectedRow, activity.getBrowseTestSupportFragment().getSelectedPosition());
+            android.util.Log.d(TAG, dumpRecyclerView(activity.getBrowseTestSupportFragment()
+                    .getGridView()));
+            android.util.Log.d(TAG, "Row " + expectedRow + " " + activity.getBrowseTestSupportFragment()
+                    .getRowsSupportFragment().getRowViewHolder(expectedRow), new Exception());
         }
     }
+
+    static String dumpRecyclerView(RecyclerView recyclerView) {
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < recyclerView.getChildCount(); i++) {
+            View child = recyclerView.getChildAt(i);
+            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
+                    recyclerView.getChildViewHolder(child);
+            b.append("child").append(i).append(":").append(vh);
+            if (vh != null) {
+                b.append(",").append(vh.getViewHolder());
+            }
+            b.append(";");
+        }
+        return b.toString();
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
index d92b58d..b24d466 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTestActivity.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseFragmentTestActivity.java.  DO NOT MODIFY. */
 
 /*
@@ -38,25 +39,19 @@
         super.onCreate(savedInstanceState);
         Intent intent = getIntent();
 
-        BrowseTestSupportFragment.NUM_ROWS = intent.getIntExtra(EXTRA_NUM_ROWS,
-                BrowseTestSupportFragment.DEFAULT_NUM_ROWS);
-        BrowseTestSupportFragment.REPEAT_PER_ROW = intent.getIntExtra(EXTRA_REPEAT_PER_ROW,
-                BrowseTestSupportFragment.DEFAULT_REPEAT_PER_ROW);
-        BrowseTestSupportFragment.LOAD_DATA_DELAY = intent.getLongExtra(EXTRA_LOAD_DATA_DELAY,
-                BrowseTestSupportFragment.DEFAULT_LOAD_DATA_DELAY);
-        BrowseTestSupportFragment.TEST_ENTRANCE_TRANSITION = intent.getBooleanExtra(
-                EXTRA_TEST_ENTRANCE_TRANSITION,
-                BrowseTestSupportFragment.DEFAULT_TEST_ENTRANCE_TRANSITION);
-        BrowseTestSupportFragment.SET_ADAPTER_AFTER_DATA_LOAD = intent.getBooleanExtra(
-                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
-                BrowseTestSupportFragment.DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
         setContentView(R.layout.browse);
-        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-        ft.replace(R.id.main_frame, new BrowseTestSupportFragment());
-        if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) {
-            ft.addToBackStack(null);
+        if (savedInstanceState == null) {
+            Bundle arguments = new Bundle();
+            arguments.putAll(intent.getExtras());
+            BrowseTestSupportFragment fragment = new BrowseTestSupportFragment();
+            fragment.setArguments(arguments);
+            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+            ft.replace(R.id.main_frame, fragment);
+            if (intent.getBooleanExtra(EXTRA_ADD_TO_BACKSTACK, false)) {
+                ft.addToBackStack(null);
+            }
+            ft.commit();
         }
-        ft.commit();
     }
 
     public BrowseTestSupportFragment getBrowseTestSupportFragment() {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
index 62fa32e..094fdc3 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
@@ -13,6 +13,12 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_NUM_ROWS;
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD;
+import static android.support.v17.leanback.app.BrowseFragmentTestActivity.EXTRA_TEST_ENTRANCE_TRANSITION;
+
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -37,23 +43,33 @@
     final static boolean DEFAULT_TEST_ENTRANCE_TRANSITION = true;
     final static boolean DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD = false;
 
-    static int NUM_ROWS = DEFAULT_NUM_ROWS;
-    static int REPEAT_PER_ROW = DEFAULT_REPEAT_PER_ROW;
-    static long LOAD_DATA_DELAY = DEFAULT_LOAD_DATA_DELAY;
-    static boolean TEST_ENTRANCE_TRANSITION = DEFAULT_TEST_ENTRANCE_TRANSITION;
-    static boolean SET_ADAPTER_AFTER_DATA_LOAD = DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD;
-
     private ArrayObjectAdapter mRowsAdapter;
 
     // For good performance, it's important to use a single instance of
     // a card presenter for all rows using that presenter.
     final static StringPresenter sCardPresenter = new StringPresenter();
 
+    int NUM_ROWS;
+    int REPEAT_PER_ROW;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
+        Bundle arguments = getArguments();
+        NUM_ROWS = arguments.getInt(EXTRA_NUM_ROWS, BrowseTestFragment.DEFAULT_NUM_ROWS);
+        REPEAT_PER_ROW = arguments.getInt(EXTRA_REPEAT_PER_ROW,
+                DEFAULT_REPEAT_PER_ROW);
+        long LOAD_DATA_DELAY = arguments.getLong(EXTRA_LOAD_DATA_DELAY,
+                DEFAULT_LOAD_DATA_DELAY);
+        boolean TEST_ENTRANCE_TRANSITION = arguments.getBoolean(
+                EXTRA_TEST_ENTRANCE_TRANSITION,
+                DEFAULT_TEST_ENTRANCE_TRANSITION);
+        final boolean SET_ADAPTER_AFTER_DATA_LOAD = arguments.getBoolean(
+                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
+                DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
+
         if (!SET_ADAPTER_AFTER_DATA_LOAD) {
             setupRows();
         }
@@ -73,7 +89,9 @@
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
-                Log.i(TAG, "onItemSelected: " + item + " row " + row);
+                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
+                        + " " + rowViewHolder
+                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
             }
         });
         if (TEST_ENTRANCE_TRANSITION) {
@@ -86,6 +104,9 @@
         new Handler().postDelayed(new Runnable() {
             @Override
             public void run() {
+                if (getActivity() == null || getActivity().isDestroyed()) {
+                    return;
+                }
                 if (SET_ADAPTER_AFTER_DATA_LOAD) {
                     setupRows();
                 }
@@ -106,15 +127,16 @@
     private void loadData() {
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
+            int index = 0;
             for (int j = 0; j < REPEAT_PER_ROW; ++j) {
-                listRowAdapter.add("Hello world");
-                listRowAdapter.add("This is a test");
-                listRowAdapter.add("Android TV");
-                listRowAdapter.add("Leanback");
-                listRowAdapter.add("Hello world");
-                listRowAdapter.add("Android TV");
-                listRowAdapter.add("Leanback");
-                listRowAdapter.add("GuidedStepFragment");
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepFragment-" + (index++));
             }
             HeaderItem header = new HeaderItem(i, "Row " + i);
             mRowsAdapter.add(new ListRow(header, listRowAdapter));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
index 6031dfa..95dfc09 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from BrowseTestFragment.java.  DO NOT MODIFY. */
 
 /*
@@ -15,6 +16,12 @@
  */
 package android.support.v17.leanback.app;
 
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_NUM_ROWS;
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD;
+import static android.support.v17.leanback.app.BrowseSupportFragmentTestActivity.EXTRA_TEST_ENTRANCE_TRANSITION;
+
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -39,23 +46,33 @@
     final static boolean DEFAULT_TEST_ENTRANCE_TRANSITION = true;
     final static boolean DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD = false;
 
-    static int NUM_ROWS = DEFAULT_NUM_ROWS;
-    static int REPEAT_PER_ROW = DEFAULT_REPEAT_PER_ROW;
-    static long LOAD_DATA_DELAY = DEFAULT_LOAD_DATA_DELAY;
-    static boolean TEST_ENTRANCE_TRANSITION = DEFAULT_TEST_ENTRANCE_TRANSITION;
-    static boolean SET_ADAPTER_AFTER_DATA_LOAD = DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD;
-
     private ArrayObjectAdapter mRowsAdapter;
 
     // For good performance, it's important to use a single instance of
     // a card presenter for all rows using that presenter.
     final static StringPresenter sCardPresenter = new StringPresenter();
 
+    int NUM_ROWS;
+    int REPEAT_PER_ROW;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
 
+        Bundle arguments = getArguments();
+        NUM_ROWS = arguments.getInt(EXTRA_NUM_ROWS, BrowseTestSupportFragment.DEFAULT_NUM_ROWS);
+        REPEAT_PER_ROW = arguments.getInt(EXTRA_REPEAT_PER_ROW,
+                DEFAULT_REPEAT_PER_ROW);
+        long LOAD_DATA_DELAY = arguments.getLong(EXTRA_LOAD_DATA_DELAY,
+                DEFAULT_LOAD_DATA_DELAY);
+        boolean TEST_ENTRANCE_TRANSITION = arguments.getBoolean(
+                EXTRA_TEST_ENTRANCE_TRANSITION,
+                DEFAULT_TEST_ENTRANCE_TRANSITION);
+        final boolean SET_ADAPTER_AFTER_DATA_LOAD = arguments.getBoolean(
+                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
+                DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
+
         if (!SET_ADAPTER_AFTER_DATA_LOAD) {
             setupRows();
         }
@@ -75,7 +92,9 @@
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
-                Log.i(TAG, "onItemSelected: " + item + " row " + row);
+                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
+                        + " " + rowViewHolder
+                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
             }
         });
         if (TEST_ENTRANCE_TRANSITION) {
@@ -88,6 +107,9 @@
         new Handler().postDelayed(new Runnable() {
             @Override
             public void run() {
+                if (getActivity() == null || getActivity().isDestroyed()) {
+                    return;
+                }
                 if (SET_ADAPTER_AFTER_DATA_LOAD) {
                     setupRows();
                 }
@@ -108,15 +130,16 @@
     private void loadData() {
         for (int i = 0; i < NUM_ROWS; ++i) {
             ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
+            int index = 0;
             for (int j = 0; j < REPEAT_PER_ROW; ++j) {
-                listRowAdapter.add("Hello world");
-                listRowAdapter.add("This is a test");
-                listRowAdapter.add("Android TV");
-                listRowAdapter.add("Leanback");
-                listRowAdapter.add("Hello world");
-                listRowAdapter.add("Android TV");
-                listRowAdapter.add("Leanback");
-                listRowAdapter.add("GuidedStepFragment");
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepSupportFragment-" + (index++));
             }
             HeaderItem header = new HeaderItem(i, "Row " + i);
             mRowsAdapter.add(new ListRow(header, listRowAdapter));
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
new file mode 100644
index 0000000..51b532a
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.graphics.CompositeDrawable;
+import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.view.View;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link DetailsTestFragment}.
+ */
+@RunWith(JUnit4.class)
+@MediumTest
+public class DetailsFragmentTest {
+
+    @Rule
+    public ActivityTestRule<DetailsFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(DetailsFragmentTestActivity.class, false, false);
+    private DetailsFragmentTestActivity mActivity;
+
+    @Test
+    public void parallaxTest() throws Throwable {
+        final int mDefaultVerticalOffset = -300;
+        Intent intent = new Intent();
+        intent.putExtra(DetailsTestFragment.VERTICAL_OFFSET, mDefaultVerticalOffset);
+        mActivity = activityTestRule.launchActivity(intent);
+
+        final DetailsTestFragment detailsFragment = mActivity.getDetailsFragment();
+        DetailsBackgroundParallaxHelper parallaxHelper = detailsFragment.getParallaxHelper();
+        final CompositeDrawable drawable = (CompositeDrawable) parallaxHelper.getDrawable();
+        final FitWidthBitmapDrawable bitmapDrawable = (FitWidthBitmapDrawable)
+                (drawable.getChildAt(0).getDrawable());
+
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mActivity.getDetailsFragment().getRowsFragment().getAdapter().size() > 1;
+            }
+        });
+
+        final VerticalGridView verticalGridView = mActivity.getDetailsFragment()
+                .getRowsFragment().getVerticalGridView();
+        final int windowHeight = verticalGridView.getHeight();
+        final int windowWidth = verticalGridView.getWidth();
+        // make sure background manager attached to window is same size as VerticalGridView
+        // i.e. no status bar.
+        assertEquals(windowHeight, mActivity.getWindow().getDecorView().getHeight());
+        assertEquals(windowWidth, mActivity.getWindow().getDecorView().getWidth());
+
+        final View detailsFrame = verticalGridView.findViewById(R.id.details_frame);
+
+        assertEquals(windowWidth, bitmapDrawable.getBounds().width());
+
+        final Rect detailsFrameRect = new Rect();
+        detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
+        verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
+
+        assertEquals(Math.min(windowHeight, detailsFrameRect.bottom),
+                bitmapDrawable.getBounds().height());
+        assertEquals(0, bitmapDrawable.getVerticalOffset());
+
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                verticalGridView.scrollToPosition(1);
+            }
+        });
+
+        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return bitmapDrawable.getVerticalOffset() == mDefaultVerticalOffset;
+            }
+        });
+
+        detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
+        verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
+        assertEquals(detailsFrameRect.bottom, bitmapDrawable.getBounds().height());
+        assertEquals(windowWidth, bitmapDrawable.getBounds().width());
+
+        ColorDrawable colorDrawable = (ColorDrawable) (drawable.getChildAt(1).getDrawable());
+        assertEquals(windowWidth, colorDrawable.getBounds().width());
+        // Since bottom is using float mapping, using float compare with delta
+        assertEquals(windowHeight - detailsFrameRect.bottom,
+                (float) colorDrawable.getBounds().height(), 2 /*delta*/);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTestActivity.java
new file mode 100644
index 0000000..9225ade
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTestActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+import android.view.View;
+
+/**
+ * Activity containing {@link DetailsFragmentTest} used for testing.
+ */
+public class DetailsFragmentTestActivity extends Activity {
+    private DetailsTestFragment mFragment;
+
+    /**
+     * Called when the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
+        getWindow().getDecorView().setSystemUiVisibility(uiOptions);
+        setContentView(R.layout.details);
+        mFragment = new DetailsTestFragment();
+
+        if (savedInstanceState == null) {
+            Intent intent = getIntent();
+            if (intent.getExtras() != null) {
+                Bundle arguments = new Bundle();
+                arguments.putAll(intent.getExtras());
+                mFragment.setArguments(arguments);
+            }
+            FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.replace(R.id.fragment_root, mFragment);
+            ft.commit();
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
+                R.drawable.spiderman));
+    }
+
+    public DetailsTestFragment getDetailsFragment() {
+        return mFragment;
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsParallaxManagerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsParallaxManagerTest.java
new file mode 100644
index 0000000..2940f49
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsParallaxManagerTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.v17.leanback.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v17.leanback.widget.ParallaxRecyclerViewSource;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link DetailsParallaxManager}.
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public class DetailsParallaxManagerTest {
+
+    @Rule
+    public ActivityTestRule<DetailsFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(DetailsFragmentTestActivity.class);
+    private DetailsFragmentTestActivity mActivity;
+
+    @Before
+    public void setUp() {
+        mActivity = activityTestRule.getActivity();
+    }
+
+    @Test
+    public void setupTest() {
+        double delta = 0.0002;
+        DetailsParallaxManager dpm = new DetailsParallaxManager(
+                mActivity.getDetailsFragment().getRowsFragment().getVerticalGridView());
+
+        assertNotNull(dpm.getParallax());
+
+        ParallaxRecyclerViewSource.ChildPositionProperty frameTop = dpm.getFrameTop();
+        assertEquals(0f, frameTop.getFraction(), delta);
+        assertEquals(0f, frameTop.getAdapterPosition(), delta);
+
+
+        ParallaxRecyclerViewSource.ChildPositionProperty frameBottom = dpm.getFrameBottom();
+        assertEquals(1f, frameBottom.getFraction(), delta);
+        assertEquals(0f, frameBottom.getAdapterPosition(), delta);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java
new file mode 100644
index 0000000..72d1294
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsTestFragment.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v17.leanback.test.R;
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.DetailsOverviewRow;
+import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
+import android.support.v17.leanback.widget.FullWidthDetailsOverviewSharedElementHelper;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ImageCardView;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.view.ViewGroup;
+
+public class DetailsTestFragment extends android.support.v17.leanback.app.DetailsFragment {
+    private static final String ITEM = "item";
+    public static final String VERTICAL_OFFSET = "details_fragment";
+
+    private static final int NUM_ROWS = 3;
+    private ArrayObjectAdapter mRowsAdapter;
+    private PhotoItem mPhotoItem;
+    private final Presenter mCardPresenter = new Presenter() {
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent) {
+            ImageCardView cardView = new ImageCardView(getActivity());
+            cardView.setFocusable(true);
+            cardView.setFocusableInTouchMode(true);
+            return new ViewHolder(cardView);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+            ImageCardView imageCardView = (ImageCardView) viewHolder.view;
+            imageCardView.setTitleText("Android Tv");
+            imageCardView.setContentText("Android Tv Production Inc.");
+            imageCardView.setMainImageDimensions(313, 176);
+        }
+
+        @Override
+        public void onUnbindViewHolder(ViewHolder viewHolder) {
+        }
+    };
+
+    private static final int ACTION_PLAY = 1;
+    private static final int ACTION_RENT = 2;
+    private static final int ACTION_BUY = 3;
+
+    private static final long TIME_TO_LOAD_OVERVIEW_ROW_MS = 1000;
+    private static final long TIME_TO_LOAD_RELATED_ROWS_MS = 2000;
+
+    private Action mActionPlay;
+    private Action mActionRent;
+    private Action mActionBuy;
+
+    private FullWidthDetailsOverviewSharedElementHelper mHelper;
+    private DetailsBackgroundParallaxHelper mParallaxHelper;
+    private int mMinVerticalOffset;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setTitle("Leanback Sample App");
+
+        if (getArguments() != null) {
+            mMinVerticalOffset = getArguments().getInt(VERTICAL_OFFSET, -100);
+        }
+        mActionPlay = new Action(ACTION_PLAY, "Play");
+        mActionRent = new Action(ACTION_RENT, "Rent", "$3.99",
+                getResources().getDrawable(R.drawable.ic_action_a));
+        mActionBuy = new Action(ACTION_BUY, "Buy $9.99");
+
+        ClassPresenterSelector ps = new ClassPresenterSelector();
+        FullWidthDetailsOverviewRowPresenter dorPresenter =
+                new FullWidthDetailsOverviewRowPresenter(new AbstractDetailsDescriptionPresenter() {
+                    @Override
+                    protected void onBindDescription(
+                            AbstractDetailsDescriptionPresenter.ViewHolder vh, Object item) {
+                        vh.getTitle().setText("Funny Movie");
+                        vh.getSubtitle().setText("Android TV Production Inc.");
+                        vh.getBody().setText("What a great movie!");
+                    }
+                });
+
+        ps.addClassPresenter(DetailsOverviewRow.class, dorPresenter);
+        ps.addClassPresenter(ListRow.class, new ListRowPresenter());
+        mRowsAdapter = new ArrayObjectAdapter(ps);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(ITEM, mPhotoItem);
+    }
+
+    public void setItem(PhotoItem photoItem) {
+        mPhotoItem = photoItem;
+        mRowsAdapter.clear();
+        new Handler().postDelayed(new Runnable() {
+            public void run() {
+                if (getActivity() == null) {
+                    return;
+                }
+                Resources res = getActivity().getResources();
+                DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle());
+                dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId()));
+                SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
+                adapter.set(ACTION_RENT, mActionRent);
+                adapter.set(ACTION_BUY, mActionBuy);
+                dor.setActionsAdapter(adapter);
+                mRowsAdapter.add(0, dor);
+                setSelectedPosition(0, true);
+            }
+        }, TIME_TO_LOAD_OVERVIEW_ROW_MS);
+
+
+        new Handler().postDelayed(new Runnable() {
+            public void run() {
+                if (getActivity() == null) {
+                    return;
+                }
+                for (int i = 0; i < NUM_ROWS; ++i) {
+                    ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(mCardPresenter);
+                    listRowAdapter.add(new PhotoItem("Hello world", R.drawable.spiderman));
+                    listRowAdapter.add(new PhotoItem("This is a test", R.drawable.spiderman));
+                    listRowAdapter.add(new PhotoItem("Android TV", R.drawable.spiderman));
+                    listRowAdapter.add(new PhotoItem("Leanback", R.drawable.spiderman));
+                    HeaderItem header = new HeaderItem(i, "Row " + i);
+                    mRowsAdapter.add(new ListRow(header, listRowAdapter));
+                }
+            }
+        }, TIME_TO_LOAD_RELATED_ROWS_MS);
+
+        setAdapter(mRowsAdapter);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mParallaxHelper = new DetailsBackgroundParallaxHelper.ParallaxBuilder(
+                getActivity(),
+                getParallaxManager())
+                .setCoverImageMinVerticalOffset(mMinVerticalOffset)
+                .build();
+        BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
+        backgroundManager.attach(getActivity().getWindow());
+        backgroundManager.setDrawable(mParallaxHelper.getDrawable());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
+                R.drawable.spiderman);
+        mParallaxHelper.setCoverImageBitmap(bitmap);
+    }
+
+    DetailsBackgroundParallaxHelper getParallaxHelper() {
+        return mParallaxHelper;
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
new file mode 100644
index 0000000..4f43de0
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide from javadoc
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class GuidedStepFragmentTest extends GuidedStepFragmentTestBase {
+
+    private static final int ON_DESTROY_TIMEOUT = 5000;
+
+    @Test
+    public void nextAndBack() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1000) {
+                    GuidedStepFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestFragment(secondFragmentName));
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestFragment.Provider second = mockProvider(secondFragmentName);
+
+        GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+        verify(first, times(1)).onCreate(any(Bundle.class));
+        verify(first, times(1)).onCreateGuidance(any(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(first, times(1)).onStart();
+        verify(first, times(1)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        verify(first, times(1)).onGuidedActionClicked(any(GuidedAction.class));
+
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(first, times(1)).onPause();
+        verify(first, times(1)).onStop();
+        verify(first, times(1)).onDestroyView();
+        verify(second, times(1)).onCreate(any(Bundle.class));
+        verify(second, times(1)).onCreateGuidance(any(Bundle.class));
+        verify(second, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(second, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(second, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(second, times(1)).onStart();
+        verify(second, times(1)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(second, times(1)).onPause();
+        verify(second, times(1)).onStop();
+        verify(second, times(1)).onDestroyView();
+        verify(second, times(1)).onDestroy();
+        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(2)).onViewStateRestored(any(Bundle.class));
+        verify(first, times(2)).onStart();
+        verify(first, times(2)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+        assertTrue(activity.isDestroyed());
+    }
+
+    @Test
+    public void restoreFragments() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
+                actions.add(new GuidedAction.Builder().id(1001).editable(true).title("text")
+                        .build());
+                actions.add(new GuidedAction.Builder().id(1002).editable(true).title("text")
+                        .autoSaveRestoreEnabled(false).build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1000) {
+                    GuidedStepFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestFragment(secondFragmentName));
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestFragment.Provider second = mockProvider(secondFragmentName);
+
+        final GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+        first.getFragment().findActionById(1001).setTitle("modified text");
+        first.getFragment().findActionById(1002).setTitle("modified text");
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.recreate();
+            }
+        });
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(first, times(2)).onCreate(any(Bundle.class));
+        verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onDestroy();
+        verify(second, times(2)).onCreate(any(Bundle.class));
+        verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(second, times(1)).onDestroy();
+        assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+        assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(second, times(2)).onPause();
+        verify(second, times(2)).onStop();
+        verify(second, times(2)).onDestroyView();
+        verify(second, times(2)).onDestroy();
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+    }
+
+
+    @Test
+    public void finishGuidedStepFragment_finishes_activity() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("Finish activity").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1001) {
+                    obj.getFragment().finishGuidedStepFragments();
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        final GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+
+        View viewFinish = first.getFragment().getActionItemView(0);
+        assertTrue(viewFinish.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+    }
+
+    @Test
+    public void finishGuidedStepFragment_finishes_fragments() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("Finish fragments").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1001) {
+                    obj.getFragment().finishGuidedStepFragments();
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        final GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName,
+                false /*asRoot*/);
+
+        View viewFinish = first.getFragment().getActionItemView(0);
+        assertTrue(viewFinish.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+
+        // fragment should be destroyed, activity should not destroyed
+        waitOnDestroy(first, 1);
+        assertFalse(activity.isDestroyed());
+    }
+
+    @Test
+    public void subActions() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        final boolean[] expandSubActionInOnCreateView = new boolean[] {false};
+        GuidedStepTestFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                if (expandSubActionInOnCreateView[0]) {
+                    obj.getFragment().expandAction(obj.getFragment().findActionById(1000), false);
+                }
+                return null;
+            }
+        }).when(first).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                List<GuidedAction> subActions = new ArrayList<GuidedAction>();
+                subActions.add(new GuidedAction.Builder().id(2000).title("item1").build());
+                subActions.add(new GuidedAction.Builder().id(2001).title("item2").build());
+                actions.add(new GuidedAction.Builder().id(1000).subActions(subActions)
+                        .title("list").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Boolean>() {
+            public Boolean answer(InvocationOnMock invocation) {
+                GuidedStepTestFragment.Provider obj = (GuidedStepTestFragment.Provider)
+                        invocation.getMock();
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                if (action.getId() == 2000) {
+                    return true;
+                } else if (action.getId() == 2001) {
+                    GuidedStepFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestFragment(secondFragmentName));
+                    return false;
+                }
+                return false;
+            }
+        }).when(first).onSubGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestFragment.Provider second = mockProvider(secondFragmentName);
+
+        final GuidedStepFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+
+        // after clicked, it sub actions list should expand
+        View viewForList = first.getFragment().getActionItemView(0);
+        assertTrue(viewForList.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertFalse(viewForList.hasFocus());
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        ArgumentCaptor<GuidedAction> actionCapture = ArgumentCaptor.forClass(GuidedAction.class);
+        verify(first, times(1)).onSubGuidedActionClicked(actionCapture.capture());
+        assertEquals(2000, actionCapture.getValue().getId());
+        // after clicked a sub action, it sub actions list should close
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertTrue(viewForList.hasFocus());
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+
+        assertFalse(viewForList.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        ArgumentCaptor<GuidedAction> actionCapture2 = ArgumentCaptor.forClass(GuidedAction.class);
+        verify(first, times(2)).onSubGuidedActionClicked(actionCapture2.capture());
+        assertEquals(2001, actionCapture2.getValue().getId());
+
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+
+        // test expand sub action when return to first fragment
+        expandSubActionInOnCreateView[0] = true;
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        assertTrue(first.getFragment().isExpanded());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertFalse(first.getFragment().isExpanded());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java
new file mode 100644
index 0000000..06beab6
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepFragmentTestActivity extends Activity {
+
+    /**
+     * Frst Test that will be included in this Activity
+     */
+    public static final String EXTRA_TEST_NAME = "testName";
+    /**
+     * True(default) to addAsRoot() for first Test, false to use add()
+     */
+    public static final String EXTRA_ADD_AS_ROOT = "addAsRoot";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        if (savedInstanceState == null) {
+            String firstTestName = intent.getStringExtra(EXTRA_TEST_NAME);
+            if (firstTestName != null) {
+                GuidedStepTestFragment testFragment = new GuidedStepTestFragment(firstTestName);
+                if (intent.getBooleanExtra(EXTRA_ADD_AS_ROOT, true)) {
+                    GuidedStepTestFragment.addAsRoot(this, testFragment, android.R.id.content);
+                } else {
+                    GuidedStepTestFragment.add(getFragmentManager(), testFragment,
+                            android.R.id.content);
+                }
+            }
+        }
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java
new file mode 100644
index 0000000..6ed254a
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTestBase.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepFragmentTestBase {
+
+    private static final long TIMEOUT = 5000;
+
+    @Rule public TestName mUnitTestName = new TestName();
+
+    @Rule
+    public ActivityTestRule<GuidedStepFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(GuidedStepFragmentTestActivity.class, false, false);
+
+    @Before
+    public void clearTests() {
+        GuidedStepTestFragment.clearTests();
+    }
+
+    public static class ExpandTransitionFinish extends PollingCheck.PollingCheckCondition {
+        GuidedStepTestFragment.Provider mProvider;
+
+        public ExpandTransitionFinish(GuidedStepTestFragment.Provider provider) {
+            mProvider = provider;
+        }
+
+        @Override
+        public boolean canPreProceed() {
+            return false;
+        }
+
+        @Override
+        public boolean canProceed() {
+            GuidedStepTestFragment fragment = mProvider.getFragment();
+            if (fragment != null && fragment.getView() != null) {
+                if (!fragment.getGuidedActionsStylist().isInExpandTransition()) {
+                    // expand transition finishes
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public static void waitOnDestroy(GuidedStepTestFragment.Provider provider,
+            int times) {
+        verify(provider, timeout((int)TIMEOUT).times(times)).onDestroy();
+    }
+
+    public static class EnterTransitionFinish extends PollingCheck.PollingCheckCondition {
+        PollingCheck.ViewScreenPositionDetector mDector =
+                new PollingCheck.ViewScreenPositionDetector();
+
+        GuidedStepTestFragment.Provider mProvider;
+
+        public EnterTransitionFinish(GuidedStepTestFragment.Provider provider) {
+            mProvider = provider;
+        }
+        @Override
+        public boolean canProceed() {
+            GuidedStepTestFragment fragment = mProvider.getFragment();
+            if (fragment != null && fragment.getView() != null) {
+                View view = fragment.getView().findViewById(R.id.guidance_title);
+                if (view != null) {
+                    if (mDector.isViewStableOnScreen(view)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    public static void sendKey(int keyCode) {
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+    }
+
+    public String generateMethodTestName(String testName) {
+        return mUnitTestName.getMethodName() + "_" + testName;
+    }
+
+    public GuidedStepFragmentTestActivity launchTestActivity(String firstTestName) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        return activityTestRule.launchActivity(intent);
+    }
+
+    public GuidedStepFragmentTestActivity launchTestActivity(String firstTestName,
+            boolean addAsRoot) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        intent.putExtra(GuidedStepFragmentTestActivity.EXTRA_ADD_AS_ROOT, addAsRoot);
+        return activityTestRule.launchActivity(intent);
+    }
+
+    public GuidedStepTestFragment.Provider mockProvider(String testName) {
+        GuidedStepTestFragment.Provider test = mock(GuidedStepTestFragment.Provider.class);
+        when(test.getActivity()).thenCallRealMethod();
+        when(test.getFragmentManager()).thenCallRealMethod();
+        when(test.getFragment()).thenCallRealMethod();
+        GuidedStepTestFragment.setupTest(testName, test);
+        return test;
+    }
+}
+
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
new file mode 100644
index 0000000..66ade14
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
@@ -0,0 +1,360 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from GuidedStepFragmentTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.support.v17.leanback.widget.GuidedAction;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide from javadoc
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class GuidedStepSupportFragmentTest extends GuidedStepSupportFragmentTestBase {
+
+    private static final int ON_DESTROY_TIMEOUT = 5000;
+
+    @Test
+    public void nextAndBack() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1000) {
+                    GuidedStepSupportFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestSupportFragment(secondFragmentName));
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
+
+        GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+        verify(first, times(1)).onCreate(any(Bundle.class));
+        verify(first, times(1)).onCreateGuidance(any(Bundle.class));
+        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(first, times(1)).onStart();
+        verify(first, times(1)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        verify(first, times(1)).onGuidedActionClicked(any(GuidedAction.class));
+
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(first, times(1)).onPause();
+        verify(first, times(1)).onStop();
+        verify(first, times(1)).onDestroyView();
+        verify(second, times(1)).onCreate(any(Bundle.class));
+        verify(second, times(1)).onCreateGuidance(any(Bundle.class));
+        verify(second, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(second, times(1)).onCreateButtonActions(any(List.class), any(Bundle.class));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(second, times(1)).onViewStateRestored(any(Bundle.class));
+        verify(second, times(1)).onStart();
+        verify(second, times(1)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(second, times(1)).onPause();
+        verify(second, times(1)).onStop();
+        verify(second, times(1)).onDestroyView();
+        verify(second, times(1)).onDestroy();
+        verify(first, times(1)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(2)).onViewStateRestored(any(Bundle.class));
+        verify(first, times(2)).onStart();
+        verify(first, times(2)).onResume();
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+        assertTrue(activity.isDestroyed());
+    }
+
+    @Test
+    public void restoreFragments() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1000).title("OK").build());
+                actions.add(new GuidedAction.Builder().id(1001).editable(true).title("text")
+                        .build());
+                actions.add(new GuidedAction.Builder().id(1002).editable(true).title("text")
+                        .autoSaveRestoreEnabled(false).build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1000) {
+                    GuidedStepSupportFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestSupportFragment(secondFragmentName));
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
+
+        final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+        first.getFragment().findActionById(1001).setTitle("modified text");
+        first.getFragment().findActionById(1002).setTitle("modified text");
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+
+        activityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.recreate();
+            }
+        });
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(first, times(2)).onCreate(any(Bundle.class));
+        verify(first, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
+        verify(first, times(1)).onDestroy();
+        verify(second, times(2)).onCreate(any(Bundle.class));
+        verify(second, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        verify(second, times(1)).onDestroy();
+        assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+        assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(second, times(2)).onPause();
+        verify(second, times(2)).onStop();
+        verify(second, times(2)).onDestroyView();
+        verify(second, times(2)).onDestroy();
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+    }
+
+
+    @Test
+    public void finishGuidedStepSupportFragment_finishes_activity() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("Finish activity").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1001) {
+                    obj.getFragment().finishGuidedStepSupportFragments();
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+
+        View viewFinish = first.getFragment().getActionItemView(0);
+        assertTrue(viewFinish.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+    }
+
+    @Test
+    public void finishGuidedStepSupportFragment_finishes_fragments() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                actions.add(new GuidedAction.Builder().id(1001).title("Finish fragments").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                if (action.getId() == 1001) {
+                    obj.getFragment().finishGuidedStepSupportFragments();
+                }
+                return null;
+            }
+        }).when(first).onGuidedActionClicked(any(GuidedAction.class));
+
+        final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName,
+                false /*asRoot*/);
+
+        View viewFinish = first.getFragment().getActionItemView(0);
+        assertTrue(viewFinish.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+
+        // fragment should be destroyed, activity should not destroyed
+        waitOnDestroy(first, 1);
+        assertFalse(activity.isDestroyed());
+    }
+
+    @Test
+    public void subActions() throws Throwable {
+        final String firstFragmentName = generateMethodTestName("first");
+        final String secondFragmentName = generateMethodTestName("second");
+        final boolean[] expandSubActionInOnCreateView = new boolean[] {false};
+        GuidedStepTestSupportFragment.Provider first = mockProvider(firstFragmentName);
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                if (expandSubActionInOnCreateView[0]) {
+                    obj.getFragment().expandAction(obj.getFragment().findActionById(1000), false);
+                }
+                return null;
+            }
+        }).when(first).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) {
+                List actions = (List) invocation.getArguments()[0];
+                List<GuidedAction> subActions = new ArrayList<GuidedAction>();
+                subActions.add(new GuidedAction.Builder().id(2000).title("item1").build());
+                subActions.add(new GuidedAction.Builder().id(2001).title("item2").build());
+                actions.add(new GuidedAction.Builder().id(1000).subActions(subActions)
+                        .title("list").build());
+                return null;
+            }
+        }).when(first).onCreateActions(any(List.class), any(Bundle.class));
+        doAnswer(new Answer<Boolean>() {
+            public Boolean answer(InvocationOnMock invocation) {
+                GuidedStepTestSupportFragment.Provider obj = (GuidedStepTestSupportFragment.Provider)
+                        invocation.getMock();
+                GuidedAction action = (GuidedAction) invocation.getArguments()[0];
+                if (action.getId() == 2000) {
+                    return true;
+                } else if (action.getId() == 2001) {
+                    GuidedStepSupportFragment.add(obj.getFragmentManager(),
+                            new GuidedStepTestSupportFragment(secondFragmentName));
+                    return false;
+                }
+                return false;
+            }
+        }).when(first).onSubGuidedActionClicked(any(GuidedAction.class));
+
+        GuidedStepTestSupportFragment.Provider second = mockProvider(secondFragmentName);
+
+        final GuidedStepSupportFragmentTestActivity activity = launchTestActivity(firstFragmentName);
+
+        // after clicked, it sub actions list should expand
+        View viewForList = first.getFragment().getActionItemView(0);
+        assertTrue(viewForList.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertFalse(viewForList.hasFocus());
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        ArgumentCaptor<GuidedAction> actionCapture = ArgumentCaptor.forClass(GuidedAction.class);
+        verify(first, times(1)).onSubGuidedActionClicked(actionCapture.capture());
+        assertEquals(2000, actionCapture.getValue().getId());
+        // after clicked a sub action, it sub actions list should close
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertTrue(viewForList.hasFocus());
+
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+
+        assertFalse(viewForList.hasFocus());
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+        ArgumentCaptor<GuidedAction> actionCapture2 = ArgumentCaptor.forClass(GuidedAction.class);
+        verify(first, times(2)).onSubGuidedActionClicked(actionCapture2.capture());
+        assertEquals(2001, actionCapture2.getValue().getId());
+
+        PollingCheck.waitFor(new EnterTransitionFinish(second));
+        verify(second, times(1)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+
+        // test expand sub action when return to first fragment
+        expandSubActionInOnCreateView[0] = true;
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new EnterTransitionFinish(first));
+        verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
+                any(Bundle.class), any(View.class));
+        assertTrue(first.getFragment().isExpanded());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new ExpandTransitionFinish(first));
+        assertFalse(first.getFragment().isExpanded());
+
+        sendKey(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
+        verify(first, timeout(ON_DESTROY_TIMEOUT).times(1)).onDestroy();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java
new file mode 100644
index 0000000..2fc8d1e
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestActivity.java
@@ -0,0 +1,57 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from GuidedStepFragmentTestActivity.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.support.v4.app.FragmentActivity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepSupportFragmentTestActivity extends FragmentActivity {
+
+    /**
+     * Frst Test that will be included in this Activity
+     */
+    public static final String EXTRA_TEST_NAME = "testName";
+    /**
+     * True(default) to addAsRoot() for first Test, false to use add()
+     */
+    public static final String EXTRA_ADD_AS_ROOT = "addAsRoot";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        if (savedInstanceState == null) {
+            String firstTestName = intent.getStringExtra(EXTRA_TEST_NAME);
+            if (firstTestName != null) {
+                GuidedStepTestSupportFragment testFragment = new GuidedStepTestSupportFragment(firstTestName);
+                if (intent.getBooleanExtra(EXTRA_ADD_AS_ROOT, true)) {
+                    GuidedStepTestSupportFragment.addAsRoot(this, testFragment, android.R.id.content);
+                } else {
+                    GuidedStepTestSupportFragment.add(getSupportFragmentManager(), testFragment,
+                            android.R.id.content);
+                }
+            }
+        }
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java
new file mode 100644
index 0000000..4fe4a24
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTestBase.java
@@ -0,0 +1,140 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from GuidedStepFrgamentTestBase.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepSupportFragmentTestBase {
+
+    private static final long TIMEOUT = 5000;
+
+    @Rule public TestName mUnitTestName = new TestName();
+
+    @Rule
+    public ActivityTestRule<GuidedStepSupportFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(GuidedStepSupportFragmentTestActivity.class, false, false);
+
+    @Before
+    public void clearTests() {
+        GuidedStepTestSupportFragment.clearTests();
+    }
+
+    public static class ExpandTransitionFinish extends PollingCheck.PollingCheckCondition {
+        GuidedStepTestSupportFragment.Provider mProvider;
+
+        public ExpandTransitionFinish(GuidedStepTestSupportFragment.Provider provider) {
+            mProvider = provider;
+        }
+
+        @Override
+        public boolean canPreProceed() {
+            return false;
+        }
+
+        @Override
+        public boolean canProceed() {
+            GuidedStepTestSupportFragment fragment = mProvider.getFragment();
+            if (fragment != null && fragment.getView() != null) {
+                if (!fragment.getGuidedActionsStylist().isInExpandTransition()) {
+                    // expand transition finishes
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    public static void waitOnDestroy(GuidedStepTestSupportFragment.Provider provider,
+            int times) {
+        verify(provider, timeout((int)TIMEOUT).times(times)).onDestroy();
+    }
+
+    public static class EnterTransitionFinish extends PollingCheck.PollingCheckCondition {
+        PollingCheck.ViewScreenPositionDetector mDector =
+                new PollingCheck.ViewScreenPositionDetector();
+
+        GuidedStepTestSupportFragment.Provider mProvider;
+
+        public EnterTransitionFinish(GuidedStepTestSupportFragment.Provider provider) {
+            mProvider = provider;
+        }
+        @Override
+        public boolean canProceed() {
+            GuidedStepTestSupportFragment fragment = mProvider.getFragment();
+            if (fragment != null && fragment.getView() != null) {
+                View view = fragment.getView().findViewById(R.id.guidance_title);
+                if (view != null) {
+                    if (mDector.isViewStableOnScreen(view)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    public static void sendKey(int keyCode) {
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+    }
+
+    public String generateMethodTestName(String testName) {
+        return mUnitTestName.getMethodName() + "_" + testName;
+    }
+
+    public GuidedStepSupportFragmentTestActivity launchTestActivity(String firstTestName) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        return activityTestRule.launchActivity(intent);
+    }
+
+    public GuidedStepSupportFragmentTestActivity launchTestActivity(String firstTestName,
+            boolean addAsRoot) {
+        Intent intent = new Intent();
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_TEST_NAME, firstTestName);
+        intent.putExtra(GuidedStepSupportFragmentTestActivity.EXTRA_ADD_AS_ROOT, addAsRoot);
+        return activityTestRule.launchActivity(intent);
+    }
+
+    public GuidedStepTestSupportFragment.Provider mockProvider(String testName) {
+        GuidedStepTestSupportFragment.Provider test = mock(GuidedStepTestSupportFragment.Provider.class);
+        when(test.getActivity()).thenCallRealMethod();
+        when(test.getFragmentManager()).thenCallRealMethod();
+        when(test.getFragment()).thenCallRealMethod();
+        GuidedStepTestSupportFragment.setupTest(testName, test);
+        return test;
+    }
+}
+
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestFragment.java
new file mode 100644
index 0000000..c530925
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestFragment.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.View;
+import android.view.LayoutInflater;
+
+
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepTestFragment extends GuidedStepFragment {
+
+    private static final String KEY_TEST_NAME = "key_test_name";
+
+    private static final HashMap<String, Provider> sTestMap = new HashMap<String, Provider>();
+
+    public static class Provider {
+
+        GuidedStepTestFragment mFragment;
+
+        public void onCreate(Bundle savedInstanceState) {
+        }
+
+        public void onSaveInstanceState(Bundle outState) {
+        }
+
+        public Guidance onCreateGuidance(Bundle savedInstanceState) {
+            return new Guidance("", "", "", null);
+        }
+
+        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        }
+
+        public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        }
+
+        public void onGuidedActionClicked(GuidedAction action) {
+        }
+
+        public boolean onSubGuidedActionClicked(GuidedAction action) {
+            return true;
+        }
+
+        public void onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState, View result) {
+        }
+
+        public void onDestroyView() {
+        }
+
+        public void onDestroy() {
+        }
+
+        public void onStart() {
+        }
+
+        public void onStop() {
+        }
+
+        public void onResume() {
+        }
+
+        public void onPause() {
+        }
+
+        public void onViewStateRestored(Bundle bundle) {
+        }
+
+        public void onDetach() {
+        }
+
+        public GuidedStepTestFragment getFragment() {
+            return mFragment;
+        }
+
+        public Activity getActivity() {
+            return mFragment.getActivity();
+        }
+
+        public FragmentManager getFragmentManager() {
+            return mFragment.getFragmentManager();
+        }
+    }
+
+    public static void setupTest(String testName, Provider provider) {
+        sTestMap.put(testName, provider);
+    }
+
+    public static void clearTests() {
+        sTestMap.clear();
+    }
+
+    CharSequence mTestName;
+    Provider mProvider;
+
+    public GuidedStepTestFragment() {
+    }
+
+    public GuidedStepTestFragment(String testName) {
+        setTestName(testName);
+    }
+
+    public void setTestName(CharSequence testName) {
+        mTestName = testName;
+    }
+
+    public CharSequence getTestName() {
+        return mTestName;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        if (savedInstanceState != null) {
+            mTestName = savedInstanceState.getCharSequence(KEY_TEST_NAME, null);
+        }
+        mProvider = sTestMap.get(mTestName);
+        if (mProvider == null) {
+            throw new IllegalArgumentException("you must setupTest()");
+        }
+        mProvider.mFragment = this;
+        super.onCreate(savedInstanceState);
+        mProvider.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putCharSequence(KEY_TEST_NAME, mTestName);
+        mProvider.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public Guidance onCreateGuidance(Bundle savedInstanceState) {
+        Guidance g = mProvider.onCreateGuidance(savedInstanceState);
+        if (g == null) {
+            g = new Guidance("", "", "", null);
+        }
+        return g;
+    }
+
+    @Override
+    public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        mProvider.onCreateActions(actions, savedInstanceState);
+    }
+
+    @Override
+    public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        mProvider.onCreateButtonActions(actions, savedInstanceState);
+    }
+
+    @Override
+    public void onGuidedActionClicked(GuidedAction action) {
+        mProvider.onGuidedActionClicked(action);
+    }
+
+    @Override
+    public boolean onSubGuidedActionClicked(GuidedAction action) {
+        return mProvider.onSubGuidedActionClicked(action);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+        View view = super.onCreateView(inflater, container, state);
+        mProvider.onCreateView(inflater, container, state, view);
+        return view;
+    }
+
+    @Override
+    public void onDestroyView() {
+        mProvider.onDestroyView();
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onDestroy() {
+        mProvider.onDestroy();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onPause() {
+        mProvider.onPause();
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mProvider.onResume();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mProvider.onStart();
+    }
+
+    @Override
+    public void onStop() {
+        mProvider.onStop();
+        super.onStop();
+    }
+
+    @Override
+    public void onDetach() {
+        mProvider.onDetach();
+        super.onDetach();
+    }
+
+    @Override
+    public void onViewStateRestored(Bundle bundle) {
+        super.onViewStateRestored(bundle);
+        mProvider.onViewStateRestored(bundle);
+    }
+}
+
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestSupportFragment.java
new file mode 100644
index 0000000..bafc2db
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepTestSupportFragment.java
@@ -0,0 +1,242 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from GuidedStepTestFragment.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.View;
+import android.view.LayoutInflater;
+
+
+import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * @hide from javadoc
+ */
+public class GuidedStepTestSupportFragment extends GuidedStepSupportFragment {
+
+    private static final String KEY_TEST_NAME = "key_test_name";
+
+    private static final HashMap<String, Provider> sTestMap = new HashMap<String, Provider>();
+
+    public static class Provider {
+
+        GuidedStepTestSupportFragment mFragment;
+
+        public void onCreate(Bundle savedInstanceState) {
+        }
+
+        public void onSaveInstanceState(Bundle outState) {
+        }
+
+        public Guidance onCreateGuidance(Bundle savedInstanceState) {
+            return new Guidance("", "", "", null);
+        }
+
+        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        }
+
+        public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        }
+
+        public void onGuidedActionClicked(GuidedAction action) {
+        }
+
+        public boolean onSubGuidedActionClicked(GuidedAction action) {
+            return true;
+        }
+
+        public void onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState, View result) {
+        }
+
+        public void onDestroyView() {
+        }
+
+        public void onDestroy() {
+        }
+
+        public void onStart() {
+        }
+
+        public void onStop() {
+        }
+
+        public void onResume() {
+        }
+
+        public void onPause() {
+        }
+
+        public void onViewStateRestored(Bundle bundle) {
+        }
+
+        public void onDetach() {
+        }
+
+        public GuidedStepTestSupportFragment getFragment() {
+            return mFragment;
+        }
+
+        public FragmentActivity getActivity() {
+            return mFragment.getActivity();
+        }
+
+        public FragmentManager getFragmentManager() {
+            return mFragment.getFragmentManager();
+        }
+    }
+
+    public static void setupTest(String testName, Provider provider) {
+        sTestMap.put(testName, provider);
+    }
+
+    public static void clearTests() {
+        sTestMap.clear();
+    }
+
+    CharSequence mTestName;
+    Provider mProvider;
+
+    public GuidedStepTestSupportFragment() {
+    }
+
+    public GuidedStepTestSupportFragment(String testName) {
+        setTestName(testName);
+    }
+
+    public void setTestName(CharSequence testName) {
+        mTestName = testName;
+    }
+
+    public CharSequence getTestName() {
+        return mTestName;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        if (savedInstanceState != null) {
+            mTestName = savedInstanceState.getCharSequence(KEY_TEST_NAME, null);
+        }
+        mProvider = sTestMap.get(mTestName);
+        if (mProvider == null) {
+            throw new IllegalArgumentException("you must setupTest()");
+        }
+        mProvider.mFragment = this;
+        super.onCreate(savedInstanceState);
+        mProvider.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putCharSequence(KEY_TEST_NAME, mTestName);
+        mProvider.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public Guidance onCreateGuidance(Bundle savedInstanceState) {
+        Guidance g = mProvider.onCreateGuidance(savedInstanceState);
+        if (g == null) {
+            g = new Guidance("", "", "", null);
+        }
+        return g;
+    }
+
+    @Override
+    public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        mProvider.onCreateActions(actions, savedInstanceState);
+    }
+
+    @Override
+    public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+        mProvider.onCreateButtonActions(actions, savedInstanceState);
+    }
+
+    @Override
+    public void onGuidedActionClicked(GuidedAction action) {
+        mProvider.onGuidedActionClicked(action);
+    }
+
+    @Override
+    public boolean onSubGuidedActionClicked(GuidedAction action) {
+        return mProvider.onSubGuidedActionClicked(action);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+        View view = super.onCreateView(inflater, container, state);
+        mProvider.onCreateView(inflater, container, state, view);
+        return view;
+    }
+
+    @Override
+    public void onDestroyView() {
+        mProvider.onDestroyView();
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onDestroy() {
+        mProvider.onDestroy();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onPause() {
+        mProvider.onPause();
+        super.onPause();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mProvider.onResume();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mProvider.onStart();
+    }
+
+    @Override
+    public void onStop() {
+        mProvider.onStop();
+        super.onStop();
+    }
+
+    @Override
+    public void onDetach() {
+        mProvider.onDetach();
+        super.onDetach();
+    }
+
+    @Override
+    public void onViewStateRestored(Bundle bundle) {
+        super.onViewStateRestored(bundle);
+        mProvider.onViewStateRestored(bundle);
+    }
+}
+
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
index 88fe85e..b7cb4e8 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
@@ -1,6 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.support.v17.leanback.app;
 
-import android.support.test.runner.AndroidJUnitRunner;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.DividerRow;
 import android.support.v17.leanback.widget.HeaderItem;
@@ -8,12 +29,10 @@
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SectionRow;
-import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -21,19 +40,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.support.test.runner.AndroidJUnit4;
-
 /**
  * Unit test for {@link ListRowDataAdapter} class.
  */
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PhotoItem.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PhotoItem.java
new file mode 100644
index 0000000..3c2460a
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PhotoItem.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PhotoItem implements Parcelable {
+    private String mTitle;
+    private String mContent;
+    private int mImageResourceId;
+
+    public PhotoItem(String title, int imageResourceId) {
+        this(title, null, imageResourceId);
+    }
+
+    public PhotoItem(String title, String content, int imageResourceId) {
+        mTitle = title;
+        mContent = content;
+        mImageResourceId = imageResourceId;
+    }
+
+    public int getImageResourceId() {
+        return mImageResourceId;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public String getContent() {
+        return mContent;
+    }
+
+    @Override
+    public String toString() {
+        return mTitle;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mTitle);
+        dest.writeInt(mImageResourceId);
+    }
+
+    public static final Parcelable.Creator<PhotoItem> CREATOR =
+            new Parcelable.Creator<PhotoItem>() {
+        @Override
+        public PhotoItem createFromParcel(Parcel in) {
+            return new PhotoItem(in);
+        }
+
+        @Override
+        public PhotoItem[] newArray(int size) {
+            return new PhotoItem[size];
+        }
+    };
+
+    private PhotoItem(Parcel in) {
+        mTitle = in.readString();
+        mImageResourceId = in.readInt();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlGlueTest.java
index 5dbec44..45b8802 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlGlueTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlGlueTest.java
@@ -13,32 +13,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.support.v17.leanback.app;
 
-import org.junit.Assert;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.view.KeyEvent;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.annotation.UiThread;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.KeyEvent;
-
-@SmallTest
 @RunWith(AndroidJUnit4.class)
+@MediumTest
 public class PlaybackControlGlueTest {
 
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlSupportGlueTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlSupportGlueTest.java
index 69e61dc..d3e3271 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlSupportGlueTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackControlSupportGlueTest.java
@@ -1,3 +1,4 @@
+// CHECKSTYLE:OFF Generated code
 /* This file is auto-generated from PlaybackControlGlueTest.java.  DO NOT MODIFY. */
 
 /*
@@ -15,32 +16,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.support.v17.leanback.app;
 
-import org.junit.Assert;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.view.KeyEvent;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.annotation.UiThread;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.KeyEvent;
-
-@SmallTest
 @RunWith(AndroidJUnit4.class)
+@MediumTest
 public class PlaybackControlSupportGlueTest {
 
 
@@ -350,4 +348,161 @@
             assertEquals(0, rewind.getIndex());
         }
     }
+
+    @Test
+    public void testMediaPauseButtonOnFF() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+        PlaybackControlsRow.MultiAction fastForward = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_FAST_FORWARD);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(fastForward);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPauseButtonOnPlay() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPauseButtonOnPause() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayButtonOnFF() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+        PlaybackControlsRow.MultiAction fastForward = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_FAST_FORWARD);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(fastForward);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayButtonOnPlay() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayButtonOnPause() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayPauseButtonOnFF() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+        PlaybackControlsRow.MultiAction fastForward = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_FAST_FORWARD);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(fastForward);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_FAST_L0, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayPauseButtonOnPlay() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+    }
+
+    @Test
+    public void testMediaPlayPauseButtonOnPause() {
+        PlaybackControlsRow row = new PlaybackControlsRow();
+        glue.setControlsRow(row);
+        SparseArrayObjectAdapter adapter = (SparseArrayObjectAdapter)
+                row.getPrimaryActionsAdapter();
+        PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction) adapter
+                .lookup(PlaybackControlSupportGlue.ACTION_PLAY_PAUSE);
+
+        glue.onActionClicked(playPause);
+        glue.onActionClicked(playPause);
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_PAUSED, glue.getCurrentSpeedId());
+        glue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
+        assertEquals(PlaybackControlSupportGlue.PLAYBACK_SPEED_NORMAL, glue.getCurrentSpeedId());
+    }
+
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
new file mode 100644
index 0000000..f27ace0
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayFragmentTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.test.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class PlaybackOverlayFragmentTest {
+
+    @Rule
+    public ActivityTestRule<PlaybackOverlayTestActivity> activityTestRule =
+            new ActivityTestRule<>(PlaybackOverlayTestActivity.class, false, false);
+    private PlaybackOverlayTestActivity mActivity;
+
+    @Test
+    public void workaroundVideoViewStealFocus() {
+        Intent intent = new Intent();
+        mActivity = activityTestRule.launchActivity(intent);
+
+        assertFalse(mActivity.findViewById(R.id.videoView).hasFocus());
+        assertTrue(mActivity.getPlaybackFragment().getView().hasFocus());
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestActivity.java
new file mode 100644
index 0000000..ea2aa38
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PlaybackOverlayTestActivity extends Activity {
+    private List<PictureInPictureListener> mListeners = new ArrayList<>();
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.playback_controls_with_video);
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        for (PictureInPictureListener listener : mListeners) {
+            listener.onPictureInPictureModeChanged(isInPictureInPictureMode);
+        }
+    }
+
+    public void registerPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void unregisterPictureInPictureListener(PictureInPictureListener listener) {
+        mListeners.remove(listener);
+    }
+
+    public interface PictureInPictureListener {
+        void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
+    }
+
+    public PlaybackOverlayTestFragment getPlaybackFragment() {
+        return (PlaybackOverlayTestFragment) getFragmentManager().findFragmentById(
+                R.id.playback_controls_fragment);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java
new file mode 100644
index 0000000..b44dd09
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/PlaybackOverlayTestFragment.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v17.leanback.test.R;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Toast;
+
+public class PlaybackOverlayTestFragment
+        extends PlaybackOverlayFragment
+        implements PlaybackOverlayTestActivity.PictureInPictureListener {
+    private static final String TAG = "leanback.PlaybackControlsFragment";
+
+    /**
+     * Change this to choose a different overlay background.
+     */
+    private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT;
+
+    /**
+     * Change the number of related content rows.
+     */
+    private static final int RELATED_CONTENT_ROWS = 3;
+
+    /**
+     * Change this to select hidden
+     */
+    private static final boolean SECONDARY_HIDDEN = false;
+
+    private static final int ROW_CONTROLS = 0;
+
+    private PlaybackControlHelper mGlue;
+    private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter;
+    private ListRowPresenter mListRowPresenter;
+
+    private OnItemViewClickedListener mOnItemViewClickedListener = new OnItemViewClickedListener() {
+        @Override
+        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                                  RowPresenter.ViewHolder rowViewHolder, Row row) {
+            Log.i(TAG, "onItemClicked: " + item + " row " + row);
+            if (item instanceof Action) {
+                mGlue.onActionClicked((Action) item);
+            }
+        }
+    };
+
+    private OnItemViewSelectedListener mOnItemViewSelectedListener =
+            new OnItemViewSelectedListener() {
+        @Override
+        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
+            Log.i(TAG, "onItemSelected: " + item + " row " + row);
+        }
+    };
+
+    public SparseArrayObjectAdapter getAdapter() {
+        return (SparseArrayObjectAdapter) super.getAdapter();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        setBackgroundType(BACKGROUND_TYPE);
+        setOnItemViewSelectedListener(mOnItemViewSelectedListener);
+
+        createComponents(getActivity());
+    }
+
+    private void createComponents(Context context) {
+        mGlue = new PlaybackControlHelper(context, this) {
+            @Override
+            public int getUpdatePeriod() {
+                int totalTime = getControlsRow().getTotalTime();
+                if (getView() == null || getView().getWidth() == 0 || totalTime <= 0) {
+                    return 1000;
+                }
+                return Math.max(16, totalTime / getView().getWidth());
+            }
+
+            @Override
+            protected void onRowChanged(PlaybackControlsRow row) {
+                if (getAdapter() == null) {
+                    return;
+                }
+                int index = getAdapter().indexOf(row);
+                if (index >= 0) {
+                    getAdapter().notifyArrayItemRangeChanged(index, 1);
+                }
+            }
+
+            @Override
+            public void onActionClicked(Action action) {
+                if (action.getId() == R.id.lb_control_picture_in_picture) {
+                    getActivity().enterPictureInPictureMode();
+                    return;
+                }
+                super.onActionClicked(action);
+            }
+        };
+
+        mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener);
+
+        mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter();
+        mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN);
+        mListRowPresenter = new ListRowPresenter();
+
+        setAdapter(new SparseArrayObjectAdapter(new PresenterSelector() {
+            @Override
+            public Presenter getPresenter(Object object) {
+                if (object instanceof PlaybackControlsRow) {
+                    return mPlaybackControlsRowPresenter;
+                } else if (object instanceof ListRow) {
+                    return mListRowPresenter;
+                }
+                throw new IllegalArgumentException("Unhandled object: " + object);
+            }
+        }));
+
+        // Add the controls row
+        getAdapter().set(ROW_CONTROLS, mGlue.getControlsRow());
+
+        // Add related content rows
+        for (int i = 0; i < RELATED_CONTENT_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter());
+            listRowAdapter.add("Some related content");
+            listRowAdapter.add("Other related content");
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter));
+        }
+
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mGlue.setFadingEnabled(true);
+        mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
+        ((PlaybackOverlayTestActivity) getActivity()).registerPictureInPictureListener(this);
+    }
+
+    @Override
+    public void onStop() {
+        mGlue.enableProgressUpdating(false);
+        ((PlaybackOverlayTestActivity) getActivity()).unregisterPictureInPictureListener(this);
+        super.onStop();
+    }
+
+    @Override
+    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+        if (isInPictureInPictureMode) {
+            // Hide the controls in picture-in-picture mode.
+            setFadingEnabled(true);
+            fadeOut();
+        } else {
+            setFadingEnabled(mGlue.isMediaPlaying());
+        }
+    }
+
+    abstract static class PlaybackControlHelper extends PlaybackControlGlue {
+        /**
+         * Change the location of the thumbs up/down controls
+         */
+        private static final boolean THUMBS_PRIMARY = true;
+
+        private static final String FAUX_TITLE = "A short song of silence";
+        private static final String FAUX_SUBTITLE = "2014";
+        private static final int FAUX_DURATION = 33 * 1000;
+
+        // These should match the playback service FF behavior
+        private static int[] sFastForwardSpeeds = { 2, 3, 4, 5 };
+
+        private boolean mIsPlaying;
+        private int mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED;
+        private long mStartTime;
+        private long mStartPosition = 0;
+
+        private PlaybackControlsRow.RepeatAction mRepeatAction;
+        private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
+        private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
+        private PlaybackControlsRow.PictureInPictureAction mPipAction;
+        private static Handler mHandler = new Handler();
+
+        private final Runnable mUpdateProgressRunnable = new Runnable() {
+            @Override
+            public void run() {
+                updateProgress();
+                mHandler.postDelayed(this, getUpdatePeriod());
+            }
+        };
+
+        PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment) {
+            super(context, fragment, sFastForwardSpeeds);
+            mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
+            mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
+            mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
+            mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
+            mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
+            mPipAction = new PlaybackControlsRow.PictureInPictureAction(context);
+        }
+
+        @Override
+        public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
+            PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
+
+            ArrayObjectAdapter adapter = new ArrayObjectAdapter(
+                    new ControlButtonPresenterSelector());
+            getControlsRow().setSecondaryActionsAdapter(adapter);
+            if (!THUMBS_PRIMARY) {
+                adapter.add(mThumbsDownAction);
+            }
+            if (android.os.Build.VERSION.SDK_INT > 23) {
+                adapter.add(mPipAction);
+            }
+            adapter.add(mRepeatAction);
+            if (!THUMBS_PRIMARY) {
+                adapter.add(mThumbsUpAction);
+            }
+
+            return presenter;
+        }
+
+        @Override
+        protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
+                PresenterSelector presenterSelector) {
+            SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
+            if (THUMBS_PRIMARY) {
+                adapter.set(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction);
+                adapter.set(PlaybackControlGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction);
+            }
+            return adapter;
+        }
+
+        @Override
+        public void onActionClicked(Action action) {
+            if (shouldDispatchAction(action)) {
+                dispatchAction(action);
+                return;
+            }
+            super.onActionClicked(action);
+        }
+
+        @Override
+        public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
+            if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                Action action = getControlsRow().getActionForKeyCode(keyEvent.getKeyCode());
+                if (shouldDispatchAction(action)) {
+                    dispatchAction(action);
+                    return true;
+                }
+            }
+            return super.onKey(view, keyCode, keyEvent);
+        }
+
+        private boolean shouldDispatchAction(Action action) {
+            return action == mRepeatAction || action == mThumbsUpAction
+                    || action == mThumbsDownAction;
+        }
+
+        private void dispatchAction(Action action) {
+            Toast.makeText(getContext(), action.toString(), Toast.LENGTH_SHORT).show();
+            PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action;
+            multiAction.nextIndex();
+            notifyActionChanged(multiAction);
+        }
+
+        private void notifyActionChanged(PlaybackControlsRow.MultiAction action) {
+            int index;
+            index = getPrimaryActionsAdapter().indexOf(action);
+            if (index >= 0) {
+                getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
+            } else {
+                index = getSecondaryActionsAdapter().indexOf(action);
+                if (index >= 0) {
+                    getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
+                }
+            }
+        }
+
+        private SparseArrayObjectAdapter getPrimaryActionsAdapter() {
+            return (SparseArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
+        }
+
+        private ArrayObjectAdapter getSecondaryActionsAdapter() {
+            return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter();
+        }
+
+        @Override
+        public boolean hasValidMedia() {
+            return true;
+        }
+
+        @Override
+        public boolean isMediaPlaying() {
+            return mIsPlaying;
+        }
+
+        @Override
+        public CharSequence getMediaTitle() {
+            return FAUX_TITLE;
+        }
+
+        @Override
+        public CharSequence getMediaSubtitle() {
+            return FAUX_SUBTITLE;
+        }
+
+        @Override
+        public int getMediaDuration() {
+            return FAUX_DURATION;
+        }
+
+        @Override
+        public Drawable getMediaArt() {
+            return null;
+        }
+
+        @Override
+        public long getSupportedActions() {
+            return PlaybackControlGlue.ACTION_PLAY_PAUSE
+                   | PlaybackControlGlue.ACTION_FAST_FORWARD
+                   | PlaybackControlGlue.ACTION_REWIND;
+        }
+
+        @Override
+        public int getCurrentSpeedId() {
+            return mSpeed;
+        }
+
+        @Override
+        public int getCurrentPosition() {
+            int speed;
+            if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) {
+                speed = 0;
+            } else if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) {
+                speed = 1;
+            } else if (mSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
+                int index = mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
+                speed = getFastForwardSpeeds()[index];
+            } else if (mSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
+                int index = -mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
+                speed = -getRewindSpeeds()[index];
+            } else {
+                return -1;
+            }
+            long position = mStartPosition + (System.currentTimeMillis() - mStartTime) * speed;
+            if (position > getMediaDuration()) {
+                position = getMediaDuration();
+                onPlaybackComplete(true);
+            } else if (position < 0) {
+                position = 0;
+                onPlaybackComplete(false);
+            }
+            return (int) position;
+        }
+
+        void onPlaybackComplete(final boolean ended) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
+                        pausePlayback();
+                    } else {
+                        startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
+                    }
+                    mStartPosition = 0;
+                    onStateChanged();
+                }
+            });
+        }
+
+        @Override
+        protected void startPlayback(int speed) {
+            if (speed == mSpeed) {
+                return;
+            }
+            mStartPosition = getCurrentPosition();
+            mSpeed = speed;
+            mIsPlaying = true;
+            mStartTime = System.currentTimeMillis();
+        }
+
+        @Override
+        protected void pausePlayback() {
+            if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) {
+                return;
+            }
+            mStartPosition = getCurrentPosition();
+            mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED;
+            mIsPlaying = false;
+        }
+
+        @Override
+        protected void skipToNext() {
+            // Not supported
+        }
+
+        @Override
+        protected void skipToPrevious() {
+            // Not supported
+        }
+
+        @Override
+        public void enableProgressUpdating(boolean enable) {
+            mHandler.removeCallbacks(mUpdateProgressRunnable);
+            if (enable) {
+                mUpdateProgressRunnable.run();
+            }
+        }
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java
new file mode 100644
index 0000000..af56715
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridFragmentTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class VerticalGridFragmentTest {
+
+    public static class GridFragment extends VerticalGridFragment {
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            if (savedInstanceState == null) {
+                prepareEntranceTransition();
+            }
+            VerticalGridPresenter gridPresenter = new VerticalGridPresenter();
+            gridPresenter.setNumberOfColumns(3);
+            setGridPresenter(gridPresenter);
+            setAdapter(new ArrayObjectAdapter());
+        }
+    }
+
+    public static class ImmediateRemoveFragmentActivity extends Activity {
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            new Handler().postDelayed(new Runnable(){
+                public void run() {
+                    GridFragment f = new GridFragment();
+                    ImmediateRemoveFragmentActivity.this.getFragmentManager().beginTransaction()
+                            .replace(android.R.id.content, f, null).commit();
+                    f.startEntranceTransition();
+                    ImmediateRemoveFragmentActivity.this.getFragmentManager().beginTransaction()
+                            .replace(android.R.id.content, new Fragment(), null).commit();
+                }
+            }, 500);
+        }
+    }
+
+    @Test
+    public void immediateRemoveFragment() throws Throwable {
+        Intent intent = new Intent();
+        ActivityTestRule<ImmediateRemoveFragmentActivity> activityTestRule =
+                new ActivityTestRule<>(ImmediateRemoveFragmentActivity.class, false, false);
+        ImmediateRemoveFragmentActivity activity = activityTestRule.launchActivity(intent);
+
+        Thread.sleep(1000);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java
new file mode 100644
index 0000000..7dd402d
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VerticalGridSupportFragmentTest.java
@@ -0,0 +1,81 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from VerticalGridFragmentTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v17.leanback.app;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.VerticalGridPresenter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class VerticalGridSupportFragmentTest {
+
+    public static class GridFragment extends VerticalGridSupportFragment {
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            if (savedInstanceState == null) {
+                prepareEntranceTransition();
+            }
+            VerticalGridPresenter gridPresenter = new VerticalGridPresenter();
+            gridPresenter.setNumberOfColumns(3);
+            setGridPresenter(gridPresenter);
+            setAdapter(new ArrayObjectAdapter());
+        }
+    }
+
+    public static class ImmediateRemoveFragmentActivity extends FragmentActivity {
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            new Handler().postDelayed(new Runnable(){
+                public void run() {
+                    GridFragment f = new GridFragment();
+                    ImmediateRemoveFragmentActivity.this.getSupportFragmentManager().beginTransaction()
+                            .replace(android.R.id.content, f, null).commit();
+                    f.startEntranceTransition();
+                    ImmediateRemoveFragmentActivity.this.getSupportFragmentManager().beginTransaction()
+                            .replace(android.R.id.content, new Fragment(), null).commit();
+                }
+            }, 500);
+        }
+    }
+
+    @Test
+    public void immediateRemoveFragment() throws Throwable {
+        Intent intent = new Intent();
+        ActivityTestRule<ImmediateRemoveFragmentActivity> activityTestRule =
+                new ActivityTestRule<>(ImmediateRemoveFragmentActivity.class, false, false);
+        ImmediateRemoveFragmentActivity activity = activityTestRule.launchActivity(intent);
+
+        Thread.sleep(1000);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java
new file mode 100644
index 0000000..b8bb68e
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.test.R;
+import android.view.SurfaceHolder;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class VideoFragmentTest {
+
+    @Rule
+    public ActivityTestRule<VideoFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(VideoFragmentTestActivity.class, false, false);
+    private VideoFragmentTestActivity mActivity;
+
+    @Test
+    public void setSurfaceViewCallbackBeforeCreate() {
+        Intent intent = new Intent();
+        mActivity = activityTestRule.launchActivity(intent);
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.replaceVideoFragment();
+            }
+        });
+
+        VideoFragment fragment = (VideoFragment) mActivity.getFragmentManager().findFragmentById(
+                R.id.video_fragment);
+        assertNotNull(fragment);
+    }
+
+    @Test
+    public void setSurfaceViewCallbackAfterCreate() {
+        Intent intent = new Intent();
+        mActivity = activityTestRule.launchActivity(intent);
+
+        VideoFragment fragment = (VideoFragment) mActivity.getFragmentManager().findFragmentById(
+                R.id.video_fragment);
+        assertNotNull(fragment);
+
+        fragment.setSurfaceHolderCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            }
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+            }
+        });
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTestActivity.java
new file mode 100644
index 0000000..e2a8f48
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoFragmentTestActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+
+/**
+ * Test activity containing {@link VideoFragment}.
+ */
+public class VideoFragmentTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.video_fragment_with_controls);
+    }
+
+    public void replaceVideoFragment() {
+        getFragmentManager().beginTransaction()
+                .replace(R.id.video_fragment, new VideoTestFragment())
+                .commitAllowingStateLoss();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/VideoTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoTestFragment.java
new file mode 100644
index 0000000..a51231f
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/VideoTestFragment.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * {@link VideoFragment} subclass used for testing.
+ */
+public class VideoTestFragment extends VideoFragment {
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+        setSurfaceHolderCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            }
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+            }
+        });
+
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
index 9cab5a1..ce11551 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedDatePickerTest.java
@@ -14,9 +14,15 @@
 
 package android.support.v17.leanback.app.wizard;
 
-import android.app.Instrumentation;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.app.GuidedStepFragment;
 import android.support.v17.leanback.test.R;
 import android.support.v17.leanback.widget.GuidanceStylist;
@@ -24,14 +30,15 @@
 import android.support.v17.leanback.widget.GuidedDatePickerAction;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.picker.DatePicker;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -39,43 +46,48 @@
 import java.util.List;
 
 @MediumTest
-public class GuidedDatePickerTest extends
-        ActivityInstrumentationTestCase2<GuidedStepAttributesTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class GuidedDatePickerTest {
 
     static final long TRANSITION_LENGTH = 1000;
     static long VERTICAL_SCROLL_WAIT = 500;
     static long HORIZONTAL_SCROLL_WAIT = 500;
-    static final long FINAL_WAIT = 3000;
+    static final long FINAL_WAIT = 1000;
 
     static final String TAG = "GuidedDatePickerTest";
 
     private static final int DAY_INDEX = 0;
     private static final int MONTH_INDEX = 1;
     private static final int YEAR_INDEX = 2;
-    Instrumentation mInstrumentation;
+
+    @Rule
+    public ActivityTestRule<GuidedStepAttributesTestActivity> activityTestRule =
+            new ActivityTestRule<>(GuidedStepAttributesTestActivity.class, false, false);
+
     GuidedStepAttributesTestActivity mActivity;
 
-    public GuidedDatePickerTest() {
-        super(GuidedStepAttributesTestActivity.class);
-    }
 
     private void initActivity(Intent intent) {
-
-        setActivityIntent(intent);
-        mActivity = getActivity();
+        mActivity = activityTestRule.launchActivity(intent);
         try {
             Thread.sleep(2000);
         } catch(InterruptedException e) {
             e.printStackTrace();
         }
+
     }
 
-    private void scrollOnField(int field, int[] columnIndices, DatePicker mPickerView,
-                               int SCROLL_DIR) throws Throwable {
+    Context mContext;
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();;
+    }
 
-        final GuidedStepFragment mFragment = (GuidedStepFragment)
-                mActivity.getGuidedStepTestFragment();
+    public static void sendKey(int keyCode) {
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+    }
 
+    private int getColumnIndexForDateField(int field, int[] columnIndices) {
         int mColDayIndex = columnIndices[0];
         int mColMonthIndex = columnIndices[1];
         int mColYearIndex = columnIndices[2];
@@ -90,13 +102,18 @@
             case Calendar.YEAR:
                 columnIndex = mColYearIndex;
         }
+        return columnIndex;
+    }
 
+    private void horizontalScrollToDateField(int field, int[] columnIndices,
+                                             DatePicker pickerView) throws Throwable{
+        int columnIndex = getColumnIndexForDateField(field, columnIndices);
 
-        LinearLayout columnsLayout = (LinearLayout) mPickerView.getChildAt(0);
+        LinearLayout columnsLayout = (LinearLayout) pickerView.getChildAt(0);
 
         int focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
         if (focusedFieldPos == -1) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
             Thread.sleep(TRANSITION_LENGTH);
         }
         focusedFieldPos = columnsLayout.indexOfChild(columnsLayout.getFocusedChild());
@@ -124,45 +141,67 @@
             horizontalScrollDir = KeyEvent.KEYCODE_DPAD_LEFT;
         }
         for(int i = 0; i < horizontalScrollOffset; i++) {
-            sendKeys(horizontalScrollDir);
+            sendKey(horizontalScrollDir);
             Thread.sleep(HORIZONTAL_SCROLL_WAIT);
         }
 
+    }
+
+    /**
+     * Scrolls vertically all the way up or down (depending on the provided scrollDir parameter)
+     * to fieldValue if it's not equal to -1; otherwise, the scrolling goes all the way to the end.
+     * @param field The date field over which the scrolling is performed
+     * @param fieldValue The field value to scroll to or -1 if the scrolling should go all the way.
+     * @param columnIndices The date field indices corresponding to day, month, and the year
+     * @param pickerView The DatePicker view.
+     * @param scrollDir The direction of scrolling to reach the desired field value.
+     * @throws Throwable
+     */
+    private void verticalScrollToFieldValue(int field, int fieldValue, int[] columnIndices,
+                                                 DatePicker pickerView, int scrollDir)
+            throws Throwable {
+
+        int columnIndex = getColumnIndexForDateField(field, columnIndices);
+        int colDayIndex = columnIndices[0];
+        int colMonthIndex = columnIndices[1];
+        int colYearIndex = columnIndices[2];
+
+        horizontalScrollToDateField(field, columnIndices, pickerView);
 
         Calendar currentActionCal = Calendar.getInstance();
-        currentActionCal.setTimeInMillis(mPickerView.getDate());
+        currentActionCal.setTimeInMillis(pickerView.getDate());
 
         Calendar minCal = Calendar.getInstance();
-        minCal.setTimeInMillis(mPickerView.getMinDate());
+        minCal.setTimeInMillis(pickerView.getMinDate());
 
         Calendar maxCal = Calendar.getInstance();
-        maxCal.setTimeInMillis(mPickerView.getMaxDate());
+        maxCal.setTimeInMillis(pickerView.getMaxDate());
 
 
         int prevColumnVal = -1;
-        int currentColumnVal = mPickerView.getColumnAt(columnIndex).getCurrentValue();
-        while( currentColumnVal != prevColumnVal ){
-            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_day_value),
-                    mPickerView.getColumnAt(mColDayIndex).getCurrentValue() ==
-                            currentActionCal.get(Calendar.DAY_OF_MONTH)
+        int currentColumnVal = pickerView.getColumnAt(columnIndex).getCurrentValue();
+        while( currentColumnVal != prevColumnVal && currentColumnVal != fieldValue){
+            assertTrue(mContext.getString(R.string.datepicker_test_wrong_day_value),
+                    pickerView.getColumnAt(colDayIndex).getCurrentValue()
+                            == currentActionCal.get(Calendar.DAY_OF_MONTH)
             );
-            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_month_value),
-                    mPickerView.getColumnAt(mColMonthIndex).getCurrentValue() ==
-                            currentActionCal.get(Calendar.MONTH)
+            assertTrue(mContext.getString(R.string.datepicker_test_wrong_month_value),
+                    pickerView.getColumnAt(colMonthIndex).getCurrentValue()
+                            == currentActionCal.get(Calendar.MONTH)
             );
-            assertTrue(getActivity().getString(R.string.datepicker_test_wrong_year_value),
-                    mPickerView.getColumnAt(mColYearIndex).getCurrentValue() ==
-                            currentActionCal.get(Calendar.YEAR)
+            assertTrue(mContext.getString(R.string.datepicker_test_wrong_year_value),
+                    pickerView.getColumnAt(colYearIndex).getCurrentValue()
+                            == currentActionCal.get(Calendar.YEAR)
             );
 
-            int offset = SCROLL_DIR == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : -1;
+            int offset = scrollDir == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : -1;
             addDate(currentActionCal, field, offset, minCal, maxCal);
 
-            sendKeys(SCROLL_DIR);
+            sendKey(scrollDir);
             Thread.sleep(VERTICAL_SCROLL_WAIT);
 
             prevColumnVal = currentColumnVal;
-            currentColumnVal = mPickerView.getColumnAt(columnIndex).getCurrentValue();
+            currentColumnVal = pickerView.getColumnAt(columnIndex).getCurrentValue();
         }
     }
 
@@ -196,18 +235,14 @@
         }
     }
 
-    public void testDifferentMonthLengths() throws Throwable {
-
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
-
-        final int NUM_DATE_ACTIONS = 1;
+    @Test
+    public void testJanuaryToFebruaryTransitionForLeapYear() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
 
         String title = "Date Picker Transition Test";
         String breadcrumb = "Month Transition Test Demo";
-        String description = "Testing the transition between longer to shorter months";
+        String description = "Testing the transition from Jan to Feb (leap year)";
         GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
                 breadcrumb, null);
 
@@ -215,13 +250,13 @@
 
         Calendar cal = Calendar.getInstance();
 
-        cal.set(Calendar.YEAR, 2016);
+        cal.set(Calendar.YEAR, 2016);   // 2016 is a leap year
         cal.set(Calendar.MONTH, Calendar.JANUARY);
-        cal.set(Calendar.DAY_OF_MONTH, 30);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
         Date initialDate = cal.getTime();
 
         GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(0)
                 .title("Date")
                 .date(initialDate.getTime())
@@ -239,14 +274,337 @@
         DatePicker mPickerView = (DatePicker) mActivity.findViewById(
                 R.id.guidedactions_activator_item);
 
-        final GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.
-                getGuidedStepTestFragment();
-        traverseMonths(mPickerView, (GuidedDatePickerAction) actionList.get(0));
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.FEBRUARY, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testJanuaryToFebruaryTransitionForLeapYear() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testFebruaryToMarchTransitionForLeapYear() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Month Transition Test Demo";
+        String description = "Testing the transition from Feb to Mar (leap year)";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2016);
+        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.MARCH, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testFebruaryToMarchTransition() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testJanuaryToFebruaryTransitionForNonLeapYear() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Month Transition Test Demo";
+        String description = "Testing the transition from Jan to Feb (nonleap year)";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2017);   // 2017 is a leap year
+        cal.set(Calendar.MONTH, Calendar.JANUARY);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.FEBRUARY, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testJanuaryToFebruaryTransition() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testFebruaryToMarchTransitionForNonLeapYear() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Month Transition Test Demo";
+        String description = "Testing the transition from Feb to Mar (nonleap year)";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2017);
+        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.MARCH, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testFebruaryToMarchTransition() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testDecemberToNovemberTransition() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Month Transition Test Demo";
+        String description = "Testing the transition from Dec to Nov";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2016);
+        cal.set(Calendar.MONTH, Calendar.DECEMBER);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.NOVEMBER, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDecemberToNovember() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testNovemberToOctoberTransition() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Month Transition Test Demo";
+        String description = "Testing the transition from Nov to Oct";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2016);
+        cal.set(Calendar.MONTH, Calendar.NOVEMBER);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.MONTH, Calendar.OCTOBER, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testNovemberToOctober() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testLeapToNonLeapYearTransition() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Leap Year Transition Test Demo";
+        String description = "Testing Feb transition from leap to nonlneap year";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2016);   // 2016 is a leap year
+        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.YEAR, 2017, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testLeapToNonLeapYearTransition() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testNonLeapToLeapYearTransition() throws Throwable {
+        long startTime = System.currentTimeMillis();
+        Intent intent = new Intent();
+
+        String title = "Date Picker Transition Test";
+        String breadcrumb = "Leap Year Transition Test Demo";
+        String description = "Testing Feb transition from nonleap to leap year";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, 2017);   // 2017 is a non-leap year
+        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
+        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        Date initialDate = cal.getTime();
+
+        GuidedDatePickerAction action = new GuidedDatePickerAction.Builder(
+                mContext)
+                .id(0)
+                .title("Date")
+                .date(initialDate.getTime())
+                .datePickerFormat("DMY")
+                .build();
+
+        actionList.add(action);
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+
+        initActivity(intent);
+
+        DatePicker mPickerView = (DatePicker) mActivity.findViewById(
+                R.id.guidedactions_activator_item);
+
+        verticalScrollToFieldValue(Calendar.YEAR, 2016, new int[] {0, 1, 2},
+                mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testNonLeapToLeapYearTransition() Execution time: " + executionTime);
         Thread.sleep(FINAL_WAIT);
     }
 
     private void traverseMonths(DatePicker mPickerView, GuidedDatePickerAction dateAction)
-            throws Throwable{
+            throws Throwable {
 
         final GuidedStepFragment mFragment = (GuidedStepFragment)
                 mActivity.getGuidedStepTestFragment();
@@ -254,7 +612,7 @@
         Calendar currentActionCal = Calendar.getInstance();
         currentActionCal.setTimeInMillis(dateAction.getDate());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
         Thread.sleep(TRANSITION_LENGTH);
 
         int prevMonth = -1;
@@ -264,7 +622,7 @@
             int currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
             // scroll down the days till reaching the last day of month
             while (currentDayOfMonth != prevDayOfMonth) {
-                sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+                sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
                 Thread.sleep(VERTICAL_SCROLL_WAIT);
                 prevDayOfMonth = currentDayOfMonth;
                 currentDayOfMonth = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
@@ -272,10 +630,10 @@
             int oldDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
             int oldMonthValue = mPickerView.getColumnAt(MONTH_INDEX).getCurrentValue();
             // increment the month
-            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+            sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
             Thread.sleep(VERTICAL_SCROLL_WAIT);
 
-            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
             Thread.sleep(TRANSITION_LENGTH);
 
             int newDayValue = mPickerView.getColumnAt(DAY_INDEX).getCurrentValue();
@@ -283,7 +641,7 @@
             verifyMonthTransition(currentActionCal,
                     oldDayValue, oldMonthValue, newDayValue, newMonthValue);
 
-            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+            sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
             Thread.sleep(TRANSITION_LENGTH);
             prevMonth = currentMonth;
             currentMonth = newMonthValue;
@@ -291,6 +649,7 @@
 
     }
 
+
     private void verifyMonthTransition(Calendar currentCal, int oldDayValue, int oldMonthValue,
                                        int newDayValue, int newMonthValue) {
 
@@ -302,25 +661,86 @@
         int expectedOldDayValue = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
         currentCal.set(Calendar.MONTH, newMonthValue);
         int numDaysInNewMonth = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
-        int expectedNewDayValue = (expectedOldDayValue <= numDaysInNewMonth) ?
-                expectedOldDayValue : numDaysInNewMonth;
+        int expectedNewDayValue = (expectedOldDayValue <= numDaysInNewMonth)
+                ? expectedOldDayValue : numDaysInNewMonth;
 
-        assertTrue(getActivity().getString(
+        assertTrue(mContext.getString(
                 R.string.datepicker_test_transition_error1, oldMonthValue),
                 oldDayValue == expectedOldDayValue
         );
-        assertTrue(getActivity().getString(
+        assertTrue(mContext.getString(
                 R.string.datepicker_test_transition_error2, newDayValue, newMonthValue),
                 newDayValue == expectedNewDayValue
         );
     }
 
-    public void testDateRanges() throws Throwable {
+    @Test
+    public void testDateRangesMDYFormat() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
+        long startTime = System.currentTimeMillis();
+
+        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
+
+        scrollToMinAndMaxDates(new int[] {1, 0, 2}, datePickerActions[0]);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDateRangesMDYFormat() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    public void testDateRangesDMYFormat() throws Throwable {
+
+        long startTime = System.currentTimeMillis();
+
+        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
+        Log.d(TAG, "setup dateactions complete!");
+        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[1]);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDateRangesDMYFormat() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testDateRangesWithYearEqual() throws Throwable {
+
+        long startTime = System.currentTimeMillis();
+
+        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
+
+        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[2]);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDateRangesWithYearEqual() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testDateRangesWithMonthAndYearEqual() throws Throwable {
+
+        long startTime = System.currentTimeMillis();
+
+        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
+
+        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[3]);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDateRangesWithMonthAndYearEqual() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    @Test
+    public void testDateRangesWithAllFieldsEqual() throws Throwable {
+
+        long startTime = System.currentTimeMillis();
+
+        GuidedDatePickerAction[] datePickerActions = setupDateActionsForMinAndMaxRangeTests();
+
+        scrollToMinAndMaxDates(new int[] {0, 1, 2}, datePickerActions[4]);
+        long executionTime = System.currentTimeMillis() - startTime;
+        Log.d(TAG, "testDateRangesWithAllFieldsEqual() Execution time: " + executionTime);
+        Thread.sleep(FINAL_WAIT);
+    }
+
+    private GuidedDatePickerAction[] setupDateActionsForMinAndMaxRangeTests() {
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
 
         SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
 
@@ -349,7 +769,7 @@
 
         // testing different date formats and the correctness of range changes as we scroll
         GuidedDatePickerAction dateAction1 = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(0)
                 .title(res.getString(R.string.datepicker_with_range_title,
                         dateFormat.format(minCal.getTime()),
@@ -362,7 +782,7 @@
                 .build();
 
         GuidedDatePickerAction dateAction2 = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(1)
                 .title(res.getString(R.string.datepicker_with_range_title,
                         dateFormat.format(minCal.getTimeInMillis()),
@@ -382,7 +802,7 @@
         maxCal.set(Calendar.MONTH, maxMonth);
 
         GuidedDatePickerAction dateAction3 = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(2)
                 .title(res.getString(R.string.datepicker_with_range_title,
                         dateFormat.format(minCal.getTimeInMillis()),
@@ -403,7 +823,7 @@
         maxCal.set(Calendar.DAY_OF_MONTH, maxDay);
 
         GuidedDatePickerAction dateAction4 = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(3)
                 .title(res.getString(R.string.datepicker_with_range_title,
                         dateFormat.format(minCal.getTimeInMillis()),
@@ -420,7 +840,7 @@
         minCal.set(Calendar.DAY_OF_MONTH, maxCal.get(Calendar.DAY_OF_MONTH));
 
         GuidedDatePickerAction dateAction5 = new GuidedDatePickerAction.Builder(
-                mInstrumentation.getContext())
+                mContext)
                 .id(4)
                 .title(res.getString(R.string.datepicker_with_range_title,
                         dateFormat.format(minCal.getTimeInMillis()),
@@ -443,17 +863,8 @@
         GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
 
         initActivity(intent);
-
-        final GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.
-                getGuidedStepTestFragment();
-
-        scrollToMinAndMaxDates(new int[] {1, 0, 2}, dateAction1);
-        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction2);
-        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction3);
-        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction4);
-        scrollToMinAndMaxDates(new int[] {0, 1, 2}, dateAction5);
-
-        Thread.sleep(FINAL_WAIT);
+        return new GuidedDatePickerAction[] {dateAction1, dateAction2, dateAction3, dateAction4,
+                dateAction5};
     }
 
     private void scrollToMinAndMaxDates(int[] columnIndices, GuidedDatePickerAction dateAction)
@@ -475,12 +886,12 @@
             verticalScrollDir = KeyEvent.KEYCODE_DPAD_UP;
         }
         for(int i = 0; i < verticalScrollOffset; i++) {
-            sendKeys(verticalScrollDir);
+            sendKey(verticalScrollDir);
             Thread.sleep(TRANSITION_LENGTH);
         }
 
-        assertTrue("The wrong action was selected!", mFragment.getSelectedActionPosition() ==
-                dateAction.getId());
+        assertTrue("The wrong action was selected!", mFragment.getSelectedActionPosition()
+                == dateAction.getId());
         DatePicker mPickerView = (DatePicker) mFragment.getActionItemView((int) dateAction.getId())
                 .findViewById(R.id.guidedactions_activator_item);
 
@@ -490,29 +901,35 @@
 
         // scrolling to the minimum date
 
-        scrollOnField(Calendar.YEAR, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        verticalScrollToFieldValue(Calendar.YEAR, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_UP);
         dateAction.setDate(mPickerView.getDate());
 
-        scrollOnField(Calendar.MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        verticalScrollToFieldValue(Calendar.MONTH, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_UP);
         dateAction.setDate(mPickerView.getDate());
 
-        scrollOnField(Calendar.DAY_OF_MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_UP);
+        verticalScrollToFieldValue(Calendar.DAY_OF_MONTH, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_UP);
         dateAction.setDate(mPickerView.getDate());
 
         Thread.sleep(VERTICAL_SCROLL_WAIT);
 
         // now scrolling to the maximum date
 
-        scrollOnField(Calendar.YEAR, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        verticalScrollToFieldValue(Calendar.YEAR, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_DOWN);
         dateAction.setDate(mPickerView.getDate());
 
-        scrollOnField(Calendar.MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        verticalScrollToFieldValue(Calendar.MONTH, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_DOWN);
         dateAction.setDate(mPickerView.getDate());
 
-        scrollOnField(Calendar.DAY_OF_MONTH, columnIndices, mPickerView, KeyEvent.KEYCODE_DPAD_DOWN);
+        verticalScrollToFieldValue(Calendar.DAY_OF_MONTH, -1, columnIndices, mPickerView,
+                KeyEvent.KEYCODE_DPAD_DOWN);
         dateAction.setDate(mPickerView.getDate());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
         Thread.sleep(TRANSITION_LENGTH);
     }
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
index 8387dbd..0283687 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTest.java
@@ -14,41 +14,46 @@
 
 package android.support.v17.leanback.app.wizard;
 
-import android.app.Instrumentation;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.app.GuidedStepFragment;
 import android.support.v17.leanback.test.R;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidedAction;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
 import android.view.KeyEvent;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 @MediumTest
-public class GuidedStepAttributesTest extends
-        ActivityInstrumentationTestCase2<GuidedStepAttributesTestActivity> {
+@RunWith(AndroidJUnit4.class)
+public class GuidedStepAttributesTest {
     static final long TRANSITION_LENGTH = 1000;
 
     static final String TAG = "GuidedStepAttributesTest";
 
-    Instrumentation mInstrumentation;
+    @Rule
+    public ActivityTestRule<GuidedStepAttributesTestActivity> activityTestRule =
+            new ActivityTestRule<>(GuidedStepAttributesTestActivity.class, false, false);
+
     GuidedStepAttributesTestActivity mActivity;
 
-    public GuidedStepAttributesTest() {
-        super(GuidedStepAttributesTestActivity.class);
-    }
-
     private void initActivity(Intent intent) {
-
-        setActivityIntent(intent);
-        mActivity = getActivity();
+        mActivity = activityTestRule.launchActivity(intent);
         try {
             Thread.sleep(2000);
         } catch(InterruptedException e) {
@@ -56,12 +61,21 @@
         }
     }
 
+    Context mContext;
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();;
+    }
+
+    public static void sendKey(int keyCode) {
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+    }
+
+    @Test
     public void testFocusDisabledOnActions() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
 
         final int NUM_SEARCH_ACTIONS = 10;
         final List<Integer> ACTIONS_WITH_DISABLED_FOCUS = new ArrayList<>(
@@ -87,7 +101,7 @@
 
         List<GuidedAction> actionList = new ArrayList<>();
         for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) {
-            actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+            actionList.add(new GuidedAction.Builder(mContext)
                     .id(ACTION_ID_SEARCH)
                     .title(res.getString(R.string.search) + "" + i)
                     .description(res.getString(R.string.search_description) + i)
@@ -113,7 +127,7 @@
                     actionList.get(lastSelectedActionId).getTitle()),
                     lastSelectedActionId == EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.get(selectIndex));
             selectIndex++;
-            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
             prevSelectedActionPosition = nextSelectedActionPosition;
             nextSelectedActionPosition = mFragment.getSelectedActionPosition();
             Thread.sleep(TRANSITION_LENGTH);
@@ -126,7 +140,7 @@
                     actionList.get(lastSelectedActionId).getTitle()),
                     lastSelectedActionId == EXPECTED_ACTIONS_ID_AFTER_EACH_SELECT.get(selectIndex));
             selectIndex++;
-            sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+            sendKey(KeyEvent.KEYCODE_DPAD_UP);
             prevSelectedActionPosition = nextSelectedActionPosition;
             nextSelectedActionPosition = mFragment.getSelectedActionPosition();
             Thread.sleep(TRANSITION_LENGTH);
@@ -148,12 +162,16 @@
         }
     };
 
+    /**
+     * Creates a number of enabled and disable actions and tests whether the flag is correctly set
+     * by clicking on each individual action and checking whether the click event is triggered.
+     * @throws Throwable
+     */
+    @Test
     public void testDisabledActions() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
 
         final int NUM_SEARCH_ACTIONS = 10;
         final List<Integer> DISABLED_ACTIONS = new ArrayList<>(
@@ -193,7 +211,7 @@
                 breadcrumb, null);
 
         List<GuidedAction> actionList = new ArrayList<>();
-        actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+        actionList.add(new GuidedAction.Builder(mContext)
                 .id(ACTION_ID_REVERT_BUTTON)
                 .title(res.getString(R.string.invert_title))
                 .description(res.getString(R.string.revert_description))
@@ -201,7 +219,7 @@
         );
 
         for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) {
-            actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+            actionList.add(new GuidedAction.Builder(mContext)
                     .id(ACTION_ID_SEARCH_END++)
                     .title(res.getString(R.string.search) + "" + i)
                     .description(res.getString(R.string.search_description) + i)
@@ -223,32 +241,102 @@
 
         initActivity(intent);
 
+        examineEnabledAndDisabledActions(actionList, CLICK_SEQUENCE, EXPECTED_FOCUSED_SEQUENCE,
+                EXPECTED_CLICKED_SEQUENCE);
+    }
+
+    /**
+     * Toggles Enabled flags in oll the actions of the prior test, and tests whether they are
+     * correctly reverted.
+     */
+    @Test
+    public void testToggleEnabledFlags() throws Throwable {
+
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
+
+        final int NUM_SEARCH_ACTIONS = 10;
+        final List<Integer> DISABLED_ACTIONS = new ArrayList<>(
+                Arrays.asList(1, 3, 5, 7));
+        final int ACTION_ID_REVERT_BUTTON = 0;
+        final int ACTION_ID_SEARCH_BEGIN = ACTION_ID_REVERT_BUTTON + 1;
+        int ACTION_ID_SEARCH_END = ACTION_ID_SEARCH_BEGIN;
+
+        // sequence of clicked actions simulated in the test
+        List<Integer> CLICK_SEQUENCE = new ArrayList<>();
+
+        // Expected Clicked sequence can be different from focused ones since some of the actions
+        // are disabled hence not clickable
+        List<Integer> EXPECTED_FOCUSED_SEQUENCE = new ArrayList<>();
+        List<Integer> EXPECTED_CLICKED_SEQUENCE = new ArrayList<>();
+        // Expected actions state according to list of DISABLED_ACTIONS: false for disabled actions
+        List<Boolean> EXPECTED_ACTIONS_STATE = new ArrayList<>(
+                Arrays.asList(new Boolean[NUM_SEARCH_ACTIONS])
+        );
+        Collections.fill(EXPECTED_ACTIONS_STATE, Boolean.FALSE);
+
+        for(int i = 0; i < NUM_SEARCH_ACTIONS; i++) {
+            CLICK_SEQUENCE.add(i + 1);
+        }
+        for(int clickedActionId : CLICK_SEQUENCE) {
+            EXPECTED_FOCUSED_SEQUENCE.add(clickedActionId);
+            if (DISABLED_ACTIONS.contains(clickedActionId - 1))
+                EXPECTED_CLICKED_SEQUENCE.add(clickedActionId);
+            else
+                EXPECTED_CLICKED_SEQUENCE.add(-1);
+        }
+
+        String title = "Guided Actions Enabled Test";
+        String breadcrumb = "Toggle Enabled Flag Test Demo";
+        String description = "";
+        GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
+                breadcrumb, null);
+
+        List<GuidedAction> actionList = new ArrayList<>();
+        actionList.add(new GuidedAction.Builder(mContext)
+                .id(ACTION_ID_REVERT_BUTTON)
+                .title(res.getString(R.string.invert_title))
+                .description(res.getString(R.string.revert_description))
+                .build()
+        );
+
+        for (int i = 0; i < NUM_SEARCH_ACTIONS; i++ ) {
+            actionList.add(new GuidedAction.Builder(mContext)
+                    .id(ACTION_ID_SEARCH_END++)
+                    .title(res.getString(R.string.search) + "" + i)
+                    .description(res.getString(R.string.search_description) + i)
+                    .build()
+            );
+        }
+        for(int action_id : DISABLED_ACTIONS ) {
+            if ( action_id >= 0 && action_id < NUM_SEARCH_ACTIONS ) {
+                actionList.get(action_id + 1).setEnabled(false);
+                EXPECTED_ACTIONS_STATE.set(action_id, Boolean.TRUE);
+            }
+        }
+
+        GuidedStepAttributesTestFragment.clear();
+        GuidedStepAttributesTestFragment.GUIDANCE = guidance;
+        GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
+        GuidedStepAttributesTestFragment.setActionClickCallback(ACTION_ID_REVERT_BUTTON,
+                sRevertCallback);
+
+        initActivity(intent);
+
         final GuidedStepFragment mFragment = (GuidedStepFragment)
                 mActivity.getGuidedStepTestFragment();
 
-        examineEnabledAndDisabledActions(actionList, CLICK_SEQUENCE, EXPECTED_FOCUSED_SEQUENCE,
-                EXPECTED_CLICKED_SEQUENCE);
-        // now toggling all enabled/disabled actions to disabled/enabled and running the test again
-        Log.d(TAG, "Toggling actions...");
-        runTestOnUiThread(new Runnable() {
+        mActivity.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mFragment.setSelectedActionPosition(0);
             }
         });
         Thread.sleep(TRANSITION_LENGTH);
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
         Thread.sleep(TRANSITION_LENGTH);
-        for(int i = 0; i < EXPECTED_CLICKED_SEQUENCE.size(); i++) {
-            if (EXPECTED_CLICKED_SEQUENCE.get(i) == -1)
-                EXPECTED_CLICKED_SEQUENCE.set(i, CLICK_SEQUENCE.get(i));
-            else
-                EXPECTED_CLICKED_SEQUENCE.set(i, -1);
-        }
-
         examineEnabledAndDisabledActions(actionList, CLICK_SEQUENCE, EXPECTED_FOCUSED_SEQUENCE,
                 EXPECTED_CLICKED_SEQUENCE);
-
     }
 
     private void examineEnabledAndDisabledActions(
@@ -264,7 +352,7 @@
             GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID =
                     GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID = -1;
             final int id = CLICK_SEQUENCE.get(i);
-            runTestOnUiThread(new Runnable() {
+            mActivity.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mFragment.setSelectedActionPosition(id);
@@ -272,34 +360,33 @@
             });
             Thread.sleep(TRANSITION_LENGTH);
 
-            sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
             Thread.sleep(TRANSITION_LENGTH);
 
-            assertTrue(mInstrumentation.getContext().getResources().getString(
+            assertTrue(mContext.getResources().getString(
                     R.string.enabled_test_wrong_focus_error_message),
-                    GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                            EXPECTED_FOCUSED_SEQUENCE.get(i)
+                    GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                            == EXPECTED_FOCUSED_SEQUENCE.get(i)
             );
-            assertTrue(mInstrumentation.getContext().getResources().getString(
+            assertTrue(mContext.getResources().getString(
                     R.string.enabled_test_wrong_click_error_message),
-                    GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID ==
-                            EXPECTED_CLICKED_SEQUENCE.get(i)
+                    GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID
+                            == EXPECTED_CLICKED_SEQUENCE.get(i)
             );
-            assertTrue(mInstrumentation.getContext().getResources().getString(
+            assertTrue(mContext.getResources().getString(
                     R.string.enabled_test_wrong_flag_error_message),
-                    (GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID == -1) ?
-                            !actionList.get(id).isEnabled() :
-                            actionList.get(id).isEnabled()
+                    (GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID == -1)
+                            ? !actionList.get(id).isEnabled()
+                            : actionList.get(id).isEnabled()
             );
         }
     }
 
+    @Test
     public void testCheckedActions() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
 
         final int NUM_RADIO_ACTIONS = 3;
         final int NUM_CHECK_BOX_ACTIONS = 3;
@@ -331,7 +418,7 @@
                 breadcrumb, null);
 
         List<GuidedAction> actionList = new ArrayList<>();
-        actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+        actionList.add(new GuidedAction.Builder(mContext)
                 .title(res.getString(R.string.radio_actions_info_title))
                 .description(res.getString(R.string.radio_actions_info_desc))
                 .infoOnly(true)
@@ -342,7 +429,7 @@
 
         int firstRadioActionIndex = actionList.size();
         for(int i = 0; i < NUM_RADIO_ACTIONS; i++) {
-            actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+            actionList.add(new GuidedAction.Builder(mContext)
                     .title(res.getString(R.string.checkbox_title) + i)
                     .description(res.getString(R.string.checkbox_desc) + i)
                     .checkSetId(GuidedAction.DEFAULT_CHECK_SET_ID)
@@ -352,7 +439,7 @@
                 actionList.get(firstRadioActionIndex + i).setChecked(true);
         }
 
-        actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+        actionList.add(new GuidedAction.Builder(mContext)
                 .title(res.getString(R.string.checkbox_actions_info_title))
                 .description(res.getString(R.string.checkbox_actions_info_desc))
                 .infoOnly(true)
@@ -362,7 +449,7 @@
         );
         int firstCheckBoxActionIndex = actionList.size();
         for(int i = 0; i < NUM_CHECK_BOX_ACTIONS; i++) {
-            actionList.add(new GuidedAction.Builder(mInstrumentation.getContext())
+            actionList.add(new GuidedAction.Builder(mContext)
                     .title(res.getString(R.string.checkbox_title) + i)
                     .description(res.getString(R.string.checkbox_desc) + i)
                     .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID)
@@ -404,11 +491,9 @@
         for(GuidedAction checkAction : actionList) {
             if (checkAction.infoOnly())
                 continue;
-            assertTrue("Action " + actionIndex + " is " +
-                            (!checkAction.isChecked() ? "un-" : "") +
-                    "checked while it shouldn't be!",
-                    checkAction.isChecked() ==
-                            EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.get(actionIndex));
+            assertTrue("Action " + actionIndex + " is " + (!checkAction.isChecked() ? "un-" : "")
+                    + "checked while it shouldn't be!", checkAction.isChecked()
+                            == EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK.get(actionIndex));
             actionIndex++;
         }
     }
@@ -424,7 +509,7 @@
         final int firstCheckBoxActionIndex = firstRadioActionIndex + NUM_RADIO_ACTIONS + 1;
         for(int actionId = 0; actionId < NUM_RADIO_ACTIONS; actionId++) {
             final int id = actionId;
-            runTestOnUiThread(new Runnable() {
+            mActivity.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     guidedStepCheckedFragment
@@ -433,7 +518,7 @@
             });
             Thread.sleep(TRANSITION_LENGTH);
 
-            sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
             Thread.sleep(TRANSITION_LENGTH);
             updateExpectedActionsStateAfterClick(EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK,
                     NUM_RADIO_ACTIONS, NUM_CHECK_BOX_ACTIONS, actionId);
@@ -442,7 +527,7 @@
 
         for(int actionId = 0; actionId < NUM_CHECK_BOX_ACTIONS; actionId++) {
             final int id = actionId;
-            runTestOnUiThread(new Runnable() {
+            mActivity.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     guidedStepCheckedFragment
@@ -451,7 +536,7 @@
             });
             Thread.sleep(TRANSITION_LENGTH);
 
-            sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
             Thread.sleep(TRANSITION_LENGTH);
             updateExpectedActionsStateAfterClick(EXPECTED_ACTIONS_STATE_AFTER_EACH_CLICK,
                     NUM_RADIO_ACTIONS, NUM_CHECK_BOX_ACTIONS, NUM_RADIO_ACTIONS + actionId);
@@ -459,20 +544,185 @@
         }
     }
 
-    public void testSubActions() throws Throwable {
+    @Test
+    public void testActionWithTwoSubActions() throws Throwable {
+        ExpectedSubActionResult result = setUpActionsForSubActionsTest();
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(),
-                GuidedStepAttributesTestActivity.class);
-        Resources res = mInstrumentation.getContext().getResources();
+        final int actionPos = 0;
+        final GuidedAction selectedAction = result.actionList.get(actionPos);
+        List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos);
+        List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos);
 
-        String TAG = "testSubActions";
+        traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq,
+                expectedClickedSeq);
+    }
+
+    @Test
+    public void testActionWithOneSubAction() throws Throwable {
+        ExpectedSubActionResult result = setUpActionsForSubActionsTest();
+
+        final int actionPos = 1;
+        final GuidedAction selectedAction = result.actionList.get(actionPos);
+        List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos);
+        List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos);
+
+        traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq,
+                expectedClickedSeq);
+    }
+
+    @Test
+    public void testActionWithZeroSubActions() throws Throwable {
+        ExpectedSubActionResult result = setUpActionsForSubActionsTest();
+
+        final int actionPos = 2;
+        final GuidedAction selectedAction = result.actionList.get(actionPos);
+        List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos);
+        List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos);
+
+        traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq,
+                expectedClickedSeq);
+    }
+
+    @Test
+    public void testActionWithThreeSubActions() throws Throwable {
+        ExpectedSubActionResult result = setUpActionsForSubActionsTest();
+
+        final int actionPos = 3;
+        final GuidedAction selectedAction = result.actionList.get(actionPos);
+        List<Integer> expectedFocusedSeq = result.expectedFocusedSeq.get(actionPos);
+        List<Integer> expectedClickedSeq = result.expectedClickedSeq.get(actionPos);
+
+        traverseSubActionsAndVerifyFocusAndClickEvents(selectedAction, actionPos, expectedFocusedSeq,
+                expectedClickedSeq);
+    }
+
+    /**
+     * Traverses the list of sub actions of a gudied action. It also verifies the correct action
+     * or sub action is focused or clicked as the traversal is performed.
+     * @param selectedAction The action of interest
+     * @param actionPos The position of selectedAction within the array of guidedactions
+     * @param expectedFocusedSeq The actual actions IDs used as a reference to verify focused actions
+     * @param expectedClickedSeq The actual action IDs used as a reference to verify clicked actions
+     * @throws Throwable
+     */
+    private void traverseSubActionsAndVerifyFocusAndClickEvents(GuidedAction selectedAction,
+                                                                int actionPos,
+                                                                List<Integer> expectedFocusedSeq,
+                                                                List<Integer> expectedClickedSeq)
+            throws Throwable{
+
+        final GuidedStepFragment mFragment =
+                (GuidedStepFragment) mActivity.getGuidedStepTestFragment();
+        int focusStep = 0, clickStep = 0;
+        GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID =
+                GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID = -1;
+
+
+        final int pos = actionPos;
+        mActivity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mFragment.setSelectedActionPosition(pos);
+            }
+        });
+        Thread.sleep(TRANSITION_LENGTH);
+
+        if (mFragment.getSelectedActionPosition() != actionPos) {
+            assertTrue(mContext.getResources().getString(
+                    R.string.subaction_test_wrong_focus_error_message),
+                    GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                            == expectedFocusedSeq.get(focusStep++)
+            );
+        } else {
+            // If the currently focused position is the same as the position of the action of interest,
+            // then GuidedStepFragment won't received onGuidedActionFocused callback. Since the first
+            // element in the expectedFocusSeq is always the id of this action, we need to move focusStep
+            // one step forward.
+            focusStep++;
+        }
+        if (selectedAction.hasSubActions()) {
+            // Following for loop clicks on a specific action and scrolls & clicks through
+            // all its subactions
+            for (int j = 0; j < selectedAction.getSubActions().size(); j++) {
+                sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+                Thread.sleep(TRANSITION_LENGTH);
+                assertTrue(mContext.getResources().getString(
+                        R.string.subaction_test_wrong_focus_error_message),
+                        GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                                == expectedFocusedSeq.get(focusStep++)
+                );
+                assertTrue(mContext.getResources().getString(
+                        R.string.subaction_test_wrong_click_error_message),
+                        GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID
+                                == expectedClickedSeq.get(clickStep++)
+                );
+
+                for (int k = 0; k < j; k++) {
+                    sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+                    Thread.sleep(TRANSITION_LENGTH);
+                    assertTrue(mContext.getResources().getString(
+                            R.string.subaction_test_wrong_focus_error_message),
+                            GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                                    == expectedFocusedSeq.get(focusStep++)
+                    );
+                }
+                sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+                Thread.sleep(TRANSITION_LENGTH);
+
+                assertTrue(mContext.getResources().getString(
+                        R.string.subaction_test_wrong_focus_error_message),
+                        GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                                == expectedFocusedSeq.get(focusStep++)
+                );
+                assertTrue(mContext.getResources().getString(
+                        R.string.subaction_test_wrong_click_error_message),
+                        GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID
+                                == expectedClickedSeq.get(clickStep++)
+                );
+            }
+        } else {
+            sendKey(KeyEvent.KEYCODE_DPAD_CENTER);
+            Thread.sleep(TRANSITION_LENGTH);
+            assertTrue(mContext.getResources().getString(
+                    R.string.subaction_test_wrong_focus_error_message),
+                    GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID
+                            == expectedFocusedSeq.get(focusStep++)
+            );
+            assertTrue(mContext.getResources().getString(
+                    R.string.subaction_test_wrong_click_error_message),
+                    GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID
+                            == expectedClickedSeq.get(clickStep++)
+            );
+        }
+    }
+
+    static class ExpectedSubActionResult {
+        List<List<Integer>> expectedFocusedSeq; // Expected sequence of action (or subaction) ids to receive focus events;
+        // Each entry corresponds to an action item in the guidedactions pane
+        List<List<Integer>> expectedClickedSeq; // Expected sequence of action (or subaction) ids to receive click events;
+        // Each entry corresponds to an action item in the guidedactions pane
+        List<GuidedAction> actionList;          // List of GuidedActions in the guidedactions pane
+    }
+
+    /**
+     * Populates a sample list of actions and subactions in the guidedactions pane.
+     * @return  An object holding the expected sequence of action and subactions IDs that receive
+     * focus and click events as well as the list of GuidedActions.
+     */
+    private ExpectedSubActionResult setUpActionsForSubActionsTest() {
+        Intent intent = new Intent();
+        Resources res = mContext.getResources();
+
+        ExpectedSubActionResult result = new ExpectedSubActionResult();
+        result.expectedFocusedSeq = new ArrayList<>();
+        result.expectedClickedSeq = new ArrayList<>();
+
         final int NUM_REGULAR_ACTIONS = 4;
         final int[] NUM_SUBACTIONS_PER_ACTION = {2, 1, 0, 3};
         final int[] REGULAR_ACTIONS_INDEX =  new int[NUM_REGULAR_ACTIONS];
         final int[] BEGIN_SUBACTION_INDEX_PER_ACTION = new int[NUM_REGULAR_ACTIONS];
         final int[] END_SUBACTION_INDEX_PER_ACTION = new int[NUM_REGULAR_ACTIONS];
-        // Actions and Subactions are assigned unique sequential IDs
+        // Actions and SubActions are assigned unique sequential IDs
         int lastIndex = 0;
         for(int i = 0; i < NUM_REGULAR_ACTIONS; i++) {
             REGULAR_ACTIONS_INDEX[i] = lastIndex;
@@ -481,29 +731,27 @@
             END_SUBACTION_INDEX_PER_ACTION[i] = (lastIndex += NUM_SUBACTIONS_PER_ACTION[i]);
         }
 
-        // Sample click sequence for the main action list (not subactions)
-        List<Integer> ACTION_CLICK_SEQUENCE = new ArrayList<>(Arrays.asList(
-                3, 2, 1, 0
-        ));
-        List<Integer> EXPECTED_FOCUSED_SEQUENCE = new ArrayList<>();
-        List<Integer> EXPECTED_CLICKED_SEQUENCE = new ArrayList<>();
+        for (int i = 0; i < NUM_REGULAR_ACTIONS; i++) {
+            List<Integer> expectedFocusSeqForEachAction = new ArrayList<>();
+            List<Integer> expectedClickedSeqForEachAction = new ArrayList<>();
+            expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]);
 
-        for(int clickedActionId : ACTION_CLICK_SEQUENCE) {
-            EXPECTED_FOCUSED_SEQUENCE.add(REGULAR_ACTIONS_INDEX[clickedActionId]);
-            if (NUM_SUBACTIONS_PER_ACTION[clickedActionId] > 0) {
-                for (int i = BEGIN_SUBACTION_INDEX_PER_ACTION[clickedActionId]; i <
-                        END_SUBACTION_INDEX_PER_ACTION[clickedActionId]; i++) {
-                    EXPECTED_CLICKED_SEQUENCE.add(REGULAR_ACTIONS_INDEX[clickedActionId]);
-                    for (int j = BEGIN_SUBACTION_INDEX_PER_ACTION[clickedActionId]; j <= i; j++) {
-                        EXPECTED_FOCUSED_SEQUENCE.add(j);
+            if (NUM_SUBACTIONS_PER_ACTION[i] > 0) {
+                for (int j = BEGIN_SUBACTION_INDEX_PER_ACTION[i];
+                        j < END_SUBACTION_INDEX_PER_ACTION[i]; j++) {
+                    expectedClickedSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]);
+                    for (int k = BEGIN_SUBACTION_INDEX_PER_ACTION[i]; k <= j; k++) {
+                        expectedFocusSeqForEachAction.add(k);
                     }
-                    EXPECTED_CLICKED_SEQUENCE.add(i);
-                    EXPECTED_FOCUSED_SEQUENCE.add(REGULAR_ACTIONS_INDEX[clickedActionId]);
+                    expectedClickedSeqForEachAction.add(j);
+                    expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]);
                 }
             } else {
-                EXPECTED_CLICKED_SEQUENCE.add(REGULAR_ACTIONS_INDEX[clickedActionId]);
-                EXPECTED_FOCUSED_SEQUENCE.add(REGULAR_ACTIONS_INDEX[clickedActionId]);
+                expectedClickedSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]);
+                expectedFocusSeqForEachAction.add(REGULAR_ACTIONS_INDEX[i]);
             }
+            result.expectedFocusedSeq.add(expectedFocusSeqForEachAction);
+            result.expectedClickedSeq.add(expectedClickedSeqForEachAction);
         }
 
         String title = "Guided SubActions Test";
@@ -516,7 +764,7 @@
 
         lastIndex = 0;
         for (int i = 0; i < NUM_REGULAR_ACTIONS; i++ ) {
-            GuidedAction action = new GuidedAction.Builder(mInstrumentation.getContext())
+            GuidedAction action = new GuidedAction.Builder(mContext)
                     .id(lastIndex++)
                     .title(res.getString(R.string.dropdown_action_title, i))
                     .description(res.getString(R.string.dropdown_action_desc, i))
@@ -525,7 +773,7 @@
                 List<GuidedAction> subActions = new ArrayList<>();
                 action.setSubActions(subActions);
                 for(int j = 0; j < NUM_SUBACTIONS_PER_ACTION[i]; j++) {
-                    subActions.add(new GuidedAction.Builder(mInstrumentation.getContext())
+                    subActions.add(new GuidedAction.Builder(mContext)
                             .id(lastIndex++)
                             .title(res.getString(R.string.subaction_title, j))
                             .description("")
@@ -535,90 +783,13 @@
             }
             actionList.add(action);
         }
+        result.actionList = actionList;
 
         GuidedStepAttributesTestFragment.clear();
         GuidedStepAttributesTestFragment.GUIDANCE = guidance;
         GuidedStepAttributesTestFragment.ACTION_LIST = actionList;
 
         initActivity(intent);
-
-        final GuidedStepFragment mFragment = (GuidedStepFragment) mActivity.
-                getGuidedStepTestFragment();
-
-        int focusStep = 0, clickStep = 0;
-        for(int i = 0; i < ACTION_CLICK_SEQUENCE.size(); i++) {
-            GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID =
-                    GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID = -1;
-            final int id = ACTION_CLICK_SEQUENCE.get(i);
-            final GuidedAction selectedAction = actionList.get(id);
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    mFragment.setSelectedActionPosition(id);
-                }
-            });
-            Thread.sleep(TRANSITION_LENGTH);
-
-            assertTrue(mInstrumentation.getContext().getResources().getString(
-                    R.string.subaction_test_wrong_focus_error_message),
-                    GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                            EXPECTED_FOCUSED_SEQUENCE.get(focusStep++)
-            );
-
-            if (selectedAction.hasSubActions()) {
-                // Following for loop clicks on a specific action and scrolls & clicks through
-                // all its subactions
-                for (int j = 0; j < selectedAction.getSubActions().size(); j++) {
-                    sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-                    Thread.sleep(TRANSITION_LENGTH);
-                    assertTrue(mInstrumentation.getContext().getResources().getString(
-                            R.string.subaction_test_wrong_focus_error_message),
-                            GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                                    EXPECTED_FOCUSED_SEQUENCE.get(focusStep++)
-                    );
-                    assertTrue(mInstrumentation.getContext().getResources().getString(
-                            R.string.subaction_test_wrong_click_error_message),
-                            GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID ==
-                                    EXPECTED_CLICKED_SEQUENCE.get(clickStep++)
-                    );
-                    for (int k = 0; k < j; k++) {
-                        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
-                        Thread.sleep(TRANSITION_LENGTH);
-                        assertTrue(mInstrumentation.getContext().getResources().getString(
-                                R.string.subaction_test_wrong_focus_error_message),
-                                GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                                        EXPECTED_FOCUSED_SEQUENCE.get(focusStep++)
-                        );
-                    }
-                    sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-                    Thread.sleep(TRANSITION_LENGTH);
-
-                    assertTrue(mInstrumentation.getContext().getResources().getString(
-                            R.string.subaction_test_wrong_focus_error_message),
-                            GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                                    EXPECTED_FOCUSED_SEQUENCE.get(focusStep++)
-                    );
-                    assertTrue(mInstrumentation.getContext().getResources().getString(
-                            R.string.subaction_test_wrong_click_error_message),
-                            GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID ==
-                                    EXPECTED_CLICKED_SEQUENCE.get(clickStep++)
-                    );
-                }
-            } else {
-                sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-                Thread.sleep(TRANSITION_LENGTH);
-                assertTrue(mInstrumentation.getContext().getResources().getString(
-                        R.string.subaction_test_wrong_focus_error_message),
-                        GuidedStepAttributesTestFragment.LAST_SELECTED_ACTION_ID ==
-                                EXPECTED_FOCUSED_SEQUENCE.get(focusStep++)
-                );
-                assertTrue(mInstrumentation.getContext().getResources().getString(
-                        R.string.subaction_test_wrong_click_error_message),
-                        GuidedStepAttributesTestFragment.LAST_CLICKED_ACTION_ID ==
-                                EXPECTED_CLICKED_SEQUENCE.get(clickStep++)
-                );
-            }
-        }
-
+        return result;
     }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestActivity.java
index a0433cc..57a7e51 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestActivity.java
@@ -16,7 +16,6 @@
 
 import android.app.Activity;
 import android.app.Fragment;
-import android.content.Intent;
 import android.os.Bundle;
 import android.support.v17.leanback.app.GuidedStepFragment;
 
@@ -31,8 +30,6 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        Intent intent = getIntent();
-
         mGuidedStepAttributesTestFragment = new GuidedStepAttributesTestFragment();
         GuidedStepFragment.addAsRoot(this, mGuidedStepAttributesTestFragment, android.R.id.content);
     }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java b/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java
new file mode 100644
index 0000000..52995e1
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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.v17.leanback.graphics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test for {@link CompositeDrawableTest}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CompositeDrawableTest {
+
+    private static final int HEIGHT = 800;
+    private static final int WIDTH = 600;
+    Bitmap mBitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
+
+    @Test
+    public void updateBounds_noBoundsRule() {
+        CompositeDrawable parentDrawable = new CompositeDrawable();
+        FitWidthBitmapDrawable drawable = new FitWidthBitmapDrawable();
+        drawable.setBitmap(mBitmap);
+        Rect bounds = new Rect(0, 0, WIDTH, HEIGHT);
+        parentDrawable.addChildDrawable(drawable);
+        parentDrawable.updateBounds(bounds);
+
+        Rect adjustedBounds = drawable.getBounds();
+        assertEquals(bounds, adjustedBounds);
+    }
+
+    @Test
+    public void updateBounds_withBoundsRule() {
+        CompositeDrawable parentDrawable = new CompositeDrawable();
+        float fraction = 0.5f;
+        FitWidthBitmapDrawable drawable = new FitWidthBitmapDrawable();
+        drawable.setBitmap(mBitmap);
+        Rect bounds = new Rect(0, 0, WIDTH, HEIGHT);
+        assertEquals(HEIGHT, bounds.height());
+        assertEquals(WIDTH, bounds.width());
+
+        // inherit from parent
+        parentDrawable.addChildDrawable(drawable);
+        parentDrawable.getChildAt(0).getBoundsRule().mBottom = BoundsRule.inheritFromParent(
+                fraction);
+        parentDrawable.updateBounds(bounds);
+
+        Rect adjustedBounds = drawable.getBounds();
+        Rect expectedBounds = new Rect(bounds);
+        expectedBounds.bottom = bounds.top + (int) (HEIGHT * fraction);
+        assertEquals(expectedBounds, adjustedBounds);
+
+        // absolute value
+        drawable.setBounds(bounds);
+        parentDrawable.getChildAt(0).getBoundsRule().mBottom = BoundsRule.absoluteValue(200);
+        parentDrawable.updateBounds(bounds);
+
+        adjustedBounds = drawable.getBounds();
+        expectedBounds = new Rect(bounds);
+        expectedBounds.bottom = 200;
+        assertEquals(expectedBounds, adjustedBounds);
+
+        // inherit with offset
+        parentDrawable.getChildAt(0).getBoundsRule().mBottom =
+                BoundsRule.inheritFromParentWithOffset(fraction, 100);
+        parentDrawable.updateBounds(bounds);
+
+        adjustedBounds = drawable.getBounds();
+        expectedBounds = new Rect(bounds);
+        expectedBounds.bottom = bounds.top + (int) (HEIGHT * fraction + 100);
+        assertEquals(expectedBounds, adjustedBounds);
+
+        // inherit from parent 2
+        bounds = new Rect(100, 200, WIDTH, HEIGHT);
+        parentDrawable.getChildAt(0).getBoundsRule().mBottom =
+                BoundsRule.inheritFromParent(fraction);
+        parentDrawable.updateBounds(bounds);
+
+        adjustedBounds = drawable.getBounds();
+        expectedBounds = new Rect(bounds);
+        expectedBounds.bottom = bounds.top + (int) ((HEIGHT - 200) * fraction);
+        assertEquals(expectedBounds, adjustedBounds);
+    }
+
+    @Test
+    public void updateBounds_withOverride() {
+        CompositeDrawable parentDrawable = new CompositeDrawable();
+        float fraction = 0.5f;
+        FitWidthBitmapDrawable drawable = new FitWidthBitmapDrawable();
+        drawable.setBitmap(mBitmap);
+        Rect bounds = new Rect(0, 0, WIDTH, HEIGHT);
+        drawable.setBounds(bounds);
+        assertEquals(HEIGHT, drawable.getBounds().height());
+        assertEquals(WIDTH, drawable.getBounds().width());
+
+        parentDrawable.addChildDrawable(drawable);
+
+        // inherit from parent
+        BoundsRule boundsRule = parentDrawable.getChildAt(0).getBoundsRule();
+        boundsRule.mTop = BoundsRule.absoluteValue(-200);
+        boundsRule.mBottom = BoundsRule.inheritFromParent(fraction);
+        parentDrawable.getChildAt(0).getBoundsRule().mTop.setAbsoluteValue(-100);
+
+        parentDrawable.updateBounds(bounds);
+
+        Rect adjustedBounds = drawable.getBounds();
+        Rect expectedBounds = new Rect(bounds);
+        expectedBounds.top = -100;
+        expectedBounds.bottom = bounds.top + (int) (HEIGHT * fraction);
+        assertEquals(expectedBounds, adjustedBounds);
+
+        // inherit from parent with offset
+        boundsRule.mBottom = BoundsRule.absoluteValue(HEIGHT);
+
+        parentDrawable.updateBounds(bounds);
+
+        adjustedBounds = drawable.getBounds();
+        expectedBounds = new Rect(bounds);
+        expectedBounds.top = -100;
+        expectedBounds.bottom = HEIGHT;
+        assertEquals(expectedBounds, adjustedBounds);
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
+    @Test
+    public void constantState() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        CompositeDrawable parentDrawable = new CompositeDrawable();
+        BitmapDrawable childDrawable = new BitmapDrawable(context.getResources(), mBitmap);
+        parentDrawable.addChildDrawable(childDrawable);
+
+        // getConstantState().newDrawable() will create a new CompositeDrawable with shared states:
+        CompositeDrawable parentDrawble2 = (CompositeDrawable)
+                parentDrawable.getConstantState().newDrawable();
+        BitmapDrawable childDrawable2 = (BitmapDrawable) parentDrawble2.getChildAt(0).getDrawable();
+        parentDrawable.setAlpha(128);
+        assertEquals(128, parentDrawble2.getAlpha());
+        assertEquals(128, childDrawable2.getAlpha());
+
+        // after mutate(), parentDrawble2 will have its own state
+        parentDrawble2.mutate();
+        childDrawable2 = (BitmapDrawable) parentDrawble2.getChildAt(0).getDrawable();
+        parentDrawable.setAlpha(64);
+        assertEquals(64, parentDrawable.getAlpha());
+        assertEquals(64, childDrawable.getAlpha());
+        assertEquals(128, parentDrawble2.getAlpha());
+        assertEquals(128, childDrawable2.getAlpha());
+        childDrawable.setAlpha(100);
+        assertEquals(128, parentDrawble2.getAlpha());
+        assertEquals(128, childDrawable2.getAlpha());
+    }
+
+    @Test
+    public void copyChildDrawableTest() {
+        double delta = .005f;
+        CompositeDrawable parent = new CompositeDrawable();
+        FitWidthBitmapDrawable child = new FitWidthBitmapDrawable();
+        parent.addChildDrawable(child);
+        parent.getChildAt(0).getBoundsRule().mBottom =
+                BoundsRule.inheritFromParentWithOffset(.5f, 100);
+
+        CompositeDrawable.ChildDrawable newChild = new CompositeDrawable.ChildDrawable(
+                parent.getChildAt(0),
+                parent,
+                null);
+        assertEquals(100, newChild.getBoundsRule().mBottom.getAbsoluteValue());
+        assertEquals(.5f, newChild.getBoundsRule().mBottom.getFraction(), delta);
+    }
+
+    @Test
+    public void mutateTest() {
+        double delta = .005f;
+        CompositeDrawable parent = new CompositeDrawable();
+        FitWidthBitmapDrawable child = new FitWidthBitmapDrawable();
+        parent.addChildDrawable(child);
+        parent.getChildAt(0).getBoundsRule().mBottom =
+                BoundsRule.inheritFromParentWithOffset(.5f, 100);
+
+        CompositeDrawable newDrawable = (CompositeDrawable) parent.getConstantState().newDrawable();
+
+        parent.mutate();
+        assertTrue(parent.mMutated);
+
+        CompositeDrawable.ChildDrawable newChild = newDrawable.getChildAt(0);
+        assertNotSame(parent.getChildAt(0), newChild);
+        assertEquals(parent.getChildAt(0).getBoundsRule().mBottom.getAbsoluteValue(),
+                newChild.getBoundsRule().mBottom.getAbsoluteValue());
+        assertEquals(parent.getChildAt(0).getBoundsRule().mBottom.getFraction(),
+                newChild.getBoundsRule().mBottom.getFraction(), delta);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java b/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java
new file mode 100644
index 0000000..a91c221
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/graphics/FitWidthBitmapDrawableTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.v17.leanback.graphics;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Build;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Unit test for {@link FitWidthBitmapDrawable}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FitWidthBitmapDrawableTest {
+    private final static int SCREEN_WIDTH = 1600;
+    private final static int SCREEN_HEIGHT = 1080;
+    private final static int WIDTH = 300;
+    private final static int HEIGHT = 600;
+    private Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
+
+    @Test
+    public void draw_withOffset() {
+        int offset = 600;
+        FitWidthBitmapDrawable drawable = new FitWidthBitmapDrawable();
+        drawable.setBitmap(bitmap);
+        drawable.setVerticalOffset(offset);
+        Rect bounds = new Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+        drawable.setBounds(bounds);
+
+        Canvas canvas = Mockito.mock(Canvas.class);
+        drawable.draw(canvas);
+
+        Rect expectedBounds = bounds;
+        verify(canvas).clipRect(expectedBounds);
+
+        Rect bitmapBounds = new Rect(0, 0, WIDTH, HEIGHT);
+        int nH = (int) (((float)SCREEN_WIDTH/WIDTH * HEIGHT) + offset);
+        Rect expectedDest = new Rect(0, offset, SCREEN_WIDTH, nH);
+        verify(canvas).drawBitmap(eq(bitmap), eq(bitmapBounds), eq(expectedDest), any(Paint.class));
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
+    @Test
+    public void constantState() {
+        FitWidthBitmapDrawable drawable = new FitWidthBitmapDrawable();
+        drawable.setBitmap(bitmap);
+        drawable.setVerticalOffset(600);
+
+        // getConstantState().newDrawable() will create a new drawable with shared states:
+        FitWidthBitmapDrawable drawable2 = (FitWidthBitmapDrawable)
+                drawable.getConstantState().newDrawable();
+        drawable.setAlpha(128);
+        assertEquals(128, drawable2.getAlpha());
+
+        // after mutate(), drawable2 will have its own state
+        drawable2.mutate();
+        drawable.setAlpha(64);
+        assertEquals(64, drawable.getAlpha());
+        assertEquals(128, drawable2.getAlpha());
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
new file mode 100644
index 0000000..2f2fc5d
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.testutils;
+
+import android.app.Activity;
+import android.view.View;
+
+import java.util.concurrent.Callable;
+
+import junit.framework.Assert;
+
+public abstract class PollingCheck {
+
+    private static final long TIME_SLICE = 250;
+    private long mTimeout = 5000;
+
+    public abstract static class PollingCheckCondition {
+        public abstract boolean canProceed();
+
+        public boolean canPreProceed() {
+            return canProceed();
+        }
+    }
+
+    public PollingCheck() {
+    }
+
+    public PollingCheck(long timeout) {
+        mTimeout = timeout;
+    }
+
+    protected abstract boolean check();
+
+    protected boolean preCheck() {
+        return check();
+    }
+
+    public void run() {
+        if (preCheck()) {
+            return;
+        }
+
+        long timeout = mTimeout;
+        while (timeout > 0) {
+            try {
+                Thread.sleep(TIME_SLICE);
+            } catch (InterruptedException e) {
+                Assert.fail("unexpected InterruptedException");
+            }
+
+            if (check()) {
+                return;
+            }
+
+            timeout -= TIME_SLICE;
+        }
+
+        Assert.fail("unexpected timeout");
+    }
+
+    public static void waitFor(final PollingCheckCondition condition) {
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+
+            @Override
+            protected boolean preCheck() {
+                return condition.canPreProceed();
+            }
+        }.run();
+    }
+
+    public static void waitFor(long timeout, final PollingCheckCondition condition) {
+        new PollingCheck(timeout) {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+        }.run();
+    }
+
+    public static class ViewScreenPositionDetector {
+
+        int[] lastLocation = null;
+        int[] newLocation = new int[2];
+
+        public boolean isViewStableOnScreen(View view) {
+            if (lastLocation == null) {
+                // get initial location
+                lastLocation = new int[2];
+                view.getLocationInWindow(lastLocation);
+            } else {
+                // get new location and compare to old location
+                view.getLocationInWindow(newLocation);
+                if (newLocation[0] == lastLocation[0]
+                        && newLocation[1] == lastLocation[1]) {
+                    // location stable,  animation finished
+                    return true;
+                }
+                lastLocation[0] = newLocation[0];
+                lastLocation[1] = newLocation[1];
+            }
+            return false;
+        }
+    }
+
+    public static class ViewStableOnScreen extends PollingCheckCondition {
+
+        View mView;
+        ViewScreenPositionDetector mDector = new ViewScreenPositionDetector();
+
+        public ViewStableOnScreen(View view) {
+            mView = view;
+        }
+
+        @Override
+        public boolean canPreProceed() {
+            return false;
+        }
+
+        @Override
+        public boolean canProceed() {
+            return mDector.isViewStableOnScreen(mView);
+        }
+
+    }
+
+    public static class ActivityDestroy extends PollingCheckCondition {
+
+        Activity mActivity;
+
+        public ActivityDestroy(Activity activity) {
+            mActivity = activity;
+        }
+
+        @Override
+        public boolean canProceed() {
+            return mActivity.isDestroyed();
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/BaseCardViewTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/BaseCardViewTest.java
new file mode 100644
index 0000000..1952418
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/BaseCardViewTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static android.support.v17.leanback.widget.BaseCardView.LayoutParams.MATCH_PARENT;
+import static android.support.v17.leanback.widget.BaseCardView.LayoutParams.VIEW_TYPE_INFO;
+import static android.support.v17.leanback.widget.BaseCardView.LayoutParams.VIEW_TYPE_MAIN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class BaseCardViewTest {
+
+    BaseCardView.LayoutParams createLayoutParams(int viewType, int width, int height) {
+        BaseCardView.LayoutParams lp = new BaseCardView.LayoutParams(width, height);
+        lp.viewType = viewType;
+        return lp;
+    }
+
+    void mockInfoHeightAnimation(BaseCardView view, int width, int startHeight, int endHeight) {
+        ((BaseCardView.InfoHeightAnimation) view.getAnimation()).mockStart();
+        measureAndLayout(view, width, startHeight);
+        ((BaseCardView.InfoHeightAnimation) view.getAnimation()).mockEnd();
+        assertNull(view.getAnimation());
+        measureAndLayout(view, width, endHeight);
+    }
+
+    void mockInfoAlphaAnimation(BaseCardView view, View infoView,
+            float startAlpha, float endAlpha) {
+        ((BaseCardView.InfoAlphaAnimation) view.getAnimation()).mockStart();
+        assertEquals(startAlpha, infoView.getAlpha(), 0.01f);
+        assertEquals(View.VISIBLE, infoView.getVisibility());
+        ((BaseCardView.InfoAlphaAnimation) view.getAnimation()).mockEnd();
+        assertNull(view.getAnimation());
+        assertEquals(endAlpha, infoView.getAlpha(), 0.01f);
+        assertEquals(endAlpha == 0f? View.GONE: View.VISIBLE, infoView.getVisibility());
+    }
+
+    void measureAndLayout(View view, int expectedWidth, int expectedHeight) {
+        view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        assertEquals(expectedWidth, view.getMeasuredWidth());
+        assertEquals(expectedHeight, view.getMeasuredHeight());
+        view.layout(0, 0, expectedWidth, expectedHeight);
+    }
+
+    void verifyLayoutTimes(View.OnLayoutChangeListener listener, int timesCalled) {
+        verify(listener, times(timesCalled)).onLayoutChange(any(View.class),
+                any(Integer.class), any(Integer.class), any(Integer.class), any(Integer.class),
+                any(Integer.class), any(Integer.class), any(Integer.class), any(Integer.class));
+    }
+
+    @Test
+    public void infoOver_InfoVisibleAlways() {
+        BaseCardView cardView = new BaseCardView(InstrumentationRegistry.getContext());
+        View main = new View(cardView.getContext());
+        main.setLayoutParams(createLayoutParams(VIEW_TYPE_MAIN, 500, 500));
+        cardView.addView(main);
+        View info = new View(cardView.getContext());
+        View.OnLayoutChangeListener onLayout = Mockito.mock(View.OnLayoutChangeListener.class);
+        info.addOnLayoutChangeListener(onLayout);
+        info.setLayoutParams(createLayoutParams(VIEW_TYPE_INFO, MATCH_PARENT, 200));
+        cardView.addView(info);
+        cardView.setCardType(BaseCardView.CARD_TYPE_INFO_OVER);
+        cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS);
+
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(true);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(false);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setSelected(true);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setSelected(false);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+    }
+
+    @Test
+    public void infoUnder_InfoVisibleAlways() {
+        BaseCardView cardView = new BaseCardView(InstrumentationRegistry.getContext());
+        View main = new View(cardView.getContext());
+        main.setLayoutParams(createLayoutParams(VIEW_TYPE_MAIN, 500, 500));
+        cardView.addView(main);
+        View info = new View(cardView.getContext());
+        View.OnLayoutChangeListener onLayout = Mockito.mock(View.OnLayoutChangeListener.class);
+        info.addOnLayoutChangeListener(onLayout);
+        info.setLayoutParams(createLayoutParams(VIEW_TYPE_INFO, MATCH_PARENT, 200));
+        cardView.addView(info);
+        cardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
+        cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS);
+
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(true);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(false);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setSelected(true);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setSelected(false);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+    }
+
+    @Test
+    public void infoUnder_InfoVisibleActivated() {
+        BaseCardView cardView = new BaseCardView(InstrumentationRegistry.getContext());
+        View main = new View(cardView.getContext());
+        main.setLayoutParams(createLayoutParams(VIEW_TYPE_MAIN, 500, 500));
+        cardView.addView(main);
+        View info = new View(cardView.getContext());
+        View.OnLayoutChangeListener onLayout = Mockito.mock(View.OnLayoutChangeListener.class);
+        info.addOnLayoutChangeListener(onLayout);
+        info.setLayoutParams(createLayoutParams(VIEW_TYPE_INFO, MATCH_PARENT, 200));
+        cardView.addView(info);
+        cardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
+        cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ACTIVATED);
+
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 0);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(true);
+        assertTrue(cardView.isLayoutRequested());
+        measureAndLayout(cardView, 500, 700);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(false);
+        assertTrue(cardView.isLayoutRequested());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        // changing selected does not affect size
+        cardView.setSelected(true);
+        assertFalse(cardView.isLayoutRequested());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        // changing selected does not affect size
+        cardView.setSelected(false);
+        assertFalse(cardView.isLayoutRequested());
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 1);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+    }
+
+    @Test
+    public void infoUnder_InfoVisibleSelected() {
+        final BaseCardView cardView = new BaseCardView(InstrumentationRegistry.getContext());
+        View main = new View(cardView.getContext());
+        main.setLayoutParams(createLayoutParams(VIEW_TYPE_MAIN, 500, 500));
+        cardView.addView(main);
+        View info = new View(cardView.getContext());
+        View.OnLayoutChangeListener onLayout = Mockito.mock(View.OnLayoutChangeListener.class);
+        info.addOnLayoutChangeListener(onLayout);
+        info.setLayoutParams(createLayoutParams(VIEW_TYPE_INFO, MATCH_PARENT, 200));
+        cardView.addView(info);
+        cardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
+        cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_SELECTED);
+
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 0);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        // changing activated does not affect size
+        cardView.setActivated(true);
+        measureAndLayout(cardView, 500, 500);
+        assertNull(cardView.getAnimation());
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        // start info height animation 500 -> 700
+        cardView.setSelected(true);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+        mockInfoHeightAnimation(cardView, 500 /*width*/, 500 /*startHeight*/, 700 /*endHeight*/);
+
+        // changing activated does not affect size
+        cardView.setActivated(false);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+        measureAndLayout(cardView, 500, 700);
+        assertNull(cardView.getAnimation());
+
+        // start info height animation 700 -> 500
+        cardView.setSelected(false);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+        mockInfoHeightAnimation(cardView, 500 /*width*/, 700 /*startHeight*/, 500 /*endHeight*/);
+    }
+
+    @Test
+    public void infoOver_InfoVisibleSelected() {
+        final BaseCardView cardView = new BaseCardView(InstrumentationRegistry.getContext());
+        View main = new View(cardView.getContext());
+        main.setLayoutParams(createLayoutParams(VIEW_TYPE_MAIN, 500, 500));
+        cardView.addView(main);
+        View info = new View(cardView.getContext());
+        View.OnLayoutChangeListener onLayout = Mockito.mock(View.OnLayoutChangeListener.class);
+        info.addOnLayoutChangeListener(onLayout);
+        info.setLayoutParams(createLayoutParams(VIEW_TYPE_INFO, MATCH_PARENT, 200));
+        cardView.addView(info);
+        cardView.setCardType(BaseCardView.CARD_TYPE_INFO_OVER);
+        cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_SELECTED);
+
+        measureAndLayout(cardView, 500, 500);
+        verifyLayoutTimes(onLayout, 0);
+        assertFalse(cardView.isSelected());
+        assertFalse(cardView.isActivated());
+        assertEquals(0f, info.getAlpha(), 0.01f);
+
+        cardView.setActivated(true);
+        assertFalse(cardView.isLayoutRequested());
+        assertNull(cardView.getAnimation());
+
+        assertEquals(info.getVisibility(), View.GONE);
+        assertEquals(0f, info.getAlpha(), 0.01f);
+        // start info animation alpha 0f -> 1f
+        cardView.setSelected(true);
+        // change visibility when start animation causing layout requested
+        assertTrue(cardView.isLayoutRequested());
+        measureAndLayout(cardView, 500, 500);
+        mockInfoAlphaAnimation(cardView, info, 0f, 1f);
+        assertEquals(1f, info.getAlpha(), 0.01f);
+
+        // start info animation alpha 1f -> 0f
+        cardView.setSelected(false);
+        assertFalse(cardView.isLayoutRequested());
+        mockInfoAlphaAnimation(cardView, info, 1f, 0f);
+        assertEquals(0f, info.getAlpha(), 0.01f);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
index d4c2e89..6678101 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
@@ -16,21 +16,16 @@
 
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.test.R;
-import android.support.v7.widget.RecyclerView;
-import android.support.v17.leanback.widget.BaseGridView;
-import android.support.v17.leanback.widget.OnChildSelectedListener;
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import java.util.ArrayList;
@@ -310,8 +305,8 @@
 
         @Override
         public FacetProvider getFacetProvider(int viewType) {
-            final Object alignmentFacet = mAlignmentViewTypeProvider != null?
-                mAlignmentViewTypeProvider.getItemAlignmentFacet(viewType) : null;
+            final Object alignmentFacet = mAlignmentViewTypeProvider != null
+                    ? mAlignmentViewTypeProvider.getItemAlignmentFacet(viewType) : null;
             if (alignmentFacet != null) {
                 return new FacetProvider() {
                     @Override
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
index 4577963..6f5529e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
@@ -15,8 +15,6 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.test.AndroidTestCase;
-
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
index 4061c66..8a38894 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -15,19 +15,34 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.app.Instrumentation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
 import android.content.Intent;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
 import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.test.R;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerViewAccessibilityDelegate;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.text.Selection;
 import android.text.Spannable;
 import android.util.SparseArray;
@@ -37,23 +52,34 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
 
 @MediumTest
-public class GridWidgetTest extends ActivityInstrumentationTestCase2<GridActivity> {
+@RunWith(AndroidJUnit4.class)
+public class GridWidgetTest {
 
     private static final boolean HUMAN_DELAY = false;
     private static final long WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS = 60000;
+    private static final int WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS = 2000;
+    private static final int WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS = 2000;
 
+    protected ActivityTestRule<GridActivity> mActivityTestRule;
     protected GridActivity mActivity;
-    protected Instrumentation mInstrumentation;
     protected BaseGridView mGridView;
     protected GridLayoutManager mLayoutManager;
+    private GridLayoutManager.OnLayoutCompleteListener mWaitLayoutListener;
     protected int mOrientation;
     protected int mNumRows;
+    protected int[] mRemovedItems;
 
     private final Comparator<View> mRowSortComparator = new Comparator<View>() {
         public int compare(View lhs, View rhs) {
@@ -75,8 +101,16 @@
         }
     };
 
-    public GridWidgetTest() {
-        super("android.support.v17.leanback.test", GridActivity.class);
+    @Rule public TestName testName = new TestName();
+
+    public static void sendKey(int keyCode) {
+        InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+    }
+
+    public static void sendRepeatedKeys(int repeats, int keyCode) {
+        for (int i = 0; i < repeats; i++) {
+            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keyCode);
+        }
     }
 
     private void humanDelay(int delay) throws InterruptedException {
@@ -86,19 +120,27 @@
      * Change size of the Adapter and notifyDataSetChanged.
      */
     private void changeArraySize(final int size) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mActivity.changeArraySize(size);
             }
         });
-        Thread.sleep(500);
+    }
+
+    static String dumpGridView(BaseGridView gridView) {
+        return "findFocus:" + gridView.getRootView().findFocus()
+                + " isLayoutRequested:" + gridView.isLayoutRequested()
+                + " selectedPosition:" + gridView.getSelectedPosition()
+                + " adapter.itemCount:" + gridView.getAdapter().getItemCount()
+                + " itemAnimator.isRunning:" + gridView.getItemAnimator().isRunning()
+                + " scrollState:" + gridView.getScrollState();
     }
 
     /**
      * Change selected position.
      */
     private void setSelectedPosition(final int position, final int scrollExtra) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPosition(position, scrollExtra);
             }
@@ -106,75 +148,13 @@
         Thread.sleep(500);
     }
 
-    protected void waitForScrollIdleAndItemAnimation(Runnable verify) throws Throwable {
-        waitForScrollIdle();
-        waitForItemAnimation();
-        verify.run();
-    }
-
-    protected void waitForItemAnimation() throws Throwable {
-        Thread.sleep(100);
-        while (mGridView.getItemAnimator() != null && mGridView.getItemAnimator().isRunning()) {
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException ex) {
-                break;
-            }
-        }
-    }
-
-    /**
-     * Wait for grid view stop scroll and optionally verify state of grid view.
-     */
-    protected void waitForScrollIdle(Runnable verify) throws Throwable {
-        Thread.sleep(100);
-        int total = 0;
-        while (mGridView.getLayoutManager().isSmoothScrolling() ||
-                mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
-            if ((total += 100) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) {
-                throw new RuntimeException("waitForScrollIdle Timeout");
-            }
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException ex) {
-                break;
-            }
-            if (verify != null) {
-                runTestOnUiThread(verify);
-            }
-        }
-    }
-
-    /**
-     * Wait for grid view stop animation and optionally verify state of grid view.
-     */
-    protected void waitForTransientStateGone(Runnable verify) throws Throwable {
-        do {
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException ex) {
-                break;
-            }
-            if (verify != null) {
-                runTestOnUiThread(verify);
-            }
-        } while (mGridView.hasTransientState());
-    }
-
-    /**
-     * Wait for grid view stop scroll.
-     */
-    protected void waitForScrollIdle() throws Throwable {
-        waitForScrollIdle(null);
-    }
-
     /**
      * Scrolls using given key.
      */
     protected void scroll(int key, Runnable verify) throws Throwable {
         do {
             if (verify != null) {
-                runTestOnUiThread(verify);
+                mActivityTestRule.runOnUiThread(verify);
             }
             sendRepeatedKeys(10, key);
             try {
@@ -182,12 +162,23 @@
             } catch (InterruptedException ex) {
                 break;
             }
-        } while (mGridView.getLayoutManager().isSmoothScrolling() ||
-                mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE);
+        } while (mGridView.getLayoutManager().isSmoothScrolling()
+                || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE);
     }
 
     protected void scrollToBegin(Runnable verify) throws Throwable {
         int key;
+        // first move to first column/row
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            key = KeyEvent.KEYCODE_DPAD_UP;
+        } else {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                key = KeyEvent.KEYCODE_DPAD_RIGHT;
+            } else {
+                key = KeyEvent.KEYCODE_DPAD_LEFT;
+            }
+        }
+        scroll(key, null);
         if (mOrientation == BaseGridView.HORIZONTAL) {
             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
                 key = KeyEvent.KEYCODE_DPAD_RIGHT;
@@ -202,6 +193,17 @@
 
     protected void scrollToEnd(Runnable verify) throws Throwable {
         int key;
+        // first move to first column/row
+        if (mOrientation == BaseGridView.HORIZONTAL) {
+            key = KeyEvent.KEYCODE_DPAD_UP;
+        } else {
+            if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
+                key = KeyEvent.KEYCODE_DPAD_RIGHT;
+            } else {
+                key = KeyEvent.KEYCODE_DPAD_LEFT;
+            }
+        }
+        scroll(key, null);
         if (mOrientation == BaseGridView.HORIZONTAL) {
             if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
                 key = KeyEvent.KEYCODE_DPAD_LEFT;
@@ -226,8 +228,8 @@
             if (mOrientation == BaseGridView.HORIZONTAL) {
                 rowLocation = v.getTop();
             } else {
-                rowLocation = mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL ?
-                    v.getRight() : v.getLeft();
+                rowLocation = mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL
+                        ? v.getRight() : v.getLeft();
             }
             ArrayList<View> views = rows.get(rowLocation);
             if (views == null) {
@@ -350,26 +352,114 @@
         return (v.getLeft() + v.getRight())/2;
     }
 
-    private void initActivity(Intent intent) {
-        setActivityIntent(intent);
-        mActivity = getActivity();
-        final String testName = getName();
-        try {
-            runTestOnUiThread(new Runnable() {
+    private void initActivity(Intent intent) throws Throwable {
+        mActivityTestRule = new ActivityTestRule<GridActivity>(GridActivity.class, false, false);
+        mActivity = mActivityTestRule.launchActivity(intent);
+        mActivityTestRule.runOnUiThread(new Runnable() {
                 public void run() {
-                    mActivity.setTitle(testName);
+                    mActivity.setTitle(testName.getMethodName());
                 }
             });
-            Thread.sleep(1000);
-        } catch (Throwable t) {
-            t.printStackTrace();
-        }
+        Thread.sleep(1000);
         mGridView = mActivity.mGridView;
+        mLayoutManager = (GridLayoutManager) mGridView.getLayoutManager();
     }
 
+    @After
+    public void clearTest() {
+        mWaitLayoutListener = null;
+        mLayoutManager = null;
+        mGridView = null;
+        mActivity = null;
+        mActivityTestRule = null;
+    }
+
+    /**
+     * Must be called before waitForLayout() to prepare layout listener.
+     */
+    protected void startWaitLayout() {
+        if (mWaitLayoutListener != null) {
+            throw new IllegalStateException("startWaitLayout() already called");
+        }
+        if (mLayoutManager.mLayoutCompleteListener != null) {
+            throw new IllegalStateException("Cannot startWaitLayout()");
+        }
+        mWaitLayoutListener = mLayoutManager.mLayoutCompleteListener =
+                mock(GridLayoutManager.OnLayoutCompleteListener.class);
+    }
+
+    /**
+     * wait layout to be called and remove the listener.
+     */
+    protected void waitForLayout() {
+        if (mWaitLayoutListener == null) {
+            throw new IllegalStateException("startWaitLayout() not called");
+        }
+        if (mWaitLayoutListener != mLayoutManager.mLayoutCompleteListener) {
+            throw new IllegalStateException("layout listener inconistent");
+        }
+        try {
+            verify(mWaitLayoutListener, timeout(WAIT_FOR_LAYOUT_PASS_TIMEOUT_MS).atLeastOnce())
+                    .onLayoutCompleted(any(RecyclerView.State.class));
+        } finally {
+            mWaitLayoutListener = null;
+            mLayoutManager.mLayoutCompleteListener = null;
+        }
+    }
+
+    /**
+     * If currently running animator, wait for it to finish, otherwise return immediately.
+     * To wait the ItemAnimator start, you can use waitForLayout() to make sure layout pass has
+     * processed adapter change.
+     */
+    protected void waitForItemAnimation() {
+        RecyclerView.ItemAnimator.ItemAnimatorFinishedListener listener = mock(
+                RecyclerView.ItemAnimator.ItemAnimatorFinishedListener.class);
+        if (mGridView.getItemAnimator().isRunning(listener)) {
+            verify(listener, timeout(WAIT_FOR_ITEM_ANIMATION_FINISH_TIMEOUT_MS).atLeastOnce())
+                    .onAnimationsFinished();
+        }
+    }
+
+    /**
+     * Run task in UI thread and wait for layout and ItemAnimator finishes.
+     */
+    protected void performAndWaitForAnimation(Runnable task) throws Throwable {
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(task);
+        waitForLayout();
+        waitForItemAnimation();
+    }
+
+    protected void waitForScrollIdle() throws Throwable {
+        waitForScrollIdle(null);
+    }
+
+    /**
+     * Wait for grid view stop scroll and optionally verify state of grid view.
+     */
+    protected void waitForScrollIdle(Runnable verify) throws Throwable {
+        Thread.sleep(100);
+        int total = 0;
+        while (mGridView.getLayoutManager().isSmoothScrolling()
+                || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
+            if ((total += 100) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) {
+                throw new RuntimeException("waitForScrollIdle Timeout");
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                break;
+            }
+            if (verify != null) {
+                mActivityTestRule.runOnUiThread(verify);
+            }
+        }
+    }
+
+    @Test
     public void testThreeRowHorizontalBasic() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
         initActivity(intent);
@@ -430,6 +520,7 @@
         }
     }
 
+    @Test
     public void testItemDecorationAndMargins() throws Throwable {
 
         final int leftMargin = 3;
@@ -438,8 +529,7 @@
         final int bottomMargin = 8;
         final int itemHeight = 100;
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight});
         intent.putExtra(GridActivity.EXTRA_LAYOUT_MARGINS,
@@ -456,13 +546,12 @@
         final int decorationRight = 19;
         final int decorationBottom = 2;
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
                         decorationRight, decorationBottom));
             }
         });
-        waitForScrollIdle();
 
         View child0 = mGridView.getChildAt(0);
         View child1 = mGridView.getChildAt(1);
@@ -483,6 +572,8 @@
 
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
     public void testItemDecorationAndMarginsAndOpticalBounds() throws Throwable {
         final int leftMargin = 3;
         final int topMargin = 4;
@@ -491,8 +582,7 @@
         final int itemHeight = 100;
         final int ninePatchDrawableResourceId = R.drawable.lb_card_shadow_focused;
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{itemHeight, itemHeight, itemHeight});
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
@@ -512,7 +602,8 @@
         final int decorationBottom = 2;
 
         final Rect opticalPaddings = new Rect();
-        mGridView.getContext().getDrawable(ninePatchDrawableResourceId).getPadding(opticalPaddings);
+        mGridView.getResources().getDrawable(ninePatchDrawableResourceId)
+                .getPadding(opticalPaddings);
         final int opticalInsetsLeft = opticalPaddings.left;
         final int opticalInsetsTop = opticalPaddings.top;
         final int opticalInsetsRight = opticalPaddings.right;
@@ -522,13 +613,12 @@
         assertTrue(opticalInsetsRight > 0);
         assertTrue(opticalInsetsBottom > 0);
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mGridView.addItemDecoration(new DividerDecoration(decorationLeft, decorationTop,
                         decorationRight, decorationBottom));
             }
         });
-        waitForScrollIdle();
 
         View child0 = mGridView.getChildAt(0);
         View child1 = mGridView.getChildAt(1);
@@ -553,10 +643,10 @@
 
     }
 
+    @Test
     public void testThreeColumnVerticalBasic() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
         initActivity(intent);
@@ -571,9 +661,9 @@
         verifyBeginAligned();
     }
 
+    @Test
     public void testRedundantAppendRemove() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid_testredundantappendremove);
         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
@@ -600,9 +690,9 @@
         verifyBeginAligned();
     }
 
+    @Test
     public void testRedundantAppendRemove2() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid_testredundantappendremove2);
         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
@@ -642,10 +732,9 @@
         verifyEdgesSame(endEdges, endEdges2);
     }
 
+    @Test
     public void testItemMovedHorizontal() throws Throwable {
-
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
@@ -655,20 +744,23 @@
 
         mGridView.setSelectedPositionSmooth(150);
         waitForScrollIdle(mVerifyLayout);
-        mActivity.swap(150, 152);
-        waitForTransientStateGone(null);
-
-        runTestOnUiThread(mVerifyLayout);
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.swap(150, 152);
+            }
+        });
+        mActivityTestRule.runOnUiThread(mVerifyLayout);
 
         scrollToBegin(mVerifyLayout);
 
         verifyBeginAligned();
     }
 
+    @Test
     public void testItemMovedVertical() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
@@ -678,10 +770,13 @@
 
         mGridView.setSelectedPositionSmooth(150);
         waitForScrollIdle(mVerifyLayout);
-        mActivity.swap(150, 152);
-        waitForTransientStateGone(null);
-
-        runTestOnUiThread(mVerifyLayout);
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.swap(150, 152);
+            }
+        });
+        mActivityTestRule.runOnUiThread(mVerifyLayout);
 
         scrollToEnd(mVerifyLayout);
         scrollToBegin(mVerifyLayout);
@@ -689,10 +784,10 @@
         verifyBeginAligned();
     }
 
+    @Test
     public void testItemAddRemoveHorizontal() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
@@ -705,15 +800,23 @@
 
         mGridView.setSelectedPositionSmooth(150);
         waitForScrollIdle(mVerifyLayout);
-        int[] removedItems = mActivity.removeItems(151, 4);
-        waitForTransientStateGone(null);
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mRemovedItems = mActivity.removeItems(151, 4);
+            }
+        });
 
         scrollToEnd(mVerifyLayout);
         mGridView.setSelectedPositionSmooth(150);
         waitForScrollIdle(mVerifyLayout);
 
-        mActivity.addItems(151, removedItems);
-        waitForTransientStateGone(null);
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.addItems(151, mRemovedItems);
+            }
+        });
         scrollToEnd(mVerifyLayout);
 
         // we should get same aligned end edges
@@ -724,10 +827,10 @@
         verifyBeginAligned();
     }
 
+    @Test
     public void testSetSelectedPositionDetached() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
@@ -737,54 +840,52 @@
 
         final int focusToIndex = 49;
         final ViewGroup parent = (ViewGroup) mGridView.getParent();
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 parent.removeView(mGridView);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 parent.addView(mGridView);
                 mGridView.requestFocus();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertEquals(mGridView.getSelectedPosition(), focusToIndex);
         assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex).hasFocus());
 
         final int focusToIndex2 = 0;
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 parent.removeView(mGridView);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPosition(focusToIndex2);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 parent.addView(mGridView);
                 mGridView.requestFocus();
             }
         });
         assertEquals(mGridView.getSelectedPosition(), focusToIndex2);
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertTrue(mGridView.getLayoutManager().findViewByPosition(focusToIndex2).hasFocus());
     }
 
+    @Test
     public void testBug22209986() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
@@ -793,47 +894,44 @@
         mNumRows = 1;
 
         final int focusToIndex = mGridView.getChildCount() - 1;
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        waitForTransientStateGone(null);
         waitForScrollIdle();
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex + 1);
             }
         });
         // let the scroll running for a while and requestLayout during scroll
         Thread.sleep(80);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 assertEquals(mGridView.getScrollState(), BaseGridView.SCROLL_STATE_SETTLING);
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
 
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertEquals(leftEdge,
                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
     }
 
+    @Test
     public void testScrollAndRemove() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
@@ -842,37 +940,36 @@
         mNumRows = 1;
 
         final int focusToIndex = mGridView.getChildCount() - 1;
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 mActivity.removeItems(focusToIndex, 1);
             }
         });
 
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertEquals(leftEdge,
                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
     }
 
+    @Test
     public void testScrollAndInsert() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         int[] items = new int[1000];
@@ -886,7 +983,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(150);
             }
@@ -895,27 +992,25 @@
 
         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
         final int focusToIndex = mGridView.getChildAdapterPosition(view);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 int[] newItems = new int[]{300, 300, 300};
                 mActivity.addItems(0, newItems);
             }
         });
-
-        waitForTransientStateGone(null);
-        waitForScrollIdle();
     }
 
+    @Test
     public void testScrollAndInsertBeforeVisibleItem() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         int[] items = new int[1000];
@@ -929,7 +1024,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(150);
             }
@@ -938,27 +1033,25 @@
 
         View view =  mGridView.getChildAt(mGridView.getChildCount() - 1);
         final int focusToIndex = mGridView.getChildAdapterPosition(view);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
+            @Override
             public void run() {
                 int[] newItems = new int[]{300, 300, 300};
                 mActivity.addItems(focusToIndex, newItems);
             }
         });
-
-        waitForTransientStateGone(null);
-        waitForScrollIdle();
     }
 
+    @Test
     public void testSmoothScrollAndRemove() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
@@ -967,40 +1060,37 @@
         mNumRows = 1;
 
         final int focusToIndex = 40;
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mActivity.removeItems(focusToIndex, 1);
             }
         });
 
-        Thread.sleep(20); // wait for layout
         assertTrue("removing the index of not attached child should not affect smooth scroller",
                 mGridView.getLayoutManager().isSmoothScrolling());
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertEquals(leftEdge,
                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
     }
 
+    @Test
     public void testSmoothScrollAndRemove2() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 50);
@@ -1009,40 +1099,40 @@
         mNumRows = 1;
 
         final int focusToIndex = 40;
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(focusToIndex);
             }
         });
 
-        final int removeIndex = mGridView.getChildCount() - 1;
-        runTestOnUiThread(new Runnable() {
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
+                final int removeIndex = mGridView.getChildViewHolder(
+                        mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition();
                 mActivity.removeItems(removeIndex, 1);
             }
         });
+        waitForLayout();
 
-        Thread.sleep(20); // wait for layout
         assertFalse("removing the index of attached child should kill smooth scroller",
                 mGridView.getLayoutManager().isSmoothScrolling());
-        waitForTransientStateGone(null);
-        waitForScrollIdle();
+        waitForItemAnimation();
         int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
         assertEquals(leftEdge,
                 mGridView.getLayoutManager().findViewByPosition(focusToIndex).getLeft());
     }
 
+    @Test
     public void testPendingSmoothScrollAndRemove() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -1063,42 +1153,41 @@
 
         // Pressing lots of key to make sure smooth scroller is running
         for (int i = 0; i < 20; i++) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         }
-        Thread.sleep(100);
 
         assertTrue(mGridView.getLayoutManager().isSmoothScrolling());
-        final int removeIndex = mGridView.getChildCount() - 1;
-        runTestOnUiThread(new Runnable() {
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
+                final int removeIndex = mGridView.getChildViewHolder(
+                        mGridView.getChildAt(mGridView.getChildCount() - 1)).getAdapterPosition();
                 mActivity.removeItems(removeIndex, 1);
             }
         });
+        waitForLayout();
 
-        Thread.sleep(20); // wait for layout
         assertFalse("removing the index of attached child should kill smooth scroller",
                 mGridView.getLayoutManager().isSmoothScrolling());
 
-        waitForTransientStateGone(null);
-        waitForScrollIdle();
+        waitForItemAnimation();
         int focusIndex = mGridView.getSelectedPosition();
-        int leftEdge = mGridView.getLayoutManager().findViewByPosition(focusIndex).getLeft();
+        int topEdge = mGridView.getLayoutManager().findViewByPosition(focusIndex).getTop();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
             }
         });
-        waitForTransientStateGone(null);
         waitForScrollIdle();
-        assertEquals(leftEdge,
-                mGridView.getLayoutManager().findViewByPosition(focusIndex).getLeft());
+        assertEquals(topEdge,
+                mGridView.getLayoutManager().findViewByPosition(focusIndex).getTop());
     }
 
+    @Test
     public void testFocusToFirstItem() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 200);
@@ -1106,13 +1195,19 @@
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 3;
 
-        int[] removedItems = mActivity.removeItems(0, 200);
+        performAndWaitForAnimation(new Runnable() {
+            public void run() {
+                mRemovedItems = mActivity.removeItems(0, 200);
+            }
+        });
 
-        waitForTransientStateGone(null);
         humanDelay(500);
-        mActivity.addItems(0, removedItems);
+        performAndWaitForAnimation(new Runnable() {
+            public void run() {
+                mActivity.addItems(0, mRemovedItems);
+            }
+        });
 
-        waitForTransientStateGone(null);
         humanDelay(500);
         assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
 
@@ -1122,6 +1217,7 @@
         assertTrue(mGridView.getLayoutManager().findViewByPosition(0).hasFocus());
     }
 
+    @Test
     public void testNonFocusableHorizontal() throws Throwable {
         final int numItems = 200;
         final int startPos = 45;
@@ -1129,8 +1225,7 @@
         final int numColumns = 3;
         final int endPos = startPos + numColumns * (skips + 1);
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
@@ -1151,27 +1246,27 @@
         waitForScrollIdle(mVerifyLayout);
 
         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+            sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
         } else {
-            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+            sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
         }
         waitForScrollIdle(mVerifyLayout);
         assertEquals(endPos, mGridView.getSelectedPosition());
 
         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+            sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
         } else {
-            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+            sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
         }
         waitForScrollIdle(mVerifyLayout);
         assertEquals(startPos, mGridView.getSelectedPosition());
 
     }
 
+    @Test
     public void testNoInitialFocusable() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         final int numItems = 100;
@@ -1192,19 +1287,19 @@
         assertTrue(mGridView.isFocused());
 
         if (mGridView.getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_RTL) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+            sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
         } else {
-            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+            sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
         }
         waitForScrollIdle(mVerifyLayout);
         assertEquals(firstFocusableIndex, mGridView.getSelectedPosition());
         assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus());
     }
 
+    @Test
     public void testFocusOutOfEmptyListView() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         final int numItems = 100;
@@ -1215,7 +1310,7 @@
         initActivity(intent);
 
         final View horizontalGridView = new HorizontalGridViewEx(mGridView.getContext());
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 horizontalGridView.setFocusable(true);
@@ -1228,15 +1323,15 @@
 
         assertTrue(horizontalGridView.isFocused());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
 
         assertTrue(mGridView.hasFocus());
     }
 
+    @Test
     public void testTransferFocusToChildWhenGainFocus() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         final int numItems = 100;
@@ -1259,10 +1354,10 @@
         assertTrue(mGridView.getLayoutManager().findViewByPosition(firstFocusableIndex).hasFocus());
     }
 
+    @Test
     public void testFocusFromSecondChild() throws Throwable {
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_linear);
         final int numItems = 100;
@@ -1278,7 +1373,7 @@
         initActivity(intent);
 
         // switching Adapter to cause a full rebind,  test if it will focus to second item.
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             @Override
             public void run() {
                 mActivity.mNumItems = numItems;
@@ -1286,8 +1381,90 @@
                 mActivity.rebindToNewAdapter();
             }
         });
+        assertTrue(mGridView.findViewHolderForAdapterPosition(1).itemView.hasFocus());
     }
 
+    @Test
+    public void removeFocusableItemAndFocusableRecyclerViewGetsFocus() throws Throwable {
+        final int numItems = 100;
+        final int numColumns = 3;
+        final int focusableIndex = 2;
+
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = false;
+        }
+        focusable[focusableIndex] = true;
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setSelectedPositionSmooth(focusableIndex);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(focusableIndex, mGridView.getSelectedPosition());
+
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(focusableIndex, 1);
+            }
+        });
+        assertTrue(dumpGridView(mGridView), mGridView.isFocused());
+    }
+
+    @Test
+    public void removeFocusableItemAndUnFocusableRecyclerViewLosesFocus() throws Throwable {
+        final int numItems = 100;
+        final int numColumns = 3;
+        final int focusableIndex = 2;
+
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.vertical_grid);
+        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = numColumns;
+        boolean[] focusable = new boolean[numItems];
+        for (int i = 0; i < focusable.length; i++) {
+            focusable[i] = false;
+        }
+        focusable[focusableIndex] = true;
+        intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
+        initActivity(intent);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setFocusableInTouchMode(false);
+                mGridView.setFocusable(false);
+                mGridView.setSelectedPositionSmooth(focusableIndex);
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(focusableIndex, mGridView.getSelectedPosition());
+
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.removeItems(focusableIndex, 1);
+            }
+        });
+        assertFalse(dumpGridView(mGridView), mGridView.hasFocus());
+    }
+
+    @Test
     public void testNonFocusableVertical() throws Throwable {
         final int numItems = 200;
         final int startPos = 44;
@@ -1295,8 +1472,7 @@
         final int numColumns = 3;
         final int endPos = startPos + numColumns * (skips + 1);
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
@@ -1316,21 +1492,21 @@
         mGridView.setSelectedPositionSmooth(startPos);
         waitForScrollIdle(mVerifyLayout);
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         waitForScrollIdle(mVerifyLayout);
         assertEquals(endPos, mGridView.getSelectedPosition());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
         waitForScrollIdle(mVerifyLayout);
         assertEquals(startPos, mGridView.getSelectedPosition());
 
     }
 
+    @Test
     public void testLtrFocusOutStartDisabled() throws Throwable {
         final int numItems = 200;
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_ltr);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
@@ -1338,7 +1514,7 @@
         mNumRows = 1;
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGridView.requestFocus();
@@ -1347,16 +1523,16 @@
         });
         waitForScrollIdle(mVerifyLayout);
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
         waitForScrollIdle(mVerifyLayout);
         assertTrue(mGridView.hasFocus());
     }
 
+    @Test
     public void testRtlFocusOutStartDisabled() throws Throwable {
         final int numItems = 200;
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
         intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
@@ -1364,7 +1540,7 @@
         mNumRows = 1;
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGridView.requestFocus();
@@ -1373,18 +1549,18 @@
         });
         waitForScrollIdle(mVerifyLayout);
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
         waitForScrollIdle(mVerifyLayout);
         assertTrue(mGridView.hasFocus());
     }
 
+    @Test
     public void testTransferFocusable() throws Throwable {
         final int numItems = 200;
         final int numColumns = 3;
         final int startPos = 1;
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
@@ -1408,13 +1584,13 @@
         assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
     }
 
+    @Test
     public void testTransferFocusable2() throws Throwable {
         final int numItems = 200;
         final int numColumns = 3;
-        final int startPos = 10;
+        final int startPos = 3; // make sure view at startPos is in visible area.
 
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
@@ -1431,6 +1607,8 @@
         intent.putExtra(GridActivity.EXTRA_ITEMS_FOCUSABLE, focusable);
         initActivity(intent);
 
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
+
         changeArraySize(0);
         assertTrue(mGridView.isFocused());
 
@@ -1438,9 +1616,9 @@
         assertTrue(mGridView.getLayoutManager().findViewByPosition(startPos).hasFocus());
     }
 
+    @Test
     public void testNonFocusableLoseInFastLayout() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         int[] items = new int[300];
@@ -1457,19 +1635,19 @@
         initActivity(intent);
 
         mGridView.setSelectedPositionSmooth(0);
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
 
         for (int i = 0; i < pressDown; i++) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         }
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
         assertFalse(mGridView.isFocused());
 
     }
 
+    @Test
     public void testFocusableViewAvailable() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0);
@@ -1481,7 +1659,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // RecyclerView does not respect focusable and focusableInTouchMode flag, so
@@ -1501,17 +1679,22 @@
                 }
             }
         });
-        mActivity.addItems(0, new int[]{200, 300, 500, 500, 200});
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        performAndWaitForAnimation(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.addItems(0, new int[]{200, 300, 500, 500, 200});
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
 
         assertFalse("GridView should not be scrolled", scrolled[0]);
-        assertTrue(mGridView.getChildAt(1).hasFocus());
+        assertTrue(mGridView.getLayoutManager().findViewByPosition(2).hasFocus());
 
     }
 
+    @Test
     public void testSetSelectionWithDelta() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 300);
@@ -1521,7 +1704,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(3);
             }
@@ -1561,7 +1744,7 @@
         assertEquals(top1 - 100, top6);
 
         // scroll to invisible item that is far away.
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(100);
             }
@@ -1576,9 +1759,9 @@
         assertEquals(top1 - 50, top8);
     }
 
+    @Test
     public void testSetSelectionWithDeltaInGrid() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
@@ -1588,7 +1771,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -1629,7 +1812,7 @@
         assertEquals(top1 - 100, top6);
 
         // scroll to invisible item that is far away.
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(200);
             }
@@ -1646,9 +1829,9 @@
     }
 
 
+    @Test
     public void testSetSelectionWithDeltaInGrid1() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_ITEMS, new int[]{
@@ -1691,7 +1874,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -1732,7 +1915,7 @@
         assertEquals(top1 - 100, top6);
 
         // scroll to invisible item that is far away.
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(200);
             }
@@ -1748,9 +1931,9 @@
         assertEquals(top1 - 50, top8);
     }
 
+    @Test
     public void testSmoothScrollSelectionEvents() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
@@ -1759,7 +1942,7 @@
         mNumRows = 3;
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(30);
             }
@@ -1787,9 +1970,9 @@
 
     }
 
+    @Test
     public void testSmoothScrollSelectionEventsLinear() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 500);
@@ -1798,7 +1981,7 @@
         mNumRows = 1;
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(10);
             }
@@ -1826,9 +2009,9 @@
 
     }
 
+    @Test
     public void testScrollToNoneExisting() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_grid);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 100);
@@ -1837,7 +2020,7 @@
         mNumRows = 3;
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(99);
             }
@@ -1846,13 +2029,13 @@
         humanDelay(500);
 
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(50);
             }
         });
         Thread.sleep(100);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.requestLayout();
                 mGridView.setSelectedPositionSmooth(0);
@@ -1863,9 +2046,9 @@
 
     }
 
+    @Test
     public void testSmoothscrollerInterrupted() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -1886,31 +2069,18 @@
 
         // Pressing lots of key to make sure smooth scroller is running
         for (int i = 0; i < 20; i++) {
-            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         }
-        Thread.sleep(100);
-        int total = 0;
-        while (mGridView.getLayoutManager().isSmoothScrolling() ||
-                mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
-            if ((total += 10) >= WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS) {
-                throw new RuntimeException("waitForScrollIdle Timeout");
-            }
-            try {
-                // Repeatedly pressing to make sure pending keys does not drop to zero.
-                Thread.sleep(10);
-                sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
-            } catch (InterruptedException ex) {
-                break;
-            }
+        while (mGridView.getLayoutManager().isSmoothScrolling()
+                || mGridView.getScrollState() != BaseGridView.SCROLL_STATE_IDLE) {
+            // Repeatedly pressing to make sure pending keys does not drop to zero.
+            sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         }
-
-        assertTrue("LinearSmoothScroller would not use many RV.smoothScrollBy() calls",
-                ((VerticalGridViewEx) mGridView).mSmoothScrollByCalled < 10);
     }
 
+    @Test
     public void testSmoothscrollerCancelled() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -1931,7 +2101,7 @@
 
         int targetPosition = items.length - 1;
         mGridView.setSelectedPositionSmooth(targetPosition);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.stopScroll();
             }
@@ -1942,9 +2112,9 @@
                 mGridView.findFocus());
     }
 
+    @Test
     public void testSetNumRowsAndAddItem() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -1964,7 +2134,7 @@
 
         mActivity.addItems(items.length, new int[]{300});
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 ((VerticalGridView) mGridView).setNumColumns(2);
             }
@@ -1974,9 +2144,9 @@
     }
 
 
+    @Test
     public void testRequestLayoutBugInLayout() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
@@ -1992,23 +2162,23 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
         });
         waitForScrollIdle(mVerifyLayout);
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
         waitForScrollIdle(mVerifyLayout);
 
         assertEquals("Line 2", ((TextView) mGridView.findFocus()).getText().toString());
     }
 
 
+    @Test
     public void testChangeLayoutInChild() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear_wrap_content);
         intent.putExtra(GridActivity.EXTRA_REQUEST_LAYOUT_ONFOCUS, true);
@@ -2023,26 +2193,26 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(0);
             }
         });
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
         verifyMargin();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
         });
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
         verifyMargin();
     }
 
+    @Test
     public void testWrapContent() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.horizontal_grid_wrap);
         int[] items = new int[200];
@@ -2055,7 +2225,7 @@
 
         initActivity(intent);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mActivity.attachToNewAdapter(new int[0]);
             }
@@ -2064,9 +2234,9 @@
     }
 
 
+    @Test
     public void testZeroFixedSecondarySize() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear_measured_with_zero);
         intent.putExtra(GridActivity.EXTRA_SECONDARY_SIZE_ZERO, true);
@@ -2083,9 +2253,9 @@
 
     }
 
+    @Test
     public void testChildStates() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         int[] items = new int[100];
         for (int i = 0; i < items.length; i++) {
@@ -2104,7 +2274,7 @@
         final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
 
         // 1 Save view states
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
                         .getText()), 0, 1);
@@ -2115,7 +2285,7 @@
         });
 
         // 2 Change view states
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(0))
                         .getText()), 1, 2);
@@ -2125,30 +2295,30 @@
         });
 
         // 3 Detached and re-attached,  should still maintain state of (2)
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(1);
             }
         });
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1);
         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2);
         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1);
         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionEnd(), 2);
 
         // 4 Recycled and rebound, should load state from (2)
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(20);
             }
         });
         waitForScrollIdle(mVerifyLayout);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setSelectedPositionSmooth(0);
             }
         });
-        waitForScrollIdleAndItemAnimation(mVerifyLayout);
+        waitForScrollIdle(mVerifyLayout);
         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionStart(), 1);
         assertEquals(((TextView) mGridView.getChildAt(0)).getSelectionEnd(), 2);
         assertEquals(((TextView) mGridView.getChildAt(1)).getSelectionStart(), 1);
@@ -2156,9 +2326,9 @@
     }
 
 
+    @Test
     public void testNoDispatchSaveChildState() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         int[] items = new int[100];
         for (int i = 0; i < items.length; i++) {
@@ -2176,7 +2346,7 @@
         final SparseArray<Parcelable> container = new SparseArray<Parcelable>();
 
         // 1. Set text selection, save view states should do nothing on child
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
                     Selection.setSelection((Spannable)(((TextView) mGridView.getChildAt(i))
@@ -2187,7 +2357,7 @@
         });
 
         // 2. clear the text selection
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
                     Selection.removeSelection((Spannable)(((TextView) mGridView.getChildAt(i))
@@ -2197,7 +2367,7 @@
         });
 
         // 3. Restore view states should be a no-op for child
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.restoreHierarchyState(container);
                 for (int i = 0; i < mGridView.getChildCount(); i++) {
@@ -2271,9 +2441,9 @@
         }
     }
 
+    @Test
     public void testMultipleScrollPosition1() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
@@ -2300,7 +2470,7 @@
         assertEquals("First view is aligned with padding top",
                 mGridView.getPaddingTop(), mGridView.getChildAt(0).getTop());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         waitForScrollIdle(mVerifyLayout);
 
         final View v = mGridView.getChildAt(0);
@@ -2340,9 +2510,9 @@
         }
     }
 
+    @Test
     public void testMultipleScrollPosition2() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -2364,7 +2534,7 @@
         assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(),
                 mGridView.getChildAt(0).getTop());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         waitForScrollIdle(mVerifyLayout);
 
         final View v = mGridView.getChildAt(0);
@@ -2403,9 +2573,9 @@
         }
     }
 
+    @Test
     public void testMultipleScrollPosition3() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.relative_layout);
         intent.putExtra(GridActivity.EXTRA_REQUEST_FOCUS_ONLAYOUT, true);
@@ -2427,7 +2597,7 @@
         assertEquals("First view is aligned with padding top", mGridView.getPaddingTop(),
                 mGridView.getChildAt(0).getTop());
 
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         waitForScrollIdle(mVerifyLayout);
 
         final View v = mGridView.getChildAt(0);
@@ -2439,9 +2609,9 @@
                 mGridView.getPaddingTop() - (t2align - t1align), v.getTop());
     }
 
+    @Test
     public void testSelectionAndAddItemInOneCycle() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0);
@@ -2449,27 +2619,27 @@
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 1;
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mActivity.addItems(0, new int[]{300, 300});
                 mGridView.setSelectedPosition(0);
             }
         });
-        waitForTransientStateGone(null);
         assertEquals(0, mGridView.getSelectedPosition());
     }
 
+    @Test
     public void testSelectViewTaskSmoothWithAdapterChange() throws Throwable {
         testSelectViewTaskWithAdapterChange(true /*smooth*/);
     }
 
+    @Test
     public void testSelectViewTaskWithAdapterChange() throws Throwable {
         testSelectViewTaskWithAdapterChange(false /*smooth*/);
     }
 
     private void testSelectViewTaskWithAdapterChange(final boolean smooth) throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2);
@@ -2484,7 +2654,7 @@
                 selectedViewByTask[0] = viewHolder.itemView;
             }
         };
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mActivity.removeItems(0, 1);
                 if (smooth) {
@@ -2494,16 +2664,15 @@
                 }
             }
         });
-        waitForTransientStateGone(null);
         assertEquals(0, mGridView.getSelectedPosition());
         assertNotNull(selectedViewByTask[0]);
         assertNotSame(firstView, selectedViewByTask[0]);
         assertSame(mGridView.getLayoutManager().findViewByPosition(0), selectedViewByTask[0]);
     }
 
+    @Test
     public void testNotifyItemTypeChangedSelectionEvent() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 10);
@@ -2521,21 +2690,20 @@
             }
         });
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 ChangeableViewTypesProvider.setViewType(0, 1);
                 mGridView.getAdapter().notifyItemChanged(0, 1);
             }
         });
-        waitForTransientStateGone(null);
         assertEquals(0, mGridView.getSelectedPosition());
         assertEquals(selectedLog.size(), 1);
         assertEquals((int) selectedLog.get(0), 0);
     }
 
+    @Test
     public void testSelectionSmoothAndAddItemInOneCycle() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 0);
@@ -2543,19 +2711,18 @@
         mOrientation = BaseGridView.HORIZONTAL;
         mNumRows = 1;
 
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             public void run() {
                 mActivity.addItems(0, new int[]{300, 300});
                 mGridView.setSelectedPositionSmooth(0);
             }
         });
-        waitForTransientStateGone(null);
         assertEquals(0, mGridView.getSelectedPosition());
     }
 
+    @Test
     public void testExtraLayoutSpace() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
@@ -2564,22 +2731,21 @@
 
         final int windowSize = mGridView.getHeight();
         final int extraLayoutSize = windowSize;
-        int itemLength = mActivity.mItemLengths[0];
         mOrientation = BaseGridView.VERTICAL;
         mNumRows = 1;
 
         // add extra layout space
-        runTestOnUiThread(new Runnable() {
+        startWaitLayout();
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setExtraLayoutSpace(extraLayoutSize);
             }
         });
-        Thread.sleep(50);
+        waitForLayout();
         View v;
         v = mGridView.getChildAt(mGridView.getChildCount() - 1);
         assertTrue(v.getTop() < windowSize + extraLayoutSize);
-        assertTrue(v.getBottom() >= windowSize + extraLayoutSize -
-                mGridView.getVerticalMargin());
+        assertTrue(v.getBottom() >= windowSize + extraLayoutSize - mGridView.getVerticalMargin());
 
         mGridView.setSelectedPositionSmooth(150);
         waitForScrollIdle(mVerifyLayout);
@@ -2588,7 +2754,7 @@
         assertTrue(v.getTop() <= -extraLayoutSize + mGridView.getVerticalMargin());
 
         // clear extra layout space
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             public void run() {
                 mGridView.setExtraLayoutSpace(0);
                 verifyMargin();
@@ -2600,9 +2766,9 @@
         assertTrue(v.getBottom() >= windowSize - mGridView.getVerticalMargin());
     }
 
+    @Test
     public void testFocusFinder() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear_with_button);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 3);
@@ -2614,13 +2780,13 @@
         // test focus from button to vertical grid view
         final View button = mActivity.findViewById(R.id.button);
         assertTrue(button.isFocused());
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         assertFalse(mGridView.isFocused());
         assertTrue(mGridView.hasFocus());
 
         // FocusFinder should find last focused(2nd) item on DPAD_DOWN
         final View secondChild = mGridView.getChildAt(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 secondChild.requestFocus();
@@ -2628,12 +2794,12 @@
             }
         });
         assertTrue(button.isFocused());
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         assertTrue(secondChild.isFocused());
 
         // Bug 26918143 Even VerticalGridView is not focusable, FocusFinder should find last focused
         // (2nd) item on DPAD_DOWN.
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 button.requestFocus();
@@ -2642,13 +2808,13 @@
         mGridView.setFocusable(false);
         mGridView.setFocusableInTouchMode(false);
         assertTrue(button.isFocused());
-        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         assertTrue(secondChild.isFocused());
     }
 
+    @Test
     public void testRestoreIndexAndAddItems() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
@@ -2659,7 +2825,7 @@
 
         assertEquals(mGridView.getSelectedPosition(), 0);
         final SparseArray states = new SparseArray();
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGridView.saveHierarchyState(states);
@@ -2667,7 +2833,7 @@
             }
 
         });
-        runTestOnUiThread(new Runnable() {
+        performAndWaitForAnimation(new Runnable() {
             @Override
             public void run() {
                 mGridView.restoreHierarchyState(states);
@@ -2676,13 +2842,12 @@
             }
 
         });
-        waitForTransientStateGone(null);
         assertEquals(mGridView.getSelectedPosition(), 0);
     }
 
+    @Test
     public void test27766012() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear_with_button_onleft);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
@@ -2696,14 +2861,14 @@
         // set remove animator two seconds
         mGridView.getItemAnimator().setRemoveDuration(2000);
         final View view = mGridView.getChildAt(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 view.requestFocus();
             }
         });
         assertTrue(view.hasFocus());
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.removeItems(0, 2);
@@ -2713,7 +2878,7 @@
         // wait one second, removing second view is still attached to parent
         Thread.sleep(1000);
         assertSame(view.getParent(), mGridView);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // refocus to the removed item and do a focus search.
@@ -2724,9 +2889,9 @@
         });
     }
 
+    @Test
     public void testBug27258366() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear_with_button_onleft);
         intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.horizontal_item);
@@ -2749,7 +2914,7 @@
                 }
             }
         };
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGridView.getAdapter().notifyDataSetChanged();
@@ -2758,21 +2923,21 @@
         Thread.sleep(100);
 
         final ViewGroup secondChild = (ViewGroup) mGridView.getChildAt(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 secondChild.requestFocus();
             }
         });
-        sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
         Thread.sleep(100);
         final View button = mActivity.findViewById(R.id.button);
         assertTrue(button.isFocused());
     }
 
+    @Test
     public void testAccessibility() throws Throwable {
-        mInstrumentation = getInstrumentation();
-        Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+        Intent intent = new Intent();
         intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
                 R.layout.vertical_linear);
         intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
@@ -2786,14 +2951,14 @@
         final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
                 .getCompatAccessibilityDelegate();
         final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
             }
         });
         assertTrue("test sanity", info.isScrollable());
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.performAccessibilityAction(mGridView,
@@ -2804,14 +2969,14 @@
         int selectedPosition1 = mGridView.getSelectedPosition();
         assertTrue(0 < selectedPosition1);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
             }
         });
         assertTrue("test sanity", info.isScrollable());
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.performAccessibilityAction(mGridView,
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/HorizontalGridViewEx.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/HorizontalGridViewEx.java
index 2c49283..107916f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/HorizontalGridViewEx.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/HorizontalGridViewEx.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.support.v17.leanback.widget;
 
 import android.content.Context;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/MediaNowPlayingViewTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/MediaNowPlayingViewTest.java
index dc6354c..a47cb00 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/MediaNowPlayingViewTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/MediaNowPlayingViewTest.java
@@ -15,15 +15,15 @@
  */
 package android.support.v17.leanback.widget;
 
-
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
 /**
  * Testing MediaNowPlayingView widget
  */
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/PagingIndicatorTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/PagingIndicatorTest.java
index abc396b..28d4cef 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/PagingIndicatorTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/PagingIndicatorTest.java
@@ -15,19 +15,30 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.junit.Assert.assertEquals;
 
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link PagingIndicator}.
+ */
 @SmallTest
-public class PagingIndicatorTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class PagingIndicatorTest {
     private PagingIndicator mIndicator;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mIndicator = new PagingIndicator(getContext());
+    @Before
+    public void setup() throws Exception {
+        mIndicator = new PagingIndicator(InstrumentationRegistry.getTargetContext());
     }
 
+    @Test
     public void testDotPosition() {
         mIndicator.setPageCount(3);
         assertDotPosition();
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
new file mode 100644
index 0000000..9347776
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
@@ -0,0 +1,239 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from ParallaxIntEffectTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParallaxFloatEffectTest {
+
+    ParallaxSource.FloatSource source;
+    float screenMax;
+    ParallaxEffect.FloatEffect effect;
+    @Mock ParallaxTarget target;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        source = new ParallaxSource.FloatSource<ParallaxSource.FloatProperty>() {
+
+            public void setListener(ParallaxSource.Listener listener) {
+            }
+
+            public float getMaxParentVisibleSize() {
+                return screenMax;
+            }
+
+            @Override
+            public FloatProperty createProperty(String name, int index) {
+                return new FloatProperty(name, index);
+            }
+        };
+        effect = new ParallaxEffect.FloatEffect();
+    }
+
+    @Test
+    public void testOneVariable() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(0));
+        effect.target(target);
+
+        // start
+        var1.setFloatValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // 25% complete
+        var1.setFloatValue(source, 405);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.25f);
+        Mockito.reset(target);
+
+        // middle
+        var1.setFloatValue(source, 270);
+        effect.performMapping(source);
+        verify(target, times(1)).update(.5f);
+        Mockito.reset(target);
+
+        // 75% complete
+        var1.setFloatValue(source, 135);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.75f);
+        Mockito.reset(target);
+
+        // end
+        var1.setFloatValue(source, 0);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // after end
+        var1.setFloatValue(source, -1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // before start
+        var1.setFloatValue(source, 1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyKeyValueOfSameVariableInDesendantOrder() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(550));
+        effect.target(target);
+        var1.setFloatValue(source, 0);
+        effect.performMapping(source);
+    }
+
+    @Test
+    public void testTwoVariable() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+        ParallaxSource.FloatProperty var2 = source.addProperty("var2");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var2.atAbsolute(540));
+        effect.target(target);
+
+        // start
+        var1.setFloatValue(source, 540);
+        var2.setFloatValue(source, 840);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // middle
+        var1.setFloatValue(source, 390);
+        var2.setFloatValue(source, 690);
+        effect.performMapping(source);
+        verify(target, times(1)).update(.5f);
+        Mockito.reset(target);
+
+        // end
+        var1.setFloatValue(source, 240);
+        var2.setFloatValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // after end
+        var1.setFloatValue(source, 200);
+        var2.setFloatValue(source, 500);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // before start
+        var1.setFloatValue(source, 1000);
+        var2.setFloatValue(source, 1300);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, -1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setFloatValue(source, 1000);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before and less
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, 500);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before and hit second
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before with estimation
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, 1080);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.5f);
+        Mockito.reset(target);
+
+        // unknown_after with estimation
+        var1.setFloatValue(source, 0);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.5f);
+        Mockito.reset(target);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatSourceTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatSourceTest.java
new file mode 100644
index 0000000..087e1b7
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatSourceTest.java
@@ -0,0 +1,147 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from ParallaxIntSourceTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParallaxFloatSourceTest {
+
+    ParallaxSource.FloatSource source;
+    float screenMax;
+
+    static void assertFloatEquals(float expected, float actual) {
+        org.junit.Assert.assertEquals((double)expected, (double)actual, 0.0001d);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        source = new ParallaxSource.FloatSource<ParallaxSource.FloatProperty>() {
+
+            public void setListener(ParallaxSource.Listener listener) {
+            }
+
+            public float getMaxParentVisibleSize() {
+                return screenMax;
+            }
+
+            @Override
+            public FloatProperty createProperty(String name, int index) {
+                return new FloatProperty(name, index);
+            }
+        };
+    }
+
+    @Test
+    public void testVariable() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+        var1.setFloatValue(source, 54);
+        assertFloatEquals((float)54, var1.getFloatValue(source));
+        assertEquals(var1.getName(), "var1");
+        var1.set(source, (float)2000);
+        assertFloatEquals((float)2000, var1.get(source).floatValue());
+    }
+
+    @Test
+    public void testFixedKeyValue() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.FloatPropertyKeyValue keyValue = var1.atAbsolute(1000);
+        assertSame(keyValue.getProperty(), var1);
+        assertFloatEquals((float)1000, keyValue.getKeyValue(source));
+    }
+
+    @Test
+    public void testFractionOfKeyValue() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.FloatPropertyKeyValue keyValue = var1.at(0, 0.5f);
+        assertSame(keyValue.getProperty(), var1);
+        assertFloatEquals((float)540, keyValue.getKeyValue(source));
+    }
+
+    @Test
+    public void testFixedKeyValueWithFraction() {
+        screenMax = 1080;
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.FloatPropertyKeyValue keyValue = var1.at(-100, 0.5f);
+        assertSame(keyValue.getProperty(), var1);
+        assertFloatEquals((float)440, keyValue.getKeyValue(source));
+
+        ParallaxSource.FloatPropertyKeyValue keyValue2 = var1.at(100, 0.5f);
+        assertSame(keyValue2.getProperty(), var1);
+        assertFloatEquals((float)640, keyValue2.getKeyValue(source));
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyFloatPropertys_wrongOrder() {
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+        ParallaxSource.FloatProperty var2 = source.addProperty("var2");;
+
+        var1.setFloatValue(source, (float)500);
+        var2.setFloatValue(source, (float)499);
+
+        source.verifyProperties();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyFloatPropertysWrong_combination() {
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+        ParallaxSource.FloatProperty var2 = source.addProperty("var2");
+
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+
+        source.verifyProperties();
+    }
+
+    @Test
+    public void testVerifyFloatPropertys_success() {
+        ParallaxSource.FloatProperty var1 = source.addProperty("var1");
+        ParallaxSource.FloatProperty var2 = source.addProperty("var2");
+
+        var1.setFloatValue(source, (float)499);
+        var2.setFloatValue(source, (float)500);
+
+        source.verifyProperties();
+
+        var1.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_BEFORE);
+        var2.setFloatValue(source, (float)500);
+
+        source.verifyProperties();
+
+        var1.setFloatValue(source, (float)499);
+        var2.setFloatValue(source, ParallaxSource.FloatProperty.UNKNOWN_AFTER);
+
+        source.verifyProperties();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
new file mode 100644
index 0000000..59ba1bc
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v17.leanback.widget;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParallaxIntEffectTest {
+
+    ParallaxSource.IntSource source;
+    int screenMax;
+    ParallaxEffect.IntEffect effect;
+    @Mock ParallaxTarget target;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        source = new ParallaxSource.IntSource<ParallaxSource.IntProperty>() {
+
+            public void setListener(ParallaxSource.Listener listener) {
+            }
+
+            public int getMaxParentVisibleSize() {
+                return screenMax;
+            }
+
+            @Override
+            public IntProperty createProperty(String name, int index) {
+                return new IntProperty(name, index);
+            }
+        };
+        effect = new ParallaxEffect.IntEffect();
+    }
+
+    @Test
+    public void testOneVariable() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(0));
+        effect.target(target);
+
+        // start
+        var1.setIntValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // 25% complete
+        var1.setIntValue(source, 405);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.25f);
+        Mockito.reset(target);
+
+        // middle
+        var1.setIntValue(source, 270);
+        effect.performMapping(source);
+        verify(target, times(1)).update(.5f);
+        Mockito.reset(target);
+
+        // 75% complete
+        var1.setIntValue(source, 135);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.75f);
+        Mockito.reset(target);
+
+        // end
+        var1.setIntValue(source, 0);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // after end
+        var1.setIntValue(source, -1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // before start
+        var1.setIntValue(source, 1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyKeyValueOfSameVariableInDesendantOrder() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(550));
+        effect.target(target);
+        var1.setIntValue(source, 0);
+        effect.performMapping(source);
+    }
+
+    @Test
+    public void testTwoVariable() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+        ParallaxSource.IntProperty var2 = source.addProperty("var2");
+
+        effect.setPropertyRanges(var1.atAbsolute(540), var2.atAbsolute(540));
+        effect.target(target);
+
+        // start
+        var1.setIntValue(source, 540);
+        var2.setIntValue(source, 840);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // middle
+        var1.setIntValue(source, 390);
+        var2.setIntValue(source, 690);
+        effect.performMapping(source);
+        verify(target, times(1)).update(.5f);
+        Mockito.reset(target);
+
+        // end
+        var1.setIntValue(source, 240);
+        var2.setIntValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // after end
+        var1.setIntValue(source, 200);
+        var2.setIntValue(source, 500);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // before start
+        var1.setIntValue(source, 1000);
+        var2.setIntValue(source, 1300);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, -1000);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_after
+        var1.setIntValue(source, 1000);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0f);
+        Mockito.reset(target);
+
+        // unknown_before and less
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, 500);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before and hit second
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, 540);
+        effect.performMapping(source);
+        verify(target, times(1)).update(1f);
+        Mockito.reset(target);
+
+        // unknown_before with estimation
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, 1080);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.5f);
+        Mockito.reset(target);
+
+        // unknown_after with estimation
+        var1.setIntValue(source, 0);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+        effect.performMapping(source);
+        verify(target, times(1)).update(0.5f);
+        Mockito.reset(target);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntSourceTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntSourceTest.java
new file mode 100644
index 0000000..f7097ec
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntSourceTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ParallaxIntSourceTest {
+
+    ParallaxSource.IntSource source;
+    int screenMax;
+
+    static void assertFloatEquals(float expected, float actual) {
+        org.junit.Assert.assertEquals((double)expected, (double)actual, 0.0001d);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        source = new ParallaxSource.IntSource<ParallaxSource.IntProperty>() {
+
+            public void setListener(ParallaxSource.Listener listener) {
+            }
+
+            public int getMaxParentVisibleSize() {
+                return screenMax;
+            }
+
+            @Override
+            public IntProperty createProperty(String name, int index) {
+                return new IntProperty(name, index);
+            }
+        };
+    }
+
+    @Test
+    public void testVariable() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+        var1.setIntValue(source, 54);
+        assertEquals((int)54, var1.getIntValue(source));
+        assertEquals(var1.getName(), "var1");
+        var1.set(source, (int)2000);
+        assertEquals((int)2000, var1.get(source).intValue());
+    }
+
+    @Test
+    public void testFixedKeyValue() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.IntPropertyKeyValue keyValue = var1.atAbsolute(1000);
+        assertSame(keyValue.getProperty(), var1);
+        assertEquals((int)1000, keyValue.getKeyValue(source));
+    }
+
+    @Test
+    public void testFractionOfKeyValue() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.IntPropertyKeyValue keyValue = var1.at(0, 0.5f);
+        assertSame(keyValue.getProperty(), var1);
+        assertEquals((int)540, keyValue.getKeyValue(source));
+    }
+
+    @Test
+    public void testFixedKeyValueWithFraction() {
+        screenMax = 1080;
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+
+        ParallaxSource.IntPropertyKeyValue keyValue = var1.at(-100, 0.5f);
+        assertSame(keyValue.getProperty(), var1);
+        assertEquals((int)440, keyValue.getKeyValue(source));
+
+        ParallaxSource.IntPropertyKeyValue keyValue2 = var1.at(100, 0.5f);
+        assertSame(keyValue2.getProperty(), var1);
+        assertEquals((int)640, keyValue2.getKeyValue(source));
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyIntPropertys_wrongOrder() {
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+        ParallaxSource.IntProperty var2 = source.addProperty("var2");;
+
+        var1.setIntValue(source, (int)500);
+        var2.setIntValue(source, (int)499);
+
+        source.verifyProperties();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testVerifyIntPropertysWrong_combination() {
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+        ParallaxSource.IntProperty var2 = source.addProperty("var2");
+
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+
+        source.verifyProperties();
+    }
+
+    @Test
+    public void testVerifyIntPropertys_success() {
+        ParallaxSource.IntProperty var1 = source.addProperty("var1");
+        ParallaxSource.IntProperty var2 = source.addProperty("var2");
+
+        var1.setIntValue(source, (int)499);
+        var2.setIntValue(source, (int)500);
+
+        source.verifyProperties();
+
+        var1.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_BEFORE);
+        var2.setIntValue(source, (int)500);
+
+        source.verifyProperties();
+
+        var1.setIntValue(source, (int)499);
+        var2.setIntValue(source, ParallaxSource.IntProperty.UNKNOWN_AFTER);
+
+        source.verifyProperties();
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/PresenterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/PresenterTest.java
index b9a230a..8923d36 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/PresenterTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/PresenterTest.java
@@ -15,23 +15,38 @@
  */
 package android.support.v17.leanback.widget;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
 import android.graphics.Bitmap;
-import android.support.v17.leanback.app.HeadersFragment;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.support.v17.leanback.R;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.v17.leanback.app.HeadersFragment;
 import android.view.ContextThemeWrapper;
-import android.widget.FrameLayout;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
-import android.support.v17.leanback.widget.DividerRow;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.DividerPresenter;
-import android.view.ContextThemeWrapper;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @MediumTest
-public class PresenterTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class PresenterTest {
+    private Context mContext;
 
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
     public void testZoomFactors() throws Throwable {
         new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_SMALL);
         new ListRowPresenter(FocusHighlight.ZOOM_FACTOR_MEDIUM);
@@ -46,7 +61,7 @@
 
     private void testHeaderPresenter(RowHeaderPresenter p) {
         int expectedVisibility;
-        Presenter.ViewHolder vh = p.onCreateViewHolder(new FrameLayout(getContext()));
+        Presenter.ViewHolder vh = p.onCreateViewHolder(new FrameLayout(mContext));
         p.onBindViewHolder(vh, null);
         expectedVisibility = p.isNullItemVisibilityGone() ? View.GONE : View.VISIBLE;
         assertTrue("Header visibility",
@@ -59,6 +74,7 @@
                 vh.view.getVisibility() == View.VISIBLE);
     }
 
+    @Test
     public void testHeaderPresenter() throws Throwable {
         HeadersFragment hf = new HeadersFragment();
         PresenterSelector ps = hf.getPresenterSelector();
@@ -90,8 +106,9 @@
         testHeaderPresenter(rhp);
     }
 
+    @Test
     public void testPlaybackControlsRowPresenter() {
-        setContext(new ContextThemeWrapper(getContext(), R.style.Theme_Leanback));
+        Context context = new ContextThemeWrapper(mContext, R.style.Theme_Leanback);
         Presenter detailsPresenter = new AbstractDetailsDescriptionPresenter() {
             @Override
             protected void onBindDescription(ViewHolder vh, Object item) {
@@ -102,7 +119,7 @@
         PlaybackControlsRowPresenter controlsRowPresenter = new PlaybackControlsRowPresenter(
                 detailsPresenter);
         PlaybackControlsRowPresenter.ViewHolder vh = (PlaybackControlsRowPresenter.ViewHolder)
-                controlsRowPresenter.onCreateViewHolder(new FrameLayout(getContext()));
+                controlsRowPresenter.onCreateViewHolder(new FrameLayout(context));
 
         Object item = new Object();
         PlaybackControlsRow controlsRow = new PlaybackControlsRow(item);
@@ -117,7 +134,7 @@
         controlsRowPresenter.onUnbindRowViewHolder(vh);
 
         controlsRow.setImageBitmap(
-                getContext(), Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888));
+                context, Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888));
         controlsRowPresenter.onBindRowViewHolder(vh, controlsRow);
         AssertHelper.assertGreaterThan("Controls card right panel layout height",
                 vh.view.findViewById(R.id.controls_card_right_panel).getLayoutParams().height, 0);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java
index 3b99486..8b0e43e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ShadowOverlayContainerTest.java
@@ -15,23 +15,40 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-@MediumTest
-public class ShadowOverlayContainerTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ShadowOverlayContainerTest {
+    private Context mContext;
+
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
     public void testWrapContent() {
-        FrameLayout frameLayout = new FrameLayout(getContext());
-        TextView textView = new TextView(getContext());
+        FrameLayout frameLayout = new FrameLayout(mContext);
+        TextView textView = new TextView(mContext);
         textView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
                 LayoutParams.WRAP_CONTENT));
         textView.setText("abc");
-        ShadowOverlayContainer container = new ShadowOverlayContainer(getContext());
+        ShadowOverlayContainer container = new ShadowOverlayContainer(mContext);
         container.initialize(true, true, true);
         container.wrap(textView);
         frameLayout.addView(container);
@@ -56,12 +73,13 @@
         assertEquals(container.getHeight(), textView.getHeight());
     }
 
+    @Test
     public void testFixedSize() {
-        FrameLayout frameLayout = new FrameLayout(getContext());
-        TextView textView = new TextView(getContext());
+        FrameLayout frameLayout = new FrameLayout(mContext);
+        TextView textView = new TextView(mContext);
         textView.setLayoutParams(new FrameLayout.LayoutParams(200, LayoutParams.WRAP_CONTENT));
         textView.setText("abc");
-        ShadowOverlayContainer container = new ShadowOverlayContainer(getContext());
+        ShadowOverlayContainer container = new ShadowOverlayContainer(mContext);
         container.initialize(true, true, true);
         container.wrap(textView);
         frameLayout.addView(container);
@@ -85,13 +103,14 @@
         assertEquals(container.getHeight(), textView.getHeight());
     }
 
+    @Test
     public void testMatchParent() {
-        FrameLayout frameLayout = new FrameLayout(getContext());
-        TextView textView = new TextView(getContext());
+        FrameLayout frameLayout = new FrameLayout(mContext);
+        TextView textView = new TextView(mContext);
         textView.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                 LayoutParams.WRAP_CONTENT));
         textView.setText("abc");
-        ShadowOverlayContainer container = new ShadowOverlayContainer(getContext());
+        ShadowOverlayContainer container = new ShadowOverlayContainer(mContext);
         container.initialize(true, true, true);
         container.wrap(textView);
         frameLayout.addView(container);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/SingleRowTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/SingleRowTest.java
index 0cb34fb..82261d1 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/SingleRowTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/SingleRowTest.java
@@ -15,16 +15,13 @@
  */
 package android.support.v17.leanback.widget;
 
-import org.junit.Assert;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Testing SingleRow algorithm
@@ -40,7 +37,7 @@
         mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
 
         mSingleRow = new SingleRow();
-        mSingleRow.setMargin(20);
+        mSingleRow.setSpacing(20);
         mSingleRow.setProvider(mProvider);
         mSingleRow.appendVisibleItems(200);
         assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
@@ -84,7 +81,7 @@
         mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
 
         mSingleRow = new SingleRow();
-        mSingleRow.setMargin(20);
+        mSingleRow.setSpacing(20);
         mSingleRow.setProvider(mProvider);
         mSingleRow.setReversedFlow(true);
         mSingleRow.appendVisibleItems(-200);
@@ -126,7 +123,7 @@
         mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
 
         mSingleRow = new SingleRow();
-        mSingleRow.setMargin(20);
+        mSingleRow.setSpacing(20);
         mSingleRow.setProvider(mProvider);
         mSingleRow.appendVisibleItems(200);
         assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
@@ -148,7 +145,7 @@
         mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
 
         mSingleRow = new SingleRow();
-        mSingleRow.setMargin(20);
+        mSingleRow.setSpacing(20);
         mSingleRow.setProvider(mProvider);
         mSingleRow.setReversedFlow(true);
         mSingleRow.appendVisibleItems(-200);
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java
index 9d933bc..871751e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/StaggeredGridDefaultTest.java
@@ -15,17 +15,13 @@
  */
 package android.support.v17.leanback.widget;
 
-import org.junit.Assert;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Testing StaggeredGridDefault algorithm
@@ -43,7 +39,7 @@
         // layout first 8 items then all items
         mStaggeredGrid = new StaggeredGridDefault();
         mStaggeredGrid.setNumRows(3);
-        mStaggeredGrid.setMargin(20);
+        mStaggeredGrid.setSpacing(20);
         mStaggeredGrid.setProvider(mProvider);
         mStaggeredGrid.appendVisibleItems(210);
         assertEquals(dump(mStaggeredGrid) + " Should fill 8 items",
@@ -59,7 +55,7 @@
         // layout all items together
         mStaggeredGrid = new StaggeredGridDefault();
         mStaggeredGrid.setNumRows(3);
-        mStaggeredGrid.setMargin(20);
+        mStaggeredGrid.setSpacing(20);
         mStaggeredGrid.setProvider(mProvider);
         mStaggeredGrid.appendVisibleItems(100000);
         assertEquals(dump(mStaggeredGrid) + " should fill 9 items",
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/VerticalGridViewEx.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/VerticalGridViewEx.java
index e262d54..3001bb2 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/VerticalGridViewEx.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/VerticalGridViewEx.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.support.v17.leanback.widget;
 
 import android.content.Context;
diff --git a/v17/leanback/tests/res/drawable/ic_action_a.png b/v17/leanback/tests/res/drawable/ic_action_a.png
new file mode 100644
index 0000000..3d555ef
--- /dev/null
+++ b/v17/leanback/tests/res/drawable/ic_action_a.png
Binary files differ
diff --git a/v17/leanback/tests/res/drawable/spiderman.jpg b/v17/leanback/tests/res/drawable/spiderman.jpg
new file mode 100644
index 0000000..b68384c
--- /dev/null
+++ b/v17/leanback/tests/res/drawable/spiderman.jpg
Binary files differ
diff --git a/documents-archive/AndroidManifest.xml b/v17/leanback/tests/res/layout/details.xml
similarity index 70%
rename from documents-archive/AndroidManifest.xml
rename to v17/leanback/tests/res/layout/details.xml
index 2cd0f7a..88617ea 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/v17/leanback/tests/res/layout/details.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fragment_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/v17/leanback/tests/res/layout/horizontal_grid.xml b/v17/leanback/tests/res/layout/horizontal_grid.xml
index 6c4eaf1..0fbb52f 100644
--- a/v17/leanback/tests/res/layout/horizontal_grid.xml
+++ b/v17/leanback/tests/res/layout/horizontal_grid.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfRows="3"
       lb:rowHeight="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/horizontal_grid_testredundantappendremove2.xml b/v17/leanback/tests/res/layout/horizontal_grid_testredundantappendremove2.xml
index fdfc0ea..746a458 100644
--- a/v17/leanback/tests/res/layout/horizontal_grid_testredundantappendremove2.xml
+++ b/v17/leanback/tests/res/layout/horizontal_grid_testredundantappendremove2.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfRows="3"
       lb:rowHeight="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/horizontal_linear.xml b/v17/leanback/tests/res/layout/horizontal_linear.xml
index 5e9da64..cca5781 100644
--- a/v17/leanback/tests/res/layout/horizontal_linear.xml
+++ b/v17/leanback/tests/res/layout/horizontal_linear.xml
@@ -13,8 +13,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/playback_controls_with_video.xml b/v17/leanback/tests/res/layout/playback_controls_with_video.xml
new file mode 100644
index 0000000..cbf2a91
--- /dev/null
+++ b/v17/leanback/tests/res/layout/playback_controls_with_video.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <VideoView
+        android:id="@+id/videoView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
+
+    <fragment
+        android:id="@+id/playback_controls_fragment"
+        android:name="android.support.v17.leanback.app.PlaybackOverlayTestFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/v17/leanback/tests/res/layout/vertical_grid.xml b/v17/leanback/tests/res/layout/vertical_grid.xml
index f4c0065..5aa5697 100644
--- a/v17/leanback/tests/res/layout/vertical_grid.xml
+++ b/v17/leanback/tests/res/layout/vertical_grid.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="3"
       lb:columnWidth="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/vertical_grid_ltr.xml b/v17/leanback/tests/res/layout/vertical_grid_ltr.xml
index 6a390a3..762c5f7 100644
--- a/v17/leanback/tests/res/layout/vertical_grid_ltr.xml
+++ b/v17/leanback/tests/res/layout/vertical_grid_ltr.xml
@@ -20,8 +20,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="150dip"
       lb:focusOutSideStart="false"
diff --git a/v17/leanback/tests/res/layout/vertical_grid_rtl.xml b/v17/leanback/tests/res/layout/vertical_grid_rtl.xml
index 87b2054..b9a53e8 100644
--- a/v17/leanback/tests/res/layout/vertical_grid_rtl.xml
+++ b/v17/leanback/tests/res/layout/vertical_grid_rtl.xml
@@ -20,8 +20,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="150dip"
       lb:focusOutSideStart="false"
diff --git a/v17/leanback/tests/res/layout/vertical_grid_testredundantappendremove.xml b/v17/leanback/tests/res/layout/vertical_grid_testredundantappendremove.xml
index bf056f8..d0fdeca 100644
--- a/v17/leanback/tests/res/layout/vertical_grid_testredundantappendremove.xml
+++ b/v17/leanback/tests/res/layout/vertical_grid_testredundantappendremove.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="3"
       lb:columnWidth="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/vertical_linear.xml b/v17/leanback/tests/res/layout/vertical_linear.xml
index 0a1d00c..c76b26a 100644
--- a/v17/leanback/tests/res/layout/vertical_linear.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/vertical_linear_measured_with_zero.xml b/v17/leanback/tests/res/layout/vertical_linear_measured_with_zero.xml
index 012829d..ef82c19 100644
--- a/v17/leanback/tests/res/layout/vertical_linear_measured_with_zero.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear_measured_with_zero.xml
@@ -16,8 +16,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1" />
     </FrameLayout>
 
diff --git a/v17/leanback/tests/res/layout/vertical_linear_with_button.xml b/v17/leanback/tests/res/layout/vertical_linear_with_button.xml
index f37bdc3..067310d 100644
--- a/v17/leanback/tests/res/layout/vertical_linear_with_button.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear_with_button.xml
@@ -20,8 +20,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="150dip"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml b/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
index 6305de6..02b027c 100644
--- a/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear_with_button_onleft.xml
@@ -20,8 +20,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="4dip"
+      android_horizontalSpacing="12dip"
+      android:verticalSpacing="4dip"
       lb:numberOfColumns="1"
       android:paddingBottom="12dip"
       android:paddingLeft="12dip"
diff --git a/v17/leanback/tests/res/layout/vertical_linear_wrap_content.xml b/v17/leanback/tests/res/layout/vertical_linear_wrap_content.xml
index 6770983..7803901 100644
--- a/v17/leanback/tests/res/layout/vertical_linear_wrap_content.xml
+++ b/v17/leanback/tests/res/layout/vertical_linear_wrap_content.xml
@@ -12,8 +12,8 @@
       android:focusable="true"
       android:focusableInTouchMode="true"
       android:background="#00ffff"
-      lb:horizontalMargin="12dip"
-      lb:verticalMargin="24dip"
+      android:horizontalSpacing="12dip"
+      android:verticalSpacing="24dip"
       lb:numberOfColumns="1"
       lb:columnWidth="wrap_content"
       android:paddingBottom="12dip"
diff --git a/v17/leanback/tests/res/layout/video_fragment_with_controls.xml b/v17/leanback/tests/res/layout/video_fragment_with_controls.xml
new file mode 100644
index 0000000..6df2b3e
--- /dev/null
+++ b/v17/leanback/tests/res/layout/video_fragment_with_controls.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <fragment
+        android:id="@+id/video_fragment"
+        android:name="android.support.v17.leanback.app.VideoFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>
diff --git a/v17/preference-leanback/Android.mk b/v17/preference-leanback/Android.mk
index 2058002..263d334 100644
--- a/v17/preference-leanback/Android.mk
+++ b/v17/preference-leanback/Android.mk
@@ -14,45 +14,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Android libraries referenced by this module's resources.
-resource_libs := \
-    android-support-v17-leanback \
-    android-support-v14-preference \
-    android-support-v7-preference \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview \
-    android-support-annotations
-
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.  The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v17-preference-leanback-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := $(resource_libs)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-LOCAL_JAR_EXCLUDE_FILES := none
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# -----------------------------------------------------------------------
-
-#  A helper sub-library that makes direct use of API 21.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v17-preference-leanback-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_JAVA_LIBRARIES := \
-    android-support-v17-preference-leanback-res \
-    android-support-v17-leanback
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
@@ -63,23 +24,25 @@
 #       android-support-v7-preference \
 #       android-support-v7-appcompat \
 #       android-support-v7-recyclerview \
-#       android-support-v4 \
-#       android-support-annotations
+#       android-support-v4
 #
 # in their makefiles to include the resources in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v17-preference-leanback
-LOCAL_SDK_VERSION := 17
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-v17-preference-leanback-api21
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v17-preference-leanback-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
-    $(resource_libs) \
-    android-support-v4
+    android-support-v17-leanback \
+    android-support-v14-preference \
+    android-support-v7-preference \
+    android-support-v7-appcompat \
+    android-support-v7-recyclerview \
+    android-support-v4 \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
index b67a56f..1054949 100644
--- a/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
+++ b/v17/preference-leanback/api21/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
@@ -16,8 +16,10 @@
 
 package android.support.v17.internal.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Outline;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.util.AttributeSet;
 import android.view.View;
@@ -32,6 +34,8 @@
  *
  * @hide
  */
+@RequiresApi(21)
+@TargetApi(21)
 @RestrictTo(GROUP_ID)
 public class OutlineOnlyWithChildrenFrameLayout extends FrameLayout {
 
diff --git a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
index 322fbe3..1b320a5 100644
--- a/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
+++ b/v17/preference-leanback/api21/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
@@ -16,6 +16,8 @@
 
 package android.support.v17.preference;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v17.leanback.transition.FadeAndShortSlide;
 import android.app.Fragment;
@@ -27,6 +29,8 @@
 /**
  * @hide
  */
+@RequiresApi(21)
+@TargetApi(21)
 @RestrictTo(GROUP_ID)
 public class LeanbackPreferenceFragmentTransitionHelperApi21 {
 
diff --git a/v17/preference-leanback/build.gradle b/v17/preference-leanback/build.gradle
index dbdce7e..2f4ec5b 100644
--- a/v17/preference-leanback/build.gradle
+++ b/v17/preference-leanback/build.gradle
@@ -1,23 +1,4 @@
-/*
- * 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
- */
-
-
-
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'preference-leanback-v17'
 
 dependencies {
@@ -34,16 +15,14 @@
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['src', 'api21']
+        main.java.srcDirs = [
+                'api21',
+                'src'
+        ]
         main.res.srcDir 'res'
-        main.assets.srcDir 'assets'
-        main.resources.srcDir 'src'
+    }
 
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
+    lintOptions {
     }
 
     compileOptions {
diff --git a/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml b/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml
new file mode 100644
index 0000000..3e04793
--- /dev/null
+++ b/v17/preference-leanback/res/layout-v21/leanback_preference_category.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/lb_preference_category_height"
+    android:clipToPadding="false"
+    android:paddingStart="@dimen/lb_preference_item_padding_start"
+    android:paddingEnd="@dimen/lb_preference_item_padding_end">
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start|center_vertical"
+        android:fontFamily="sans-serif-condensed"
+        android:textColor="?android:attr/colorAccent"
+        android:textSize="@dimen/lb_preference_category_text_size"/>
+</FrameLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_category.xml b/v17/preference-leanback/res/layout/leanback_preference_category.xml
index 97bb3f0..cfc5f1a 100644
--- a/v17/preference-leanback/res/layout/leanback_preference_category.xml
+++ b/v17/preference-leanback/res/layout/leanback_preference_category.xml
@@ -28,6 +28,6 @@
         android:layout_height="wrap_content"
         android:layout_gravity="start|center_vertical"
         android:fontFamily="sans-serif-condensed"
-        android:textColor="?android:attr/colorAccent"
+        android:textColor="?android:attr/textColorPrimary"
         android:textSize="@dimen/lb_preference_category_text_size"/>
 </FrameLayout>
diff --git a/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml b/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml
new file mode 100644
index 0000000..c121d77
--- /dev/null
+++ b/v17/preference-leanback/res/layout/leanback_preference_widget_seekbar.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Leanback-styled Layout used for a SeekBar preference -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:minHeight="?android:attr/listPreferredItemHeight"
+              android:gravity="center_vertical"
+              android:paddingStart="@dimen/lb_preference_item_padding_start"
+              android:paddingEnd="@dimen/lb_preference_item_padding_end"
+              android:clickable="true"
+              android:focusable="true"
+              android:clipChildren="false"
+              android:clipToPadding="false">
+
+    <ImageView
+            android:id="@+android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:minWidth="@dimen/preference_icon_minWidth"/>
+
+    <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="6dip"
+            android:layout_weight="1"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+
+        <TextView android:id="@+android:id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:layout_marginBottom="@dimen/lb_preference_item_primary_text_margin_bottom"
+                  android:fontFamily="sans-serif-condensed"
+                  android:ellipsize="marquee"
+                  android:fadingEdge="horizontal"
+                  android:textColor="@color/lb_preference_item_primary_text_color"
+                  android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+
+        <TextView android:id="@+android:id/summary"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_below="@android:id/title"
+                  android:layout_alignStart="@android:id/title"
+                  android:fontFamily="sans-serif-condensed"
+                  android:textColor="@color/lb_preference_item_secondary_text_color"
+                  android:textSize="@dimen/lb_preference_item_secondary_text_size"
+                  android:maxLines="4" />
+
+        <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+        to the children of this container layout. Otherwise, the animated pressed state will also
+        play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+        The background of the SeekBar is also set to null to disable the ripple background -->
+        <android.support.v7.preference.UnPressableLinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/summary"
+                android:layout_alignStart="@android:id/title"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+            <SeekBar
+                    android:id="@+id/seekbar"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:paddingStart="@dimen/lb_preference_seekbar_padding_start"
+                    android:paddingEnd="@dimen/lb_preference_seekbar_padding_end"
+                    android:focusable="false"
+                    android:clickable="false"
+                    android:background="@null" />
+
+            <TextView android:id="@+id/seekbar_value"
+                      android:layout_width="@dimen/lb_preference_seekbar_value_width"
+                      android:layout_height="match_parent"
+                      android:gravity="right|center_vertical"
+                      android:fontFamily="sans-serif-condensed"
+                      android:textColor="@color/lb_preference_item_primary_text_color"
+                      android:textSize="@dimen/lb_preference_item_primary_text_size"/>
+        </android.support.v7.preference.UnPressableLinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v17/preference-leanback/res/values/dimens.xml b/v17/preference-leanback/res/values/dimens.xml
index f3d36af..ed662a1 100644
--- a/v17/preference-leanback/res/values/dimens.xml
+++ b/v17/preference-leanback/res/values/dimens.xml
@@ -38,4 +38,7 @@
     <dimen name="lb_preference_category_height">40dp</dimen>
 
     <dimen name="lb_settings_pane_width">360dp</dimen>
+    <dimen name="lb_preference_seekbar_padding_start">0dp</dimen>
+    <dimen name="lb_preference_seekbar_padding_end">22dp</dimen>
+    <dimen name="lb_preference_seekbar_value_width">36dp</dimen>
 </resources>
diff --git a/v17/preference-leanback/res/values/styles.xml b/v17/preference-leanback/res/values/styles.xml
index fbb1ad0..cb12b3a 100644
--- a/v17/preference-leanback/res/values/styles.xml
+++ b/v17/preference-leanback/res/values/styles.xml
@@ -50,6 +50,12 @@
         <item name="android:switchTextOff">@string/v7_preference_off</item>
     </style>
 
+    <style name="LeanbackPreference.SeekBarPreference">
+        <item name="android:layout">@layout/leanback_preference_widget_seekbar</item>
+        <item name="adjustable">true</item>
+        <item name="showSeekBarValue">true</item>
+    </style>
+
     <style name="LeanbackPreference.PreferenceScreen">
     </style>
 
diff --git a/v17/preference-leanback/res/values/themes.xml b/v17/preference-leanback/res/values/themes.xml
index 591fdfa..d173b30 100644
--- a/v17/preference-leanback/res/values/themes.xml
+++ b/v17/preference-leanback/res/values/themes.xml
@@ -25,6 +25,7 @@
         <item name="preferenceInformationStyle">@style/LeanbackPreference.Information</item>
         <item name="checkBoxPreferenceStyle">@style/LeanbackPreference.CheckBoxPreference</item>
         <item name="switchPreferenceCompatStyle">@style/LeanbackPreference.SwitchPreferenceCompat</item>
+        <item name="seekBarPreferenceStyle">@style/LeanbackPreference.SeekBarPreference</item>
         <item name="switchPreferenceStyle">@style/LeanbackPreference.SwitchPreference</item>
         <item name="dialogPreferenceStyle">@style/LeanbackPreference.DialogPreference</item>
         <item name="editTextPreferenceStyle">@style/LeanbackPreference.DialogPreference.EditTextPreference</item>
diff --git a/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceFragment.java b/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceFragment.java
index 4d5df85..dbff1c8 100644
--- a/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceFragment.java
+++ b/v17/preference-leanback/src/android/support/v17/preference/LeanbackPreferenceFragment.java
@@ -61,7 +61,19 @@
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        final TextView decorTitle = (TextView) view.findViewById(R.id.decor_title);
-        decorTitle.setText(getPreferenceScreen().getTitle());
+        setTitle(getPreferenceScreen().getTitle());
+    }
+
+    /**
+     * Set the title to be shown above the preference list
+     * @param title Title text to be shown
+     */
+    public void setTitle(CharSequence title) {
+        final View view = getView();
+        final TextView decorTitle = view == null
+                ? null : (TextView) view.findViewById(R.id.decor_title);
+        if (decorTitle != null) {
+            decorTitle.setText(title);
+        }
     }
 }
diff --git a/v4/Android.mk b/v4/Android.mk
index d183f4d..a9c9145 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -24,14 +24,18 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v4
-LOCAL_SDK_VERSION := 9
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+# Some projects expect to inherit android-support-annotations from
+# android-support-v4, so we need to keep it static until they can be fixed.
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-compat \
     android-support-media-compat \
     android-support-core-utils \
     android-support-core-ui \
-    android-support-fragment
+    android-support-fragment \
+    android-support-annotations
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/appcompat/Android.mk b/v7/appcompat/Android.mk
index 21e9e8b..93baa95 100644
--- a/v7/appcompat/Android.mk
+++ b/v7/appcompat/Android.mk
@@ -31,7 +31,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-vectordrawable \
     android-support-animatedvectordrawable
-LOCAL_JAVA_LIBRARIES := android-support-v4
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-v4
 LOCAL_AAPT_FLAGS := --no-version-vectors
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 45ec21f..e6bb42b 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -3,6 +3,7 @@
 archivesBaseName = 'appcompat-v7'
 
 dependencies {
+    compile project(':support-annotations')
     compile project(':support-v4')
     compile project(':support-vector-drawable')
     compile project(':support-animated-vector-drawable')
diff --git a/v7/appcompat/res-public/values/public_styles.xml b/v7/appcompat/res-public/values/public_styles.xml
index 9e6df30..38efc46 100644
--- a/v7/appcompat/res-public/values/public_styles.xml
+++ b/v7/appcompat/res-public/values/public_styles.xml
@@ -54,6 +54,8 @@
     <public type="style" name="TextAppearance.AppCompat.Widget.ActionMode.Title"/>
     <public type="style" name="TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse"/>
     <public type="style" name="TextAppearance.AppCompat.Widget.Button"/>
+    <public type="style" name="TextAppearance.AppCompat.Widget.Button.Borderless.Colored"/>
+    <public type="style" name="TextAppearance.AppCompat.Widget.Button.Colored"/>
     <public type="style" name="TextAppearance.AppCompat.Widget.Button.Inverse"/>
     <public type="style" name="TextAppearance.AppCompat.Widget.DropDownItem"/>
     <public type="style" name="TextAppearance.AppCompat.Widget.PopupMenu.Header"/>
diff --git a/v7/appcompat/res/color-v23/abc_btn_colored_text_material.xml b/v7/appcompat/res/color-v23/abc_btn_colored_text_material.xml
new file mode 100644
index 0000000..74170d6
--- /dev/null
+++ b/v7/appcompat/res/color-v23/abc_btn_colored_text_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/textColorPrimary" />
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_btn_colored_text_material.xml b/v7/appcompat/res/color/abc_btn_colored_text_material.xml
new file mode 100644
index 0000000..897a3f7
--- /dev/null
+++ b/v7/appcompat/res/color/abc_btn_colored_text_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Used for the text of a bordered colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false"
+          app:alpha="?android:attr/disabledAlpha"
+          android:color="?android:attr/textColorPrimary" />
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/notification_media_action.xml b/v7/appcompat/res/layout-v11/notification_media_action.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_media_action.xml
rename to v7/appcompat/res/layout-v11/notification_media_action.xml
diff --git a/v7/appcompat/res/layout/notification_media_cancel_action.xml b/v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_media_cancel_action.xml
rename to v7/appcompat/res/layout-v11/notification_media_cancel_action.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media.xml b/v7/appcompat/res/layout-v11/notification_template_big_media.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_custom.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_custom.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_narrow.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_narrow.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_narrow.xml
diff --git a/v7/appcompat/res/layout/notification_template_big_media_narrow_custom.xml b/v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
similarity index 100%
rename from v7/appcompat/res/layout/notification_template_big_media_narrow_custom.xml
rename to v7/appcompat/res/layout-v11/notification_template_big_media_narrow_custom.xml
diff --git a/v7/appcompat/res/values-kn-rIN/strings.xml b/v7/appcompat/res/values-kn-rIN/strings.xml
index e240f96..ce95380 100644
--- a/v7/appcompat/res/values-kn-rIN/strings.xml
+++ b/v7/appcompat/res/values-kn-rIN/strings.xml
@@ -23,7 +23,7 @@
     <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"ಸಂಕುಚಿಸು"</string>
     <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
     <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
-    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ಹುಡುಕು"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ಹುಡುಕಿ"</string>
     <string name="abc_search_hint" msgid="7723749260725869598">"ಹುಡುಕಿ…"</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ಪ್ರಶ್ನೆಯನ್ನು ಹುಡುಕಿ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ಪ್ರಶ್ನೆಯನ್ನು ತೆರವುಗೊಳಿಸು"</string>
@@ -36,5 +36,5 @@
     <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"999+"</string>
     <string name="abc_capital_on" msgid="3405795526292276155">"ಆನ್"</string>
     <string name="abc_capital_off" msgid="121134116657445385">"ಆಫ್"</string>
-    <string name="search_menu_title" msgid="146198913615257606">"ಹುಡುಕು"</string>
+    <string name="search_menu_title" msgid="146198913615257606">"ಹುಡುಕಿ"</string>
 </resources>
diff --git a/v7/appcompat/res/values-my-rMM/strings.xml b/v7/appcompat/res/values-my-rMM/strings.xml
index 93f779d..0248c15 100644
--- a/v7/appcompat/res/values-my-rMM/strings.xml
+++ b/v7/appcompat/res/values-my-rMM/strings.xml
@@ -27,7 +27,7 @@
     <string name="abc_search_hint" msgid="7723749260725869598">"ရှာဖွေပါ..."</string>
     <string name="abc_searchview_description_query" msgid="2550479030709304392">"ရှာစရာ အချက်အလက်နေရာ"</string>
     <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ရှာစရာ အချက်အလက်များ ဖယ်ရှားရန်"</string>
-    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ရှာဖွေစရာ အချက်အလက်ကို အတည်ပြုရန်"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ရှာဖွေစရာ အချက်အလက်ကို ပေးပို့ရန်"</string>
     <string name="abc_searchview_description_voice" msgid="893419373245838918">"အသံဖြင့် ရှာဖွေခြင်း"</string>
     <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"အက်ပ်တစ်ခုခုကို ရွေးချယ်ပါ"</string>
     <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"အားလုံးကို ကြည့်ရန်"</string>
diff --git a/documents-archive/AndroidManifest.xml b/v7/appcompat/res/values-v24/styles_base_text.xml
similarity index 61%
copy from documents-archive/AndroidManifest.xml
copy to v7/appcompat/res/values-v24/styles_base_text.xml
index 2cd0f7a..2e6182d 100644
--- a/documents-archive/AndroidManifest.xml
+++ b/v7/appcompat/res/values-v24/styles_base_text.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!--
+     Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.provider">
-    <uses-sdk android:minSdkVersion="19"/>
-    <application />
-</manifest>
+<resources>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.Button.Colored" parent="android:TextAppearance.Material.Widget.Button.Colored" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.Button.Borderless.Colored" parent="android:TextAppearance.Material.Widget.Button.Borderless.Colored" />
+
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/styles.xml b/v7/appcompat/res/values/styles.xml
index ec8bbe1..7595d74 100644
--- a/v7/appcompat/res/values/styles.xml
+++ b/v7/appcompat/res/values/styles.xml
@@ -298,6 +298,10 @@
 
     <style name="TextAppearance.AppCompat.Widget.TextView.SpinnerItem" parent="Base.TextAppearance.AppCompat.Widget.TextView.SpinnerItem" />
 
+    <style name="TextAppearance.AppCompat.Widget.Button.Colored" parent="Base.TextAppearance.AppCompat.Widget.Button.Colored" />
+
+    <style name="TextAppearance.AppCompat.Widget.Button.Borderless.Colored" parent="Base.TextAppearance.AppCompat.Widget.Button.Borderless.Colored" />
+
     <!--
          The following themes are deprecated.
     -->
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index b1d6c95..b8c1f29 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -453,7 +453,7 @@
     <!-- Colored bordered ink button -->
     <style name="Base.Widget.AppCompat.Button.Colored">
         <item name="android:background">@drawable/abc_btn_colored_material</item>
-        <item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.Button.Inverse</item>
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.Button.Colored</item>
     </style>
 
     <!-- Borderless ink button -->
@@ -463,7 +463,7 @@
 
     <!-- Colored borderless ink button -->
     <style name="Base.Widget.AppCompat.Button.Borderless.Colored">
-        <item name="android:textColor">@color/abc_btn_colored_borderless_text_material</item>
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat.Widget.Button.Borderless.Colored</item>
     </style>
 
     <style name="Base.Widget.AppCompat.Button.ButtonBar.AlertDialog" parent="Widget.AppCompat.Button.Borderless.Colored">
diff --git a/v7/appcompat/res/values/styles_base_text.xml b/v7/appcompat/res/values/styles_base_text.xml
index 8597179..6a43144 100644
--- a/v7/appcompat/res/values/styles_base_text.xml
+++ b/v7/appcompat/res/values/styles_base_text.xml
@@ -139,4 +139,12 @@
         <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
     </style>
 
+    <style name="Base.TextAppearance.AppCompat.Widget.Button.Colored">
+        <item name="android:textColor">@color/abc_btn_colored_text_material</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.Button.Borderless.Colored" parent="Base.TextAppearance.AppCompat.Widget.Button">
+        <item name="android:textColor">@color/abc_btn_colored_borderless_text_material</item>
+    </style>
+
 </resources>
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
index 3db19dc..1fe51b8 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggle.java
@@ -15,6 +15,7 @@
  */
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.StringRes;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
@@ -207,6 +209,8 @@
             mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             mActivityImpl = new JellybeanMr2Delegate(activity);
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            mActivityImpl = new IcsDelegate(activity);
         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
             mActivityImpl = new HoneycombDelegate(activity);
         } else {
@@ -492,8 +496,10 @@
     }
 
     /**
-     * Delegate if SDK version is between honeycomb and JBMR2
+     * Delegate if SDK version is between Honeycomb and ICS
      */
+    @RequiresApi(11)
+    @TargetApi(11)
     private static class HoneycombDelegate implements Delegate {
 
         final Activity mActivity;
@@ -510,14 +516,7 @@
 
         @Override
         public Context getActionBarThemedContext() {
-            final ActionBar actionBar = mActivity.getActionBar();
-            final Context context;
-            if (actionBar != null) {
-                context = actionBar.getThemedContext();
-            } else {
-                context = mActivity;
-            }
-            return context;
+            return mActivity;
         }
 
         @Override
@@ -546,8 +545,34 @@
     }
 
     /**
+     * Delegate if SDK version is between ICS and JBMR2
+     */
+    @RequiresApi(14)
+    @TargetApi(14)
+    private static class IcsDelegate extends HoneycombDelegate {
+
+        IcsDelegate(Activity activity) {
+            super(activity);
+        }
+
+        @Override
+        public Context getActionBarThemedContext() {
+            final ActionBar actionBar = mActivity.getActionBar();
+            final Context context;
+            if (actionBar != null) {
+                context = actionBar.getThemedContext();
+            } else {
+                context = mActivity;
+            }
+            return context;
+        }
+    }
+
+    /**
      * Delegate if SDK version is JB MR2 or newer
      */
+    @RequiresApi(18)
+    @TargetApi(18)
     private static class JellybeanMr2Delegate implements Delegate {
 
         final Activity mActivity;
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
index f25365e..1463ecb 100644
--- a/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
+++ b/v7/appcompat/src/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
@@ -18,11 +18,13 @@
 package android.support.v7.app;
 
 import android.R;
+import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,6 +41,8 @@
  *
  * Moved from Support-v4
  */
+@RequiresApi(11)
+@TargetApi(11)
 class ActionBarDrawerToggleHoneycomb {
     private static final String TAG = "ActionBarDrawerToggleHoneycomb";
 
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
index b26f3df..6da5250 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplBase.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
@@ -23,6 +24,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.support.v7.appcompat.R;
 import android.support.v7.view.ActionMode;
 import android.support.v7.view.SupportMenuInflater;
@@ -36,6 +38,8 @@
 import android.view.View;
 import android.view.Window;
 
+@RequiresApi(9)
+@TargetApi(9)
 abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
 
     static final boolean DEBUG = false;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
index 8313e49..9f162dd 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplN.java
@@ -16,13 +16,17 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.Window;
 
 import java.util.List;
 
+@RequiresApi(24)
+@TargetApi(24)
 class AppCompatDelegateImplN extends AppCompatDelegateImplV23 {
 
     AppCompatDelegateImplN(Context context, Window window, AppCompatCallback callback) {
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
index 9d882c8..f3fda8e 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV11.java
@@ -16,11 +16,15 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.Window;
 
+@RequiresApi(11)
+@TargetApi(11)
 class AppCompatDelegateImplV11 extends AppCompatDelegateImplV9 {
 
     AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
@@ -28,8 +32,13 @@
     }
 
     @Override
+    public boolean hasWindowFeature(int featureId) {
+        return super.hasWindowFeature(featureId) || mWindow.hasFeature(featureId);
+    }
+
+    @Override
     View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        // On Honeycomb+, Activity's private inflater factory will handle calling it's
+        // On Honeycomb+, Activity's private inflater factory will handle calling its
         // onCreateView(...)
         return null;
     }
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
index 631fc35..70734c9 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV14.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -28,6 +29,7 @@
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.VisibleForTesting;
 import android.support.v7.view.SupportActionModeWrapper;
 import android.util.DisplayMetrics;
@@ -35,6 +37,8 @@
 import android.view.ActionMode;
 import android.view.Window;
 
+@RequiresApi(14)
+@TargetApi(14)
 class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 {
 
     private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode";
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
index 5b3beec..d061114 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV23.java
@@ -16,11 +16,15 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.UiModeManager;
 import android.content.Context;
+import android.support.annotation.RequiresApi;
 import android.view.ActionMode;
 import android.view.Window;
 
+@RequiresApi(23)
+@TargetApi(23)
 class AppCompatDelegateImplV23 extends AppCompatDelegateImplV14 {
 
     private final UiModeManager mUiModeManager;
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
index 9491922..b52c0ba 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV9.java
@@ -20,6 +20,7 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.Window.FEATURE_OPTIONS_PANEL;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Dialog;
 import android.content.Context;
@@ -36,6 +37,7 @@
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.NavUtils;
 import android.support.v4.os.ParcelableCompat;
 import android.support.v4.os.ParcelableCompatCreatorCallbacks;
@@ -90,6 +92,8 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
+@RequiresApi(9)
+@TargetApi(9)
 class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
         implements MenuBuilder.Callback, LayoutInflaterFactory {
 
@@ -212,7 +216,7 @@
 
         if (toolbar != null) {
             final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
-                    ((Activity) mContext).getTitle(), mAppCompatWindowCallback);
+                    ((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback);
             mActionBar = tbab;
             mWindow.setCallback(tbab.getWrappedWindowCallback());
         } else {
@@ -619,7 +623,7 @@
             case Window.FEATURE_NO_TITLE:
                 return mWindowNoTitle;
         }
-        return mWindow.hasFeature(featureId);
+        return false;
     }
 
     @Override
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
index 9d8cf07..d1c3f45 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
@@ -18,6 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -27,6 +28,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
@@ -82,6 +84,8 @@
         return null;
     }
 
+    @RequiresApi(24)
+    @TargetApi(24)
     private static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder,
             android.support.v4.app.NotificationCompat.Builder b) {
         if (b.mStyle instanceof DecoratedCustomViewStyle) {
@@ -93,6 +97,8 @@
         }
     }
 
+    @RequiresApi(21)
+    @TargetApi(21)
     private static RemoteViews addStyleGetContentViewLollipop(
             NotificationBuilderWithBuilderAccessor builder,
             android.support.v4.app.NotificationCompat.Builder b) {
@@ -131,6 +137,8 @@
         return addStyleGetContentViewJellybean(builder, b);
     }
 
+    @RequiresApi(16)
+    @TargetApi(16)
     private static RemoteViews addStyleGetContentViewJellybean(
             NotificationBuilderWithBuilderAccessor builder,
             android.support.v4.app.NotificationCompat.Builder b) {
@@ -217,6 +225,8 @@
         return false;
     }
 
+    @RequiresApi(14)
+    @TargetApi(14)
     private static RemoteViews addStyleGetContentViewIcs(
             NotificationBuilderWithBuilderAccessor builder,
             android.support.v4.app.NotificationCompat.Builder b) {
@@ -240,6 +250,8 @@
         return null;
     }
 
+    @RequiresApi(16)
+    @TargetApi(16)
     private static void addBigStyleToBuilderJellybean(Notification n,
             android.support.v4.app.NotificationCompat.Builder b) {
         if (b.mStyle instanceof MediaStyle) {
@@ -259,7 +271,7 @@
                         innerView);
             }
         } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedBigStyleToBuilder(n, b);
+            addDecoratedBigStyleToBuilderJellybean(n, b);
         }
     }
 
@@ -279,7 +291,9 @@
         return remoteViews;
     }
 
-    private static void addDecoratedBigStyleToBuilder(Notification n,
+    @RequiresApi(16)
+    @TargetApi(16)
+    private static void addDecoratedBigStyleToBuilderJellybean(Notification n,
             android.support.v4.app.NotificationCompat.Builder b) {
         RemoteViews bigContentView = b.getBigContentView();
         RemoteViews innerView = bigContentView != null ? bigContentView : b.getContentView();
@@ -296,7 +310,9 @@
         n.bigContentView = remoteViews;
     }
 
-    private static void addDecoratedHeadsUpToBuilder(Notification n,
+    @RequiresApi(21)
+    @TargetApi(21)
+    private static void addDecoratedHeadsUpToBuilderLollipop(Notification n,
             android.support.v4.app.NotificationCompat.Builder b) {
         RemoteViews headsUp = b.getHeadsUpContentView();
         RemoteViews innerView = headsUp != null ? headsUp : b.getContentView();
@@ -313,6 +329,8 @@
         n.headsUpContentView = remoteViews;
     }
 
+    @RequiresApi(21)
+    @TargetApi(21)
     private static void addBigStyleToBuilderLollipop(Notification n,
             android.support.v4.app.NotificationCompat.Builder b) {
         RemoteViews innerView = b.getBigContentView() != null
@@ -328,7 +346,7 @@
                             innerView);
             setBackgroundColor(b.mContext, n.bigContentView, b.getColor());
         } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedBigStyleToBuilder(n, b);
+            addDecoratedBigStyleToBuilderJellybean(n, b);
         }
     }
 
@@ -340,6 +358,8 @@
         views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color);
     }
 
+    @RequiresApi(21)
+    @TargetApi(21)
     private static void addHeadsUpToBuilderLollipop(Notification n,
             android.support.v4.app.NotificationCompat.Builder b) {
         RemoteViews innerView = b.getHeadsUpContentView() != null
@@ -355,7 +375,7 @@
                     innerView);
             setBackgroundColor(b.mContext, n.headsUpContentView, b.getColor());
         } else if (b.mStyle instanceof DecoratedCustomViewStyle) {
-            addDecoratedHeadsUpToBuilder(n, b);
+            addDecoratedHeadsUpToBuilderLollipop(n, b);
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
index cfa8839..9b0f028 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl21.java
@@ -16,10 +16,14 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.media.session.MediaSession;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
 
+@RequiresApi(21)
+@TargetApi(21)
 class NotificationCompatImpl21 {
 
     public static void addMediaStyle(NotificationBuilderWithBuilderAccessor b,
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
index 0547ad4..a65751b 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImpl24.java
@@ -16,9 +16,13 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
 
+@RequiresApi(24)
+@TargetApi(24)
 class NotificationCompatImpl24 {
 
     public static void addDecoratedCustomViewStyle(NotificationBuilderWithBuilderAccessor b) {
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
index a37c921..a6e73ef 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplBase.java
@@ -16,6 +16,7 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -28,6 +29,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationCompatBase;
@@ -44,12 +46,16 @@
  * Helper class to generate MediaStyle notifications for pre-Lollipop platforms. Overrides
  * contentView and bigContentView of the notification.
  */
+@RequiresApi(9)
+@TargetApi(9)
 class NotificationCompatImplBase {
 
     static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 3;
     static final int MAX_MEDIA_BUTTONS = 5;
     private static final int MAX_ACTION_BUTTONS = 3;
 
+    @RequiresApi(11)
+    @TargetApi(11)
     public static <T extends NotificationCompatBase.Action> RemoteViews overrideContentViewMedia(
             NotificationBuilderWithBuilderAccessor builder,
             Context context, CharSequence contentTitle, CharSequence contentText,
@@ -68,6 +74,8 @@
         return views;
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     private static <T extends NotificationCompatBase.Action> RemoteViews generateContentViewMedia(
             Context context, CharSequence contentTitle, CharSequence contentText,
             CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -112,6 +120,8 @@
         return view;
     }
 
+    @RequiresApi(16)
+    @TargetApi(16)
     public static <T extends NotificationCompatBase.Action> void overrideMediaBigContentView(
             Notification n, Context context, CharSequence contentTitle, CharSequence contentText,
             CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -119,13 +129,15 @@
             boolean showCancelButton, PendingIntent cancelButtonIntent,
             boolean decoratedCustomView) {
         n.bigContentView = generateMediaBigView(context, contentTitle, contentText, contentInfo,
-                number, largeIcon, subText, useChronometer, when, priority, color,
-                actions, showCancelButton, cancelButtonIntent, decoratedCustomView);
+                number, largeIcon, subText, useChronometer, when, priority, color, actions,
+                showCancelButton, cancelButtonIntent, decoratedCustomView);
         if (showCancelButton) {
             n.flags |= Notification.FLAG_ONGOING_EVENT;
         }
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     public static <T extends NotificationCompatBase.Action> RemoteViews generateMediaBigView(
             Context context, CharSequence contentTitle, CharSequence contentText,
             CharSequence contentInfo, int number, Bitmap largeIcon, CharSequence subText,
@@ -156,6 +168,8 @@
         return big;
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     private static RemoteViews generateMediaActionButton(Context context,
             NotificationCompatBase.Action action) {
         final boolean tombstone = (action.getActionIntent() == null);
@@ -165,12 +179,14 @@
         if (!tombstone) {
             button.setOnClickPendingIntent(R.id.action0, action.getActionIntent());
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+        if (Build.VERSION.SDK_INT >= 15) {
             button.setContentDescription(R.id.action0, action.getTitle());
         }
         return button;
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     private static int getBigMediaLayoutResource(boolean decoratedCustomView, int actionCount) {
         if (actionCount <= 3) {
             return decoratedCustomView
@@ -223,7 +239,9 @@
         if (!tombstone) {
             button.setOnClickPendingIntent(R.id.action_container, action.actionIntent);
         }
-        button.setContentDescription(R.id.action_container, action.title);
+        if (Build.VERSION.SDK_INT >= 15) {
+            button.setContentDescription(R.id.action_container, action.title);
+        }
         return button;
     }
 
@@ -265,9 +283,7 @@
         boolean showLine2 = false;
 
         boolean minPriority = priority < NotificationCompat.PRIORITY_LOW;
-        boolean afterJellyBean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
-        boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
-        if (afterJellyBean && !afterLollipop) {
+        if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 21) {
             // lets color the backgrounds
             if (minPriority) {
                 contentView.setInt(R.id.notification_background,
@@ -285,7 +301,7 @@
         if (largeIcon != null) {
             // On versions before Jellybean, the large icon was shown by SystemUI, so we need to hide
             // it here.
-            if (afterJellyBean) {
+            if (Build.VERSION.SDK_INT >= 16) {
                 contentView.setViewVisibility(R.id.icon, View.VISIBLE);
                 contentView.setImageViewBitmap(R.id.icon, largeIcon);
             } else {
@@ -296,7 +312,7 @@
                         R.dimen.notification_right_icon_size);
                 int iconSize = backgroundSize - res.getDimensionPixelSize(
                         R.dimen.notification_small_icon_background_padding) * 2;
-                if (afterLollipop) {
+                if (Build.VERSION.SDK_INT >= 21) {
                     Bitmap smallBit = createIconWithBackground(context,
                             smallIcon,
                             backgroundSize,
@@ -311,7 +327,7 @@
             }
         } else if (smallIcon != 0) { // small icon at left
             contentView.setViewVisibility(R.id.icon, View.VISIBLE);
-            if (afterLollipop) {
+            if (Build.VERSION.SDK_INT >= 21) {
                 int backgroundSize = res.getDimensionPixelSize(
                         R.dimen.notification_large_icon_width)
                         - res.getDimensionPixelSize(R.dimen.notification_big_circle_margin);
@@ -336,7 +352,7 @@
             showLine3 = true;
         }
         // If there is a large icon we have a right side
-        boolean hasRightSide = !afterLollipop && largeIcon != null;
+        boolean hasRightSide = !(Build.VERSION.SDK_INT >= 21) && largeIcon != null;
         if (contentInfo != null) {
             contentView.setTextViewText(R.id.info, contentInfo);
             contentView.setViewVisibility(R.id.info, View.VISIBLE);
@@ -360,7 +376,7 @@
         }
 
         // Need to show three lines? Only allow on Jellybean+
-        if (subText != null && afterJellyBean) {
+        if (subText != null && Build.VERSION.SDK_INT >= 16) {
             contentView.setTextViewText(R.id.text, subText);
             if (contentText != null) {
                 contentView.setTextViewText(R.id.text2, contentText);
@@ -372,7 +388,7 @@
         }
 
         // RemoteViews.setViewPadding and RemoteViews.setTextViewTextSize is not available on ICS-
-        if (showLine2 && afterJellyBean) {
+        if (showLine2 && Build.VERSION.SDK_INT >= 16) {
             if (fitIn1U) {
                 // need to shrink all the type to make sure everything fits
                 final float subTextSize = res.getDimensionPixelSize(
@@ -384,7 +400,7 @@
         }
 
         if (when != 0) {
-            if (useChronometer && afterJellyBean) {
+            if (useChronometer && Build.VERSION.SDK_INT >= 16) {
                 contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
                 contentView.setLong(R.id.chronometer, "setBase",
                         when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
index cf8d128..b600d43 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompatImplJellybean.java
@@ -16,9 +16,13 @@
 
 package android.support.v7.app;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.NotificationBuilderWithBuilderAccessor;
 
+@RequiresApi(16)
+@TargetApi(16)
 class NotificationCompatImplJellybean {
 
     public static void addBigTextStyle(NotificationBuilderWithBuilderAccessor b,
diff --git a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
index 8841d12..1fd0e61 100644
--- a/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/ContextThemeWrapper.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StyleRes;
@@ -111,5 +112,11 @@
         }
         onApplyThemeResource(mTheme, mThemeResource, first);
     }
+
+    @Override
+    public AssetManager getAssets() {
+        // Ensure we're returning assets with the correct configuration.
+        return getResources().getAssets();
+    }
 }
 
diff --git a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
index 0225400..b05de9a 100644
--- a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.view;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.view.ActionMode;
 import android.view.KeyEvent;
@@ -57,6 +59,8 @@
         return mWrapped.dispatchKeyEvent(event);
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
         return mWrapped.dispatchKeyShortcutEvent(event);
@@ -72,6 +76,8 @@
         return mWrapped.dispatchTrackballEvent(event);
     }
 
+    @RequiresApi(12)
+    @TargetApi(12)
     @Override
     public boolean dispatchGenericMotionEvent(MotionEvent event) {
         return mWrapped.dispatchGenericMotionEvent(event);
@@ -137,6 +143,8 @@
         mWrapped.onPanelClosed(featureId, menu);
     }
 
+    @RequiresApi(23)
+    @TargetApi(23)
     @Override
     public boolean onSearchRequested(SearchEvent searchEvent) {
         return mWrapped.onSearchRequested(searchEvent);
@@ -147,26 +155,36 @@
         return mWrapped.onSearchRequested();
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
         return mWrapped.onWindowStartingActionMode(callback);
     }
 
+    @RequiresApi(23)
+    @TargetApi(23)
     @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
         return mWrapped.onWindowStartingActionMode(callback, type);
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public void onActionModeStarted(ActionMode mode) {
         mWrapped.onActionModeStarted(mode);
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public void onActionModeFinished(ActionMode mode) {
         mWrapped.onActionModeFinished(mode);
     }
 
+    @RequiresApi(24)
+    @TargetApi(24)
     @Override
     public void onProvideKeyboardShortcuts(
             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
index 1a9e4e2..649b253 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
@@ -42,7 +43,8 @@
  * @hide
  */
 @RestrictTo(GROUP_ID)
-@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+@TargetApi(14)
+@RequiresApi(14)
 public class MenuItemWrapperICS extends BaseMenuWrapper<SupportMenuItem> implements MenuItem {
     static final String LOG_TAG = "MenuItemWrapper";
 
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
index bdcfc71..fd6c141 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperJB.java
@@ -19,6 +19,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
@@ -32,7 +33,8 @@
  * @hide
  */
 @RestrictTo(GROUP_ID)
-@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+@TargetApi(16)
+@RequiresApi(16)
 class MenuItemWrapperJB extends MenuItemWrapperICS {
 
     MenuItemWrapperJB(Context context, SupportMenuItem object) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
index 9bf58fa..25208af 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuWrapperICS.java
@@ -16,9 +16,11 @@
 
 package android.support.v7.view.menu;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.support.annotation.RequiresApi;
 import android.support.v4.internal.view.SupportMenu;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -28,6 +30,8 @@
 /**
  * Wraps a support {@link SupportMenu} as a framework {@link android.view.Menu}
  */
+@TargetApi(14)
+@RequiresApi(14)
 class MenuWrapperICS extends BaseMenuWrapper<SupportMenu> implements Menu {
 
     MenuWrapperICS(Context context, SupportMenu object) {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
index 185d443..4952d5d 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/SubMenuWrapperICS.java
@@ -16,8 +16,10 @@
 
 package android.support.v7.view.menu;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportSubMenu;
 import android.view.MenuItem;
@@ -31,6 +33,8 @@
  * @hide
  */
 @RestrictTo(GROUP_ID)
+@RequiresApi(14)
+@TargetApi(14)
 class SubMenuWrapperICS extends MenuWrapperICS implements SubMenu {
 
     SubMenuWrapperICS(Context context, SupportSubMenu subMenu) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
index b2fa191..d8b0f2d 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawable.java
@@ -16,10 +16,14 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(9)
+@TargetApi(9)
 class ActionBarBackgroundDrawable extends Drawable {
 
     final ActionBarContainer mContainer;
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
index 0b05fe7..56f2a09 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
@@ -16,9 +16,13 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Outline;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(21)
+@TargetApi(21)
 class ActionBarBackgroundDrawableV21 extends ActionBarBackgroundDrawable {
 
     public ActionBarBackgroundDrawableV21(ActionBarContainer container) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
index af5ff2d..613be53 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -16,12 +16,14 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.TintableBackgroundView;
 import android.support.v7.appcompat.R;
@@ -168,6 +170,8 @@
         event.setClassName(Button.class.getName());
     }
 
+    @RequiresApi(14)
+    @TargetApi(14)
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index b51df4b..1e943e9 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -23,6 +23,7 @@
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -37,6 +38,7 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
 import android.support.graphics.drawable.VectorDrawableCompat;
@@ -87,13 +89,12 @@
     }
 
     private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) {
-        final int sdk = Build.VERSION.SDK_INT;
         // This sdk version check will affect src:appCompat code path.
         // Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use the
         // VectorDrawableCompat before Nougat to utilize the bug fixes in VectorDrawableCompat.
-        if (sdk < 24) {
+        if (Build.VERSION.SDK_INT < 24) {
             manager.addDelegate("vector", new VdcInflateDelegate());
-            if (sdk >= 11) {
+            if (Build.VERSION.SDK_INT >= 11) {
                 // AnimatedVectorDrawableCompat only works on API v11+
                 manager.addDelegate("animated-vector", new AvdcInflateDelegate());
             }
@@ -750,6 +751,8 @@
         }
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     private static class AvdcInflateDelegate implements InflateDelegate {
         AvdcInflateDelegate() {
         }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
index 7053c39..bac1cb8 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBar.java
@@ -16,8 +16,10 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.support.annotation.RequiresApi;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.widget.SeekBar;
@@ -59,6 +61,8 @@
         mAppCompatSeekBarHelper.drawableStateChanged();
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
index 1d84401..93e1851 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSeekBarHelper.java
@@ -16,11 +16,13 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.appcompat.R;
@@ -140,6 +142,8 @@
         }
     }
 
+    @RequiresApi(11)
+    @TargetApi(11)
     void jumpDrawablesToCurrentState() {
         if (mTickMark != null) {
             mTickMark.jumpToCurrentState();
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
index cb026ed..beb4b73 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatSpinner.java
@@ -67,9 +67,6 @@
  */
 public class AppCompatSpinner extends Spinner implements TintableBackgroundView {
 
-    static final boolean IS_AT_LEAST_M = Build.VERSION.SDK_INT >= 23;
-    private static final boolean IS_AT_LEAST_JB = Build.VERSION.SDK_INT >= 16;
-
     private static final int[] ATTRS_ANDROID_SPINNERMODE = {android.R.attr.spinnerMode};
 
     private static final int MAX_ITEMS_MEASURED = 15;
@@ -211,7 +208,7 @@
             } else {
                 // If we're running on a < M device, we'll use the current context and still handle
                 // any dropdown popup
-                mPopupContext = !IS_AT_LEAST_M ? context : null;
+                mPopupContext = !(Build.VERSION.SDK_INT >= 23) ? context : null;
             }
         }
 
@@ -296,7 +293,7 @@
     public Context getPopupContext() {
         if (mPopup != null) {
             return mPopupContext;
-        } else if (IS_AT_LEAST_M) {
+        } else if (Build.VERSION.SDK_INT >= 23) {
             return super.getPopupContext();
         }
         return null;
@@ -305,7 +302,7 @@
     public void setPopupBackgroundDrawable(Drawable background) {
         if (mPopup != null) {
             mPopup.setBackgroundDrawable(background);
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             super.setPopupBackgroundDrawable(background);
         }
     }
@@ -317,7 +314,7 @@
     public Drawable getPopupBackground() {
         if (mPopup != null) {
             return mPopup.getBackground();
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             return super.getPopupBackground();
         }
         return null;
@@ -326,7 +323,7 @@
     public void setDropDownVerticalOffset(int pixels) {
         if (mPopup != null) {
             mPopup.setVerticalOffset(pixels);
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             super.setDropDownVerticalOffset(pixels);
         }
     }
@@ -334,7 +331,7 @@
     public int getDropDownVerticalOffset() {
         if (mPopup != null) {
             return mPopup.getVerticalOffset();
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             return super.getDropDownVerticalOffset();
         }
         return 0;
@@ -343,7 +340,7 @@
     public void setDropDownHorizontalOffset(int pixels) {
         if (mPopup != null) {
             mPopup.setHorizontalOffset(pixels);
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             super.setDropDownHorizontalOffset(pixels);
         }
     }
@@ -357,7 +354,7 @@
     public int getDropDownHorizontalOffset() {
         if (mPopup != null) {
             return mPopup.getHorizontalOffset();
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             return super.getDropDownHorizontalOffset();
         }
         return 0;
@@ -366,7 +363,7 @@
     public void setDropDownWidth(int pixels) {
         if (mPopup != null) {
             mDropDownWidth = pixels;
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             super.setDropDownWidth(pixels);
         }
     }
@@ -374,7 +371,7 @@
     public int getDropDownWidth() {
         if (mPopup != null) {
             return mDropDownWidth;
-        } else if (IS_AT_LEAST_JB) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             return super.getDropDownWidth();
         }
         return 0;
@@ -607,7 +604,8 @@
             }
 
             if (dropDownTheme != null) {
-                 if (IS_AT_LEAST_M && adapter instanceof android.widget.ThemedSpinnerAdapter) {
+                 if (Build.VERSION.SDK_INT >= 23
+                         && adapter instanceof android.widget.ThemedSpinnerAdapter) {
                     final android.widget.ThemedSpinnerAdapter themedAdapter =
                             (android.widget.ThemedSpinnerAdapter) adapter;
                     if (themedAdapter.getDropDownViewTheme() != dropDownTheme) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
index 01c9849..c210036 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelper.java
@@ -16,16 +16,20 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v7.appcompat.R;
 import android.support.v7.text.AllCapsTransformationMethod;
 import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+@RequiresApi(9)
+@TargetApi(9)
 class AppCompatTextHelper {
 
     static AppCompatTextHelper create(TextView textView) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
index c8597fb..e022756 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextHelperV17.java
@@ -16,13 +16,17 @@
 
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+@RequiresApi(17)
+@TargetApi(17)
 class AppCompatTextHelperV17 extends AppCompatTextHelper {
     private TintInfo mDrawableStartTint;
     private TintInfo mDrawableEndTint;
diff --git a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
index 9de02b2..91c0fd7 100644
--- a/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
+++ b/v7/appcompat/src/android/support/v7/widget/ForwardingListener.java
@@ -16,8 +16,12 @@
 
 package android.support.v7.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
@@ -29,9 +33,6 @@
 import android.view.ViewParent;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
-
 /**
  * Abstract class that forwards touch events to a {@link ShowableListMenu}.
  *
@@ -86,6 +87,8 @@
         mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
     }
 
+    @RequiresApi(12)
+    @TargetApi(12)
     private void addDetachListenerApi12(View src) {
         src.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
             @Override
diff --git a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
index 5040bba..8202d45 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Build;
 import android.support.annotation.NonNull;
@@ -119,4 +120,10 @@
     public Resources getResources() {
         return mResources;
     }
+
+    @Override
+    public AssetManager getAssets() {
+        // Ensure we're returning assets with the correct configuration.
+        return mResources.getAssets();
+    }
 }
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index c69f147..01b28a8 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -2194,7 +2194,7 @@
      * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
      * ActionBar API. See
      * {@link android.support.v7.app.AppCompatActivity#setSupportActionBar(Toolbar)
-     * ActionBarActivity.setActionBar}
+     * AppCompatActivity.setSupportActionBar}
      * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
      */
     public static class LayoutParams extends ActionBar.LayoutParams {
diff --git a/v7/appcompat/tests/AndroidManifest.xml b/v7/appcompat/tests/AndroidManifest.xml
index a667c50..7385a5f 100644
--- a/v7/appcompat/tests/AndroidManifest.xml
+++ b/v7/appcompat/tests/AndroidManifest.xml
@@ -34,9 +34,9 @@
                 android:name="android.support.v7.app.AppCompatActivity"/>
 
         <activity
-                android:name="android.support.v7.app.WindowDecorActionBarActivity"/>
+                android:name="android.support.v7.app.WindowDecorAppCompatActivity"/>
         <activity
-                android:name="android.support.v7.app.ToolbarActionBarActivity"
+                android:name="android.support.v7.app.ToolbarAppCompatActivity"
                 android:theme="@style/Theme.AppCompat.NoActionBar"/>
 
         <activity
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
index 50346b9..4d7e07e 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
@@ -15,43 +15,50 @@
  */
 package android.support.v7.app;
 
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteCursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.DataInteraction;
-import android.support.v7.appcompat.test.R;
-import android.support.v7.testutils.TestUtilsMatchers;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import android.widget.Button;
-import android.widget.CheckedTextView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import org.hamcrest.Matcher;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
 import static android.support.test.espresso.Espresso.onData;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.RootMatchers.isDialog;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.core.AllOf.allOf;
 import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.espresso.DataInteraction;
+import android.support.test.filters.SmallTest;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.TestUtilsMatchers;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
 
 public class AlertDialogCursorTest
         extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
@@ -117,11 +124,11 @@
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Throwable {
         if (mCursor != null) {
             // Close the cursor on the UI thread as the list view in the alert dialog
             // will get notified of any change to the underlying cursor.
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            mActivityTestRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mCursor.close();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
index 6a9d8c1..8039d58 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogTest.java
@@ -15,6 +15,38 @@
  */
 package android.support.v7.app;
 
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.PositionAssertions.isBelow;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.LayoutMatchers.hasEllipsizedText;
+import static android.support.test.espresso.matcher.RootMatchers.isDialog;
+import static android.support.test.espresso.matcher.ViewMatchers.hasSibling;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.drawable.ColorDrawable;
@@ -22,14 +54,13 @@
 import android.os.Message;
 import android.support.annotation.ColorInt;
 import android.support.annotation.StringRes;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.ViewInteraction;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
@@ -40,27 +71,12 @@
 import android.widget.ImageView;
 import android.widget.ListAdapter;
 import android.widget.ListView;
+
 import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import static android.support.test.espresso.Espresso.onData;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.PositionAssertions.isBelow;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.LayoutMatchers.hasEllipsizedText;
-import static android.support.test.espresso.matcher.RootMatchers.isDialog;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.core.AllOf.allOf;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-
 /**
  * Tests in this class make a few assumptions about the underlying implementation of
  * <code>AlertDialog</code>. While the assumptions don't go all the way down to individual
@@ -162,7 +178,7 @@
 
     @Test
     @SmallTest
-    public void testMessageStringPostCreation() {
+    public void testMessageStringPostCreation() throws Throwable {
         final String dialogInitialMessage = "Initial message";
         final String dialogUpdatedMessage = "Updated message";
         AlertDialog.Builder builder = new AlertDialog.Builder(mActivityTestRule.getActivity())
@@ -175,7 +191,7 @@
         onView(withText(dialogInitialMessage)).inRoot(isDialog()).check(matches(isDisplayed()));
 
         // Update the dialog message
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setMessage(dialogUpdatedMessage);
@@ -733,7 +749,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(R.drawable.test_drawable_green);
@@ -764,7 +780,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(R.drawable.test_drawable_green);
@@ -796,7 +812,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(0);
@@ -849,7 +865,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(new TestDrawable(0xFF503090, 40, 40));
@@ -880,7 +896,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(new TestDrawable(0xFF503090, 40, 40));
@@ -912,7 +928,7 @@
         Thread.sleep(1000);
 
         // Change the icon
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAlertDialog.setIcon(null);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index 1ecff78..689bf49 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -35,15 +35,14 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import android.os.Build;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.FitWindowsContentLayout;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.testutils.TestUtils;
 import android.support.v7.view.ActionMode;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.Menu;
 import android.view.View;
 import android.view.WindowInsets;
@@ -68,17 +67,13 @@
         assertEquals(getActivity().getTitle(), getActivity().getSupportActionBar().getTitle());
     }
 
+    @UiThreadTest
     @Test
-    public void testSetActionBarTitle() throws Throwable {
+    public void testSetActionBarTitle() {
         final String newTitle = "hello";
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getActivity().setTitle(newTitle);
-                assertEquals("New title is set to ActionBar",
-                        newTitle, getActivity().getSupportActionBar().getTitle());
-            }
-        });
+        mActivityTestRule.getActivity().setTitle(newTitle);
+        assertEquals("New title is set to ActionBar",
+                newTitle, mActivityTestRule.getActivity().getSupportActionBar().getTitle());
     }
 
     @Test
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
index 76ca5dc..c98df27 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseInstrumentationTestCase.java
@@ -16,15 +16,15 @@
 
 package android.support.v7.app;
 
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 public abstract class BaseInstrumentationTestCase<A extends Activity> {
 
@@ -44,22 +44,4 @@
     public Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
-
-    @Deprecated
-    public void runTestOnUiThread(final Runnable r) throws Throwable {
-        final Throwable[] exceptions = new Throwable[1];
-        getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                try {
-                    r.run();
-                } catch (Throwable throwable) {
-                    exceptions[0] = throwable;
-                }
-            }
-        });
-        if (exceptions[0] != null) {
-            throw exceptions[0];
-        }
-    }
-
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
index 606f05d..d435f42 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
@@ -29,10 +29,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.view.ActionMode;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
index 9c50144..cb21347 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
@@ -17,18 +17,15 @@
 package android.support.v7.app;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 import android.os.SystemClock;
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MenuItem;
-import android.view.View;
 
 import org.junit.Test;
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
index f104f02..9533f38 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
@@ -17,17 +17,17 @@
 package android.support.v7.app;
 
 import android.support.test.annotation.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Test;
 
-public class BasicsTestCaseWithToolbar extends BaseBasicsTestCase<ToolbarActionBarActivity> {
+@SmallTest
+public class BasicsTestCaseWithToolbar extends BaseBasicsTestCase<ToolbarAppCompatActivity> {
     public BasicsTestCaseWithToolbar() {
-        super(ToolbarActionBarActivity.class);
+        super(ToolbarAppCompatActivity.class);
     }
 
     @Test
-    @SmallTest
     @UiThreadTest
     public void testSupportActionModeAppCompatCallbacks() {
         // Since we're using Toolbar, any action modes will be created from the window
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
index 27b5039..aee6a50 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
@@ -17,17 +17,17 @@
 package android.support.v7.app;
 
 import android.support.test.annotation.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Test;
 
-public class BasicsTestCaseWithWindowDecor extends BaseBasicsTestCase<WindowDecorActionBarActivity> {
+@SmallTest
+public class BasicsTestCaseWithWindowDecor extends BaseBasicsTestCase<WindowDecorAppCompatActivity> {
     public BasicsTestCaseWithWindowDecor() {
-        super(WindowDecorActionBarActivity.class);
+        super(WindowDecorAppCompatActivity.class);
     }
 
     @Test
-    @SmallTest
     @UiThreadTest
     public void testSupportActionModeAppCompatCallbacks() {
         // Since we're using the decor action bar, any action modes not will be created
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
index a165d53..8230cbc 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DialogTestCase.java
@@ -16,20 +16,20 @@
 
 package android.support.v7.app;
 
-import org.junit.Test;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.test.filters.MediumTest;
+
+import org.junit.Test;
+
 @MediumTest
-public class DialogTestCase extends BaseInstrumentationTestCase<WindowDecorActionBarActivity> {
+public class DialogTestCase extends BaseInstrumentationTestCase<WindowDecorAppCompatActivity> {
 
     public DialogTestCase() {
-        super(WindowDecorActionBarActivity.class);
+        super(WindowDecorAppCompatActivity.class);
     }
 
     @Test
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
index fb46ce9..198c63a 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutTest.java
@@ -15,29 +15,30 @@
  */
 package android.support.v7.app;
 
-import android.support.test.InstrumentationRegistry;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.mockito.Mockito.mock;
+
+import android.support.annotation.LayoutRes;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.filters.SmallTest;
 import android.support.v4.widget.DrawerLayout;
+import android.support.v7.appcompat.test.R;
+import android.view.View;
+import android.view.ViewStub;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Test;
 
-import android.support.annotation.LayoutRes;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import android.view.ViewStub;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.core.AllOf.allOf;
-import static org.mockito.Mockito.mock;
-
 /**
  * Test cases to verify that <code>DrawerLayout</code> only supports configurations
  * with at most one drawer child along each vertical (left / right) edge.
@@ -49,17 +50,13 @@
         super(DrawerDynamicLayoutActivity.class);
     }
 
+    @UiThreadTest
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         // Now that the test is done, replace the activity content view with ViewStub so
         // that it's ready to be replaced for the next test.
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final DrawerDynamicLayoutActivity activity = mActivityTestRule.getActivity();
-                activity.setContentView(R.layout.drawer_dynamic_layout);
-            }
-        });
+        final DrawerDynamicLayoutActivity activity = mActivityTestRule.getActivity();
+        activity.setContentView(R.layout.drawer_dynamic_layout);
     }
 
     /**
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
index 1ca2095..d636ac4 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleTest.java
@@ -15,25 +15,27 @@
  */
 package android.support.v7.app;
 
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.appcompat.test.R;
-import android.support.v7.custom.CustomDrawerLayout;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-import org.junit.Before;
-import org.junit.Test;
-
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.v7.testutils.DrawerLayoutActions.closeDrawer;
 import static android.support.v7.testutils.DrawerLayoutActions.openDrawer;
 import static android.support.v7.testutils.DrawerLayoutActions.setDrawerLockMode;
 import static android.support.v7.testutils.TestUtilsActions.setLayoutDirection;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.SmallTest;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.custom.CustomDrawerLayout;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+
 public class DrawerLayoutDoubleTest
         extends BaseInstrumentationTestCase<DrawerLayoutDoubleActivity> {
     private CustomDrawerLayout mDrawerLayout;
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
index 636e7d1..f49735e 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
@@ -15,32 +15,48 @@
  */
 package android.support.v7.app;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.v7.testutils.DrawerLayoutActions.closeDrawer;
+import static android.support.v7.testutils.DrawerLayoutActions.openDrawer;
+import static android.support.v7.testutils.DrawerLayoutActions.setDrawerLockMode;
+import static android.support.v7.testutils.DrawerLayoutActions.wrap;
+import static android.support.v7.testutils.TestUtilsMatchers.inAscendingOrder;
+import static android.support.v7.testutils.TestUtilsMatchers.inDescendingOrder;
+import static android.support.v7.testutils.TestUtilsMatchers.inRange;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.action.GeneralLocation;
 import android.support.test.espresso.action.GeneralSwipeAction;
 import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Swipe;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.CustomDrawerLayout;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.v7.testutils.DrawerLayoutActions.*;
-import static android.support.v7.testutils.TestUtilsMatchers.*;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-
 public class DrawerLayoutTest extends BaseInstrumentationTestCase<DrawerLayoutActivity> {
     private CustomDrawerLayout mDrawerLayout;
 
@@ -95,10 +111,10 @@
 
     @Test
     @MediumTest
-    public void testDrawerOpenCloseFocus() {
+    public void testDrawerOpenCloseFocus() throws Throwable {
         assertFalse("Initial state", mDrawerLayout.isDrawerOpen(GravityCompat.START));
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mContentView.setFocusableInTouchMode(true);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
index 32a78cf..c2e2d16 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/FragmentContentIdTest.java
@@ -16,18 +16,15 @@
 
 package android.support.v7.app;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
+import android.support.test.filters.SmallTest;
+import android.support.v7.appcompat.test.R;
+
 import org.junit.Test;
 
 public class FragmentContentIdTest extends BaseInstrumentationTestCase<FragmentContentIdActivity> {
@@ -38,11 +35,11 @@
 
     @SmallTest
     @Test
-    public void testFragmentAddedToAndroidContentIdCanBeRemoved() {
-        getInstrumentation().runOnMainSync(new Runnable() {
+    public void testFragmentAddedToAndroidContentIdCanBeRemoved() throws Throwable {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                getActivity().replaceWithFragmentB();
+                mActivityTestRule.getActivity().replaceWithFragmentB();
             }
         });
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
index 9caf551..cdaecc0 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithToolbar.java
@@ -16,8 +16,8 @@
 
 package android.support.v7.app;
 
-public class KeyEventsTestCaseWithToolbar extends BaseKeyEventsTestCase<ToolbarActionBarActivity> {
+public class KeyEventsTestCaseWithToolbar extends BaseKeyEventsTestCase<ToolbarAppCompatActivity> {
     public KeyEventsTestCaseWithToolbar() {
-        super(ToolbarActionBarActivity.class);
+        super(ToolbarAppCompatActivity.class);
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithWindowDecor.java b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithWindowDecor.java
index 9707a4c..02296fe 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithWindowDecor.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyEventsTestCaseWithWindowDecor.java
@@ -16,8 +16,8 @@
 
 package android.support.v7.app;
 
-public class KeyEventsTestCaseWithWindowDecor extends BaseKeyEventsTestCase<WindowDecorActionBarActivity> {
+public class KeyEventsTestCaseWithWindowDecor extends BaseKeyEventsTestCase<WindowDecorAppCompatActivity> {
     public KeyEventsTestCaseWithWindowDecor() {
-        super(WindowDecorActionBarActivity.class);
+        super(WindowDecorAppCompatActivity.class);
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
index a27b20b..4f6310c 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
@@ -20,8 +20,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.SystemClock;
+import android.support.test.filters.SmallTest;
 import android.support.v7.testutils.BaseTestActivity;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.View;
@@ -29,9 +29,9 @@
 import org.junit.Test;
 
 public class KeyboardShortcutsTestCaseWithToolbar
-        extends BaseKeyboardShortcutsTestCase<ToolbarActionBarActivity> {
+        extends BaseKeyboardShortcutsTestCase<ToolbarAppCompatActivity> {
     public KeyboardShortcutsTestCaseWithToolbar() {
-        super(ToolbarActionBarActivity.class);
+        super(ToolbarAppCompatActivity.class);
     }
 
     @Test
@@ -40,7 +40,7 @@
         final BaseTestActivity activity = getActivity();
 
         final View editText = activity.findViewById(android.support.v7.appcompat.test.R.id.editText);
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 editText.requestFocus();
@@ -52,7 +52,7 @@
         getInstrumentation().waitForIdleSync();
 
         // Should jump to the action bar after control-<
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertFalse(editText.hasFocus());
@@ -64,7 +64,7 @@
         getInstrumentation().waitForIdleSync();
 
         // Should jump to the first view again.
-        runTestOnUiThread(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertTrue(editText.hasFocus());
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithWindowDecor.java b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithWindowDecor.java
index 4289143..eac1881 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithWindowDecor.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithWindowDecor.java
@@ -17,8 +17,8 @@
 package android.support.v7.app;
 
 public class KeyboardShortcutsTestCaseWithWindowDecor
-        extends BaseKeyboardShortcutsTestCase<WindowDecorActionBarActivity> {
+        extends BaseKeyboardShortcutsTestCase<WindowDecorAppCompatActivity> {
     public KeyboardShortcutsTestCaseWithWindowDecor() {
-        super(WindowDecorActionBarActivity.class);
+        super(WindowDecorAppCompatActivity.class);
     }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
index 0166ded..f7004b3 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
@@ -16,12 +16,15 @@
 
 package android.support.v7.app;
 
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.Build;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.custom.ContextWrapperFrameLayout;
 import android.support.v7.widget.AppCompatAutoCompleteTextView;
@@ -33,15 +36,13 @@
 import android.support.v7.widget.AppCompatRadioButton;
 import android.support.v7.widget.AppCompatRatingBar;
 import android.support.v7.widget.AppCompatSpinner;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
 
 public class LayoutInflaterFactoryTestCase
         extends BaseInstrumentationTestCase<LayoutInflaterFactoryTestActivity> {
@@ -56,150 +57,137 @@
         AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testAndroidThemeInflation() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final LayoutInflater inflater = LayoutInflater.from(getActivity());
-                assertThemedContext(inflater.inflate(R.layout.layout_android_theme, null));
-            }
-        });
+    public void testAndroidThemeInflation() {
+        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        assertThemedContext(inflater.inflate(R.layout.layout_android_theme, null));
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testAppThemeInflation() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final LayoutInflater inflater = LayoutInflater.from(getActivity());
-                assertThemedContext(inflater.inflate(R.layout.layout_app_theme, null));
-            }
-        });
+    public void testAppThemeInflation() {
+        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        assertThemedContext(inflater.inflate(R.layout.layout_app_theme, null));
     }
 
+    // Propagation of themed context to children only works on API 11+.
+    @SdkSuppress(minSdkVersion = 11)
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testAndroidThemeWithChildrenInflation() throws Throwable {
-        if (Build.VERSION.SDK_INT < 11) {
-            // Propagation of themed context to children only works on API 11+. Ignoring test.
-            return;
+    public void testAndroidThemeWithChildrenInflation() {
+        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final ViewGroup root = (ViewGroup) inflater.inflate(
+                R.layout.layout_android_theme_children, null);
+
+        assertThemedContext(root);
+
+        for (int i = 0; i < root.getChildCount(); i++) {
+            final View child = root.getChildAt(i);
+            assertThemedContext(child);
         }
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                LayoutInflater inflater = LayoutInflater.from(getActivity());
-                final ViewGroup root = (ViewGroup) inflater.inflate(
-                        R.layout.layout_android_theme_children, null);
-
-                assertThemedContext(root);
-
-                for (int i = 0; i < root.getChildCount(); i++) {
-                    final View child = root.getChildAt(i);
-                    assertThemedContext(child);
-                }
-            }
-        });
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testSpinnerInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_spinner, AppCompatSpinner.class);
+    public void testSpinnerInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_spinner, AppCompatSpinner.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testEditTextInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_edittext, AppCompatEditText.class);
+    public void testEditTextInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_edittext, AppCompatEditText.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testButtonInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_button, AppCompatButton.class);
+    public void testButtonInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_button, AppCompatButton.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testRadioButtonInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_radiobutton, AppCompatRadioButton.class);
+    public void testRadioButtonInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_radiobutton, AppCompatRadioButton.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testRadioButtonInflationWithVectorButton() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_radiobutton_vector,
+    public void testRadioButtonInflationWithVectorButton() {
+        verifyAppCompatWidgetInflation(R.layout.layout_radiobutton_vector,
                 AppCompatRadioButton.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testImageViewInflationWithVectorSrc() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_imageview_vector,
+    public void testImageViewInflationWithVectorSrc() {
+        verifyAppCompatWidgetInflation(R.layout.layout_imageview_vector,
                 AppCompatImageView.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testContextWrapperParentImageViewInflationWithVectorSrc() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_contextwrapperparent_imageview_vector,
+    public void testContextWrapperParentImageViewInflationWithVectorSrc() {
+        verifyAppCompatWidgetInflation(R.layout.layout_contextwrapperparent_imageview_vector,
                 ContextWrapperFrameLayout.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testCheckBoxInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_checkbox, AppCompatCheckBox.class);
+    public void testCheckBoxInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_checkbox, AppCompatCheckBox.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testActvInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_actv, AppCompatAutoCompleteTextView.class);
+    public void testActvInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_actv, AppCompatAutoCompleteTextView.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testMactvInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_mactv,
+    public void testMactvInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_mactv,
                 AppCompatMultiAutoCompleteTextView.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testRatingBarInflation() throws Throwable {
-        testAppCompatWidgetInflation(R.layout.layout_ratingbar, AppCompatRatingBar.class);
+    public void testRatingBarInflation() {
+        verifyAppCompatWidgetInflation(R.layout.layout_ratingbar, AppCompatRatingBar.class);
     }
 
+    @UiThreadTest
     @Test
     @SmallTest
-    public void testDeclarativeOnClickWithContextWrapper() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                LayoutInflater inflater = LayoutInflater.from(getActivity());
-                View view = inflater.inflate(R.layout.layout_button_themed_onclick, null);
+    public void testDeclarativeOnClickWithContextWrapper() {
+        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        View view = inflater.inflate(R.layout.layout_button_themed_onclick, null);
 
-                assertTrue(view.performClick());
-                assertTrue(getActivity().wasDeclarativeOnClickCalled());
-            }
-        });
+        assertTrue(view.performClick());
+        assertTrue(getActivity().wasDeclarativeOnClickCalled());
     }
 
-    private void testAppCompatWidgetInflation(final int layout, final Class<?> expectedClass)
-            throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                LayoutInflater inflater = LayoutInflater.from(getActivity());
-                View view = inflater.inflate(layout, null);
-                assertSame("View is " + expectedClass.getSimpleName(), expectedClass,
-                        view.getClass());
-            }
-        });
+    private void verifyAppCompatWidgetInflation(final int layout, final Class<?> expectedClass) {
+        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        View view = inflater.inflate(layout, null);
+        assertSame("View is " + expectedClass.getSimpleName(), expectedClass,
+                view.getClass());
     }
 
     private static void assertThemedContext(View view) {
diff --git a/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java b/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java
index 09ed7ab..be6e2ac 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java
@@ -16,18 +16,21 @@
 
 package android.support.v7.app;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.view.menu.MenuBuilder;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
+@MediumTest
 public class MenuBuilderTest {
 
     @Test
diff --git a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
index a3d91d2..a9c8dc2 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
@@ -21,14 +21,16 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.support.v7.app.NightModeActivity.TOP_ACTIVITY;
-import static android.support.v7.testutils.TestUtils.setLocalNightModeAndWaitForRecreate;
 import static android.support.v7.testutils.TestUtilsMatchers.isBackground;
 
 import static org.junit.Assert.assertFalse;
 
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.Suppress;
 import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,20 +54,22 @@
     }
 
     @Test
-    public void testLocalDayNightModeRecreatesActivity() {
+    public void testLocalDayNightModeRecreatesActivity() throws Throwable {
         // Verify first that we're in day mode
         onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
 
         // Now force the local night mode to be yes (aka night mode)
-        setLocalNightModeAndWaitForRecreate(getActivity(), AppCompatDelegate.MODE_NIGHT_YES);
+        setLocalNightModeAndWaitForRecreate(
+                mActivityTestRule.getActivity(), AppCompatDelegate.MODE_NIGHT_YES);
 
         // Now check the text has changed, signifying that night resources are being used
         onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
     }
 
+    @Suppress // Disabled b/31515380
     @Test
-    public void testColorConvertedDrawableChangesWithNightMode() {
-        final NightModeActivity activity = getActivity();
+    public void testColorConvertedDrawableChangesWithNightMode() throws Throwable {
+        final NightModeActivity activity = mActivityTestRule.getActivity();
         final int dayColor = activity.getResources().getColor(R.color.color_sky_day);
         final int nightColor = activity.getResources().getColor(R.color.color_sky_night);
 
@@ -86,12 +90,12 @@
     }
 
     @Test
-    public void testNightModeAutoRecreatesOnTimeChange() {
+    public void testNightModeAutoRecreatesOnTimeChange() throws Throwable {
         // Create a fake TwilightManager and set it as the app instance
         final FakeTwilightManager twilightManager = new FakeTwilightManager();
         TwilightManager.setInstance(twilightManager);
 
-        final NightModeActivity activity = getActivity();
+        final NightModeActivity activity = mActivityTestRule.getActivity();
         final AppCompatDelegateImplV14 delegate = (AppCompatDelegateImplV14) activity.getDelegate();
 
         // Verify that we're currently in day mode
@@ -104,7 +108,7 @@
         assertFalse(activity.isDestroyed());
 
         // Now update the fake twilight manager to be in night and trigger a fake 'time' change
-        getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 twilightManager.setIsNight(true);
@@ -135,4 +139,16 @@
             mIsNight = night;
         }
     }
+
+    private void setLocalNightModeAndWaitForRecreate(final AppCompatActivity activity,
+            @AppCompatDelegate.NightMode final int nightMode) throws Throwable {
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                activity.getDelegate().setLocalNightMode(nightMode);
+            }
+        });
+        instrumentation.waitForIdleSync();
+    }
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/ToolbarActionBarActivity.java b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
similarity index 94%
rename from v7/appcompat/tests/src/android/support/v7/app/ToolbarActionBarActivity.java
rename to v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
index 4b07a2e..9042363 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/ToolbarActionBarActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/ToolbarAppCompatActivity.java
@@ -20,7 +20,7 @@
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.widget.Toolbar;
 
-public class ToolbarActionBarActivity extends BaseTestActivity {
+public class ToolbarAppCompatActivity extends BaseTestActivity {
 
     private Toolbar mToolbar;
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java b/v7/appcompat/tests/src/android/support/v7/app/WindowDecorAppCompatActivity.java
similarity index 92%
rename from v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java
rename to v7/appcompat/tests/src/android/support/v7/app/WindowDecorAppCompatActivity.java
index 90366aa..6454b11 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/WindowDecorActionBarActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/WindowDecorAppCompatActivity.java
@@ -19,7 +19,7 @@
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 
-public class WindowDecorActionBarActivity extends BaseTestActivity {
+public class WindowDecorAppCompatActivity extends BaseTestActivity {
 
     @Override
     protected int getContentViewLayoutResId() {
diff --git a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
index 8ccc8b7..bfb456a 100644
--- a/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/res/content/AppCompatResourcesTestCase.java
@@ -22,13 +22,13 @@
 import android.app.Activity;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.support.test.filters.SmallTest;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.testutils.TestUtils;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Test;
 
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
index 37a3732..6c1f167 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtils.java
@@ -16,7 +16,6 @@
 
 package android.support.v7.testutils;
 
-import android.app.Instrumentation;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -25,10 +24,7 @@
 import android.os.SystemClock;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
-import android.support.test.InstrumentationRegistry;
 import android.support.v4.util.Pair;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.widget.TintTypedArray;
 import android.view.View;
 import android.view.ViewParent;
@@ -254,16 +250,4 @@
             a.recycle();
         }
     }
-
-    public static void setLocalNightModeAndWaitForRecreate(final AppCompatActivity activity,
-            @AppCompatDelegate.NightMode final int nightMode) {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                activity.getDelegate().setLocalNightMode(nightMode);
-            }
-        });
-        instrumentation.waitForIdleSync();
-    }
 }
\ No newline at end of file
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
index 315c12d..e721ebd 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
@@ -15,6 +15,11 @@
  */
 package android.support.v7.widget;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertNull;
+
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.PorterDuff;
@@ -22,6 +27,7 @@
 import android.support.annotation.ColorInt;
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
+import android.support.test.filters.SmallTest;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.app.BaseInstrumentationTestCase;
@@ -30,16 +36,12 @@
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.testutils.TestUtils;
 import android.support.v7.testutils.TestUtilsActions;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
+
 import org.junit.Before;
 import org.junit.Test;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assert.assertNull;
-
 /**
  * Base class for testing custom view extensions in appcompat-v7 that implement the
  * <code>TintableBackgroundView</code> interface. Extensions of this class run all tests
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
index 340737e..1ea0468 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
@@ -15,16 +15,17 @@
  */
 package android.support.v7.widget;
 
-import android.support.v7.appcompat.test.R;
-import android.support.v7.testutils.TestUtilsActions;
-import android.test.suitebuilder.annotation.SmallTest;
-import org.junit.Test;
-
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.v7.testutils.TestUtilsActions.setTextAppearance;
+
 import static org.junit.Assert.assertEquals;
 
+import android.support.test.filters.SmallTest;
+import android.support.v7.appcompat.test.R;
+
+import org.junit.Test;
+
 /**
  * In addition to all tinting-related tests done by the base class, this class provides
  * tests specific to {@link AppCompatButton} class.
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
index bb712d1..d6801da 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
@@ -17,15 +17,12 @@
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
-import android.graphics.drawable.Drawable;
 import android.support.test.espresso.ViewInteraction;
+import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.TestUtilsMatchers;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.widget.ImageView;
 
 import org.junit.Test;
 
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
index 52dd727..b7a28af 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
@@ -15,17 +15,6 @@
  */
 package android.support.v7.widget;
 
-import android.content.res.Resources;
-import android.support.annotation.ColorInt;
-import android.support.annotation.ColorRes;
-import android.support.annotation.IdRes;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.content.res.ResourcesCompat;
-import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-import org.hamcrest.Matcher;
-import org.junit.Test;
-
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
@@ -35,6 +24,18 @@
 import static android.support.v7.testutils.TestUtilsMatchers.hasChild;
 import static android.support.v7.testutils.TestUtilsMatchers.isCombinedBackground;
 
+import android.content.res.Resources;
+import android.support.annotation.ColorInt;
+import android.support.annotation.ColorRes;
+import android.support.annotation.IdRes;
+import android.support.test.filters.SmallTest;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v7.appcompat.test.R;
+
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
 /**
  * In addition to all tinting-related tests done by the base class, this class provides
  * tests specific to {@link AppCompatSpinner} class.
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
index 8da53d6..d9c3c55 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
@@ -17,18 +17,15 @@
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
 import static android.support.v7.testutils.TestUtilsActions.setEnabled;
 import static android.support.v7.testutils.TestUtilsActions.setTextAppearance;
+
 import static org.junit.Assert.assertEquals;
 
 import android.graphics.Color;
-import android.support.test.espresso.action.ViewActions;
+import android.support.test.filters.SmallTest;
 import android.support.v4.content.ContextCompat;
-import android.support.v4.content.res.ResourcesCompat;
 import android.support.v7.appcompat.test.R;
-import android.support.v7.testutils.TestUtilsActions;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.TextView;
 
 import org.junit.Test;
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
index e166626..44c05ae 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
@@ -15,17 +15,39 @@
  */
 package android.support.v7.widget;
 
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.Instrumentation;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
 import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.LayoutInflater;
-import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -39,20 +61,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
 public class ListPopupWindowTest extends BaseInstrumentationTestCase<PopupTestActivity> {
     private FrameLayout mContainer;
 
@@ -144,14 +152,14 @@
 
     @Test
     @SmallTest
-    public void testDismissalViaAPI() {
+    public void testDismissalViaAPI() throws Throwable {
         Builder popupBuilder = new Builder().withDismissListener();
         popupBuilder.wireToActionButton();
 
         onView(withId(R.id.test_button)).perform(click());
         assertTrue("Popup window showing", mListPopupWindow.isShowing());
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mListPopupWindow.dismiss();
@@ -270,7 +278,7 @@
 
     @Test
     @SmallTest
-    public void testItemClickViaAPI() {
+    public void testItemClickViaAPI() throws Throwable {
         Builder popupBuilder = new Builder().withItemClickListener();
         popupBuilder.wireToActionButton();
 
@@ -281,7 +289,7 @@
         verify(popupBuilder.mOnItemClickListener, never()).onItemClick(
                 any(AdapterView.class), any(View.class), any(int.class), any(int.class));
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mListPopupWindow.performItemClick(1);
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
index ba27a88..3775079 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/PopupMenuTest.java
@@ -48,9 +48,9 @@
 import android.support.test.espresso.Root;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
+import android.support.test.filters.SmallTest;
 import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -285,7 +285,7 @@
 
     @Test
     @SmallTest
-    public void testDismissalViaAPI() {
+    public void testDismissalViaAPI() throws Throwable {
         Builder menuBuilder = new Builder().withDismissListener();
         menuBuilder.wireToActionButton();
 
@@ -293,7 +293,7 @@
 
         // Since PopupMenu is not a View, we can't use Espresso's view actions to invoke
         // the dismiss() API
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mPopupMenu.dismiss();
@@ -388,7 +388,7 @@
 
     @Test
     @SmallTest
-    public void testSimpleMenuItemClickViaAPI() {
+    public void testSimpleMenuItemClickViaAPI() throws Throwable {
         Builder menuBuilder = new Builder().withMenuItemClickListener();
         menuBuilder.wireToActionButton();
 
@@ -397,7 +397,7 @@
         // Verify that our menu item click listener hasn't been called yet
         verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mPopupMenu.getMenu().performIdentifierAction(R.id.action_highlight, 0);
@@ -485,7 +485,7 @@
         // Verify that our menu item click listener hasn't been called yet
         verify(menuBuilder.mOnMenuItemClickListener, never()).onMenuItemClick(any(MenuItem.class));
 
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mPopupMenu.getMenu().performIdentifierAction(R.id.action_share, 0);
@@ -527,7 +527,7 @@
                 .check(matches(isDisplayed()));
 
         // Now ask the share submenu to perform an action on its specific menu item
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mPopupMenu.getMenu().findItem(R.id.action_share).getSubMenu().
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
index 673a6aa..eb2d462 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/TintResourcesTest.java
@@ -21,9 +21,9 @@
 
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.support.test.filters.SmallTest;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.BaseInstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Test;
 
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
index 7861dc1..cd3b407 100644
--- a/v7/cardview/Android.mk
+++ b/v7/cardview/Android.mk
@@ -14,63 +14,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.  The resources library and the R class that it
-# contains will not be linked into the final static library.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-cardview-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library to resolve cyclic dependencies between CardView and platform dependent
-# implementations
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-base
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, base)
-LOCAL_JAVA_LIBRARIES := android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of Gingerbread APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-gingerbread
-LOCAL_SDK_VERSION := 9
-LOCAL_SRC_FILES := $(call all-java-files-under, gingerbread)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-base
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
-    android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JB MR1 APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-gingerbread
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
-    android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of L APIs
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-cardview-api21
-LOCAL_SDK_VERSION := 21
-LOCAL_SRC_FILES := $(call all-java-files-under, api21)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-jellybean-mr1
-LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res \
-    android-support-annotations
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
@@ -80,12 +23,16 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-cardview
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-api21
-LOCAL_JAVA_LIBRARIES := android-support-annotations
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v7-cardview-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,base) \
+    $(call all-java-files-under,gingerbread) \
+    $(call all-java-files-under,jellybean-mr1) \
+    $(call all-java-files-under,api21) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
index 413a287..b4f2460 100644
--- a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
+++ b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
@@ -15,11 +15,15 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
+@RequiresApi(21)
+@TargetApi(21)
 class CardViewApi21 implements CardViewImpl {
 
     @Override
diff --git a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
index 3a85d9c..7e85b7f 100644
--- a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
+++ b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
@@ -15,6 +15,7 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -28,6 +29,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 
 import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateVerticalPadding;
 import static android.support.v7.widget.RoundRectDrawableWithShadow.calculateHorizontalPadding;
@@ -38,6 +40,8 @@
  * <p>
  * Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
  */
+@RequiresApi(21)
+@TargetApi(21)
 class RoundRectDrawable extends Drawable {
     private float mRadius;
     private final Paint mPaint;
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
index b5be921..2573631 100644
--- a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
+++ b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
@@ -15,7 +15,9 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 
 /**
@@ -23,6 +25,8 @@
  * <p>
  * Necessary to resolve circular dependency between base CardView and platform implementations.
  */
+@RequiresApi(9)
+@TargetApi(9)
 interface CardViewDelegate {
     void setCardBackground(Drawable drawable);
     Drawable getCardBackground();
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
index 26799da..f36bd2b 100644
--- a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
+++ b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
@@ -15,13 +15,17 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 
 /**
  * Interface for platform specific CardView implementations.
  */
+@RequiresApi(9)
+@TargetApi(9)
 interface CardViewImpl {
     void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
             float radius, float elevation, float maxElevation);
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index e9e0ab5..12f9e39 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'cardview-v7'
 
 dependencies {
@@ -7,25 +6,22 @@
 }
 
 android {
-    // WARNING: should be 7
     compileSdkVersion project.ext.currentSdk
 
     defaultConfig {
         minSdkVersion 9
-        // TODO: get target from branch
-        //targetSdkVersion 19
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
     }
 
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = ['base', 'gingerbread', 'jellybean-mr1', 'api21', 'src']
-        main.aidl.srcDirs = ['base', 'gingerbread', 'jellybean-mr1', 'api21', 'src']
-        main.res.srcDirs = ['res']
+        main.java.srcDirs = [
+                'base',
+                'gingerbread',
+                'jellybean-mr1',
+                'api21',
+                'src'
+        ]
+        main.res.srcDir 'res'
 
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/java'
diff --git a/v7/cardview/dummy/Dummy.java b/v7/cardview/dummy/Dummy.java
deleted file mode 100644
index be16dc7..0000000
--- a/v7/cardview/dummy/Dummy.java
+++ /dev/null
@@ -1 +0,0 @@
-// Dummy java file used to build the resource library.
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
index 857cffa..f430213 100644
--- a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
+++ b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
@@ -15,6 +15,7 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -23,7 +24,10 @@
 import android.graphics.RectF;
 import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(9)
+@TargetApi(9)
 class CardViewGingerbread implements CardViewImpl {
 
     final RectF sCornerRect = new RectF();
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java b/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
index 5cefd8f..32bf877 100644
--- a/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
+++ b/v7/cardview/gingerbread/android/support/v7/widget/RoundRectDrawableWithShadow.java
@@ -15,6 +15,7 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -30,11 +31,14 @@
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v7.cardview.R;
 
 /**
  * A rounded rectangle drawable which also includes a shadow around.
  */
+@RequiresApi(9)
+@TargetApi(9)
 class RoundRectDrawableWithShadow extends Drawable {
     // used to calculate content padding
     final static double COS_45 = Math.cos(Math.toRadians(45));
diff --git a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
index 4c32227..a9c0e0a 100644
--- a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
+++ b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
@@ -15,10 +15,14 @@
  */
 package android.support.v7.widget;
 
+import android.annotation.TargetApi;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(17)
+@TargetApi(17)
 class CardViewJellybeanMr1 extends CardViewGingerbread {
 
     @Override
diff --git a/v7/gridlayout/Android.mk b/v7/gridlayout/Android.mk
index 7938918..6eac23b4 100644
--- a/v7/gridlayout/Android.mk
+++ b/v7/gridlayout/Android.mk
@@ -26,10 +26,13 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-gridlayout
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SDK_VERSION := 9
-LOCAL_SHARED_ANDROID_LIBRARIES += android-support-compat android-support-core-ui
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-compat \
+    android-support-core-ui \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/gridlayout/tests/src/android/support/v7/widget/GridLayoutTest.java b/v7/gridlayout/tests/src/android/support/v7/widget/GridLayoutTest.java
index b82fea0..a389386 100644
--- a/v7/gridlayout/tests/src/android/support/v7/widget/GridLayoutTest.java
+++ b/v7/gridlayout/tests/src/android/support/v7/widget/GridLayoutTest.java
@@ -16,25 +16,26 @@
 
 package android.support.v7.widget;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.gridlayout.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class GridLayoutTest {
@@ -48,9 +49,9 @@
         mActivityTestRule = new ActivityTestRule<>(GridLayoutTestActivity.class);
     }
 
-    private void setContentView(final int layoutId) {
+    private void setContentView(final int layoutId) throws Throwable {
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final Activity activity = mActivityTestRule.getActivity();
@@ -65,7 +66,7 @@
     }
 
     @Test
-    public void testUseDefaultMargin() {
+    public void testUseDefaultMargin() throws Throwable {
         setContentView(R.layout.use_default_margin_test);
         int left = mLeftView.getWidth();
         int right = mRightView.getWidth();
@@ -78,7 +79,7 @@
     }
 
     @Test
-    public void testImplicitFillHorizontal() {
+    public void testImplicitFillHorizontal() throws Throwable {
         setContentView(R.layout.fill_horizontal_test);
         int left = mLeftView.getWidth();
         int right = mRightView.getWidth();
@@ -91,7 +92,7 @@
     }
 
     @Test
-    public void testMakeViewGone() {
+    public void testMakeViewGone() throws Throwable {
         setContentView(R.layout.make_view_gone_test);
         int left = mLeftView.getWidth();
         int right = mRightView.getWidth();
@@ -101,7 +102,7 @@
         assertTrue("test sanity", total > 0);
         // set second view to gone
         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.runOnMainSync(new Runnable() {
+        mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final View rightView = mActivityTestRule.getActivity().findViewById(R.id.rightView);
@@ -116,7 +117,7 @@
     }
 
     @Test
-    public void testWrapContentInOtherDirection() {
+    public void testWrapContentInOtherDirection() throws Throwable {
         setContentView(R.layout.height_wrap_content_test);
         int left = mLeftView.getHeight();
         int right = mRightView.getHeight();
diff --git a/v7/mediarouter/Android.mk b/v7/mediarouter/Android.mk
index 0c00f76..21b4a62 100644
--- a/v7/mediarouter/Android.mk
+++ b/v7/mediarouter/Android.mk
@@ -14,55 +14,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Build the resources using the latest applicable SDK version.
-# We do this here because the final static library must be compiled with an older
-# SDK version than the resources.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-mediarouter-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := android-support-v7-appcompat
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean
-LOCAL_SDK_VERSION := 16
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean)
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean MR1 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean-mr1
-LOCAL_SDK_VERSION := 17
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of JellyBean MR2 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-jellybean-mr2
-LOCAL_SDK_VERSION := 18
-LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr2)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr1
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# A helper sub-library that makes direct use of V24 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-v7-mediarouter-api24
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api24)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr2
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
@@ -76,11 +27,14 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-mediarouter
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-api24
-LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v7-mediarouter-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,jellybean) \
+    $(call all-java-files-under,jellybean-mr1) \
+    $(call all-java-files-under,jellybean-mr2) \
+    $(call all-java-files-under,api24) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-appcompat \
     android-support-v7-palette \
diff --git a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
index 3734b59..48bef17 100644
--- a/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
+++ b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
@@ -16,6 +16,11 @@
 
 package android.support.v7.media;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(24)
+@TargetApi(24)
 final class MediaRouterApi24 {
     public static final class RouteInfo {
         public static int getDeviceType(Object routeObj) {
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 16761c2..61a5985 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -1,36 +1,28 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'mediarouter-v7'
 
-
-// some of the source requires compiling against a newer API.
-// Right now, use normal Java source sets to compile those into a jar and 
-// package it as a local dependencies inside the library aar.
-
-createApiSourceSets(project, gradle.ext.studioCompat.modules.mediaRouter.apiTargets)
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-setApiModuleDependencies(project, dependencies, gradle.ext.studioCompat.modules.mediaRouter.dependencies)
-// keep these dependencies here since API specific implementations don't need to access them.
 dependencies {
     compile project(":support-appcompat-v7")
     compile project(":support-palette-v7")
 }
+
 android {
     compileSdkVersion project.ext.currentSdk
 
+    defaultConfig {
+        minSdkVersion 9
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDir 'src'
+        main.java.srcDirs = [
+                'jellybean',
+                'jellybean-mr1',
+                'jellybean-mr2',
+                'api24',
+                'src'
+        ]
         main.res.srcDir 'res'
-        main.assets.srcDir 'assets'
-        main.resources.srcDir 'src'
-
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
-        androidTest.setRoot('tests')
-        androidTest.java.srcDir 'tests/src'
     }
 
     compileOptions {
@@ -73,11 +65,6 @@
         from android.sourceSets.main.java.srcDirs
     }
 
-    project.ext.allSS.each { ss ->
-        javadocTask.source ss.java
-        sourcesJarTask.from ss.java.srcDirs
-    }
-
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/v7/mediarouter/dummy/Dummy.java b/v7/mediarouter/dummy/Dummy.java
deleted file mode 100644
index be16dc7..0000000
--- a/v7/mediarouter/dummy/Dummy.java
+++ /dev/null
@@ -1 +0,0 @@
-// Dummy java file used to build the resource library.
diff --git a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
index 0ef744f..3a42a2f 100644
--- a/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
+++ b/v7/mediarouter/jellybean-mr1/android/support/v7/media/MediaRouterJellybeanMr1.java
@@ -16,10 +16,12 @@
 
 package android.support.v7.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Handler;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 import android.view.Display;
 
@@ -27,6 +29,8 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
+@RequiresApi(17)
+@TargetApi(17)
 final class MediaRouterJellybeanMr1 {
     private static final String TAG = "MediaRouterJellybeanMr1";
 
diff --git a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
index f3c2966..6799faa 100644
--- a/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
+++ b/v7/mediarouter/jellybean-mr2/android/support/v7/media/MediaRouterJellybeanMr2.java
@@ -16,6 +16,11 @@
 
 package android.support.v7.media;
 
+import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(18)
+@TargetApi(18)
 final class MediaRouterJellybeanMr2 {
     public static Object getDefaultRoute(Object routerObj) {
         return ((android.media.MediaRouter)routerObj).getDefaultRoute();
diff --git a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
index c030332..85071a4 100644
--- a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
+++ b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
@@ -16,16 +16,21 @@
 
 package android.support.v7.media;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.util.Log;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
+@RequiresApi(16)
+@TargetApi(16)
 final class MediaRouterJellybean {
     private static final String TAG = "MediaRouterJellybean";
 
@@ -111,6 +116,21 @@
         return new VolumeCallbackProxy<VolumeCallback>(callback);
     }
 
+    static boolean isBluetoothA2dpOn(Object routerObj) {
+        try {
+            Field globalRouterField = routerObj.getClass().getDeclaredField("sStatic");
+            globalRouterField.setAccessible(true);
+            Object globalRouterObj = globalRouterField.get(null);
+            Method method = globalRouterObj.getClass().getDeclaredMethod("isBluetoothA2dpOn", null);
+            method.setAccessible(true);
+            Object result = method.invoke(globalRouterObj, null);
+            return (Boolean) result;
+        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
+                | NoSuchMethodException | InvocationTargetException e) {
+            return false;
+        }
+    }
+
     public static final class RouteInfo {
         public static CharSequence getName(Object routeObj, Context context) {
             return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
diff --git a/v7/mediarouter/res/layout/mr_chooser_dialog.xml b/v7/mediarouter/res/layout/mr_chooser_dialog.xml
index bda99f5..ea0f7d5 100644
--- a/v7/mediarouter/res/layout/mr_chooser_dialog.xml
+++ b/v7/mediarouter/res/layout/mr_chooser_dialog.xml
@@ -14,16 +14,25 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
-             android:layout_height="wrap_content" >
-
+             android:layout_height="wrap_content"
+             android:orientation="vertical">
+    <TextView android:id="@+id/mr_chooser_title"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:paddingLeft="24dp"
+              android:paddingRight="24dp"
+              android:paddingTop="24dp"
+              android:text="@string/mr_chooser_title"
+              android:singleLine="true"
+              android:ellipsize="end"
+              android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
     <ListView android:id="@+id/mr_chooser_list"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:divider="@android:color/transparent"
               android:dividerHeight="0dp" />
-
     <LinearLayout android:id="@android:id/empty"
               android:layout_width="fill_parent"
               android:layout_height="240dp"
@@ -35,11 +44,12 @@
         <TextView android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_gravity="center"
-                  android:text="@string/mr_chooser_searching" />
+                  android:text="@string/mr_chooser_searching"
+                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
         <ProgressBar android:layout_width="150dp"
                      android:layout_height="wrap_content"
                      android:layout_gravity="center"
                      android:indeterminate="true"
                      style="?android:attr/progressBarStyleHorizontal" />
     </LinearLayout>
-</FrameLayout>
+</LinearLayout>
diff --git a/v7/mediarouter/res/layout/mr_chooser_list_item.xml b/v7/mediarouter/res/layout/mr_chooser_list_item.xml
index d578560..f51b3a6 100644
--- a/v7/mediarouter/res/layout/mr_chooser_list_item.xml
+++ b/v7/mediarouter/res/layout/mr_chooser_list_item.xml
@@ -38,14 +38,14 @@
                   android:layout_height="32dp"
                   android:singleLine="true"
                   android:ellipsize="marquee"
-                  android:textAppearance="?attr/mediaRouteChooserPrimaryTextStyle" />
+                  android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText" />
 
         <TextView android:id="@+id/mr_chooser_route_desc"
                   android:layout_width="fill_parent"
                   android:layout_height="24dp"
                   android:singleLine="true"
                   android:ellipsize="marquee"
-                  android:textAppearance="?attr/mediaRouteChooserSecondaryTextStyle" />
+                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText" />
     </LinearLayout>
 
 </LinearLayout>
diff --git a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
index 0cfcd6c..c8aa79f 100644
--- a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
+++ b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
@@ -16,15 +16,14 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:id="@+id/mr_expandable_area"
-          android:background="@android:color/transparent"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent">
     <LinearLayout android:id="@+id/mr_dialog_area"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
-                  android:background="?attr/MediaRouteControllerWindowBackground"
                   android:layout_gravity="center"
-                  android:orientation="vertical">
+                  android:orientation="vertical"
+                  android:background="?attr/colorBackgroundFloating">
         <LinearLayout android:id="@+id/mr_title_bar"
                       android:layout_width="fill_parent"
                       android:layout_height="wrap_content"
@@ -38,7 +37,7 @@
                       android:gravity="center_vertical"
                       android:singleLine="true"
                       android:ellipsize="end"
-                      android:textAppearance="?attr/mediaRouteControllerTitleTextStyle" />
+                      android:textAppearance="@style/TextAppearance.MediaRouter.Title" />
             <ImageButton android:id="@+id/mr_close"
                          android:layout_width="48dp"
                          android:layout_height="48dp"
@@ -74,7 +73,8 @@
                               android:orientation="vertical"
                               android:paddingTop="16dp"
                               android:paddingBottom="16dp"
-                              android:layout_gravity="bottom">
+                              android:layout_gravity="bottom"
+                              android:theme="?attr/mediaRouteControlPanelThemeOverlay">
                     <include android:id="@+id/mr_playback_control"
                              layout="@layout/mr_playback_control" />
                     <View android:id="@+id/mr_control_divider"
@@ -92,7 +92,8 @@
                         android:scrollbarStyle="outsideOverlay"
                         android:clipToPadding="false"
                         android:visibility="gone"
-                        android:splitMotionEvents="false" />
+                        android:splitMotionEvents="false"
+                        android:theme="?attr/mediaRouteControlPanelThemeOverlay" />
             </LinearLayout>
         </FrameLayout>
         <include layout="@layout/abc_alert_dialog_button_bar_material" />
diff --git a/v7/mediarouter/res/layout/mr_controller_volume_item.xml b/v7/mediarouter/res/layout/mr_controller_volume_item.xml
index 985646d..d2d1572 100644
--- a/v7/mediarouter/res/layout/mr_controller_volume_item.xml
+++ b/v7/mediarouter/res/layout/mr_controller_volume_item.xml
@@ -27,7 +27,7 @@
         <TextView android:id="@+id/mr_name"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:textAppearance="?attr/mediaRouteControllerSecondaryTextStyle"
+                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
                   android:singleLine="true" />
         <LinearLayout android:layout_width="fill_parent"
                       android:layout_height="wrap_content"
diff --git a/v7/mediarouter/res/layout/mr_playback_control.xml b/v7/mediarouter/res/layout/mr_playback_control.xml
index 9ee2191..b441254 100644
--- a/v7/mediarouter/res/layout/mr_playback_control.xml
+++ b/v7/mediarouter/res/layout/mr_playback_control.xml
@@ -39,12 +39,12 @@
         <TextView android:id="@+id/mr_control_title"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:textAppearance="?attr/mediaRouteControllerPrimaryTextStyle"
+                  android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText"
                   android:singleLine="true" />
         <TextView android:id="@+id/mr_control_subtitle"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
-                  android:textAppearance="?attr/mediaRouteControllerSecondaryTextStyle"
+                  android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
                   android:singleLine="true" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/v7/mediarouter/res/values-af/strings.xml b/v7/mediarouter/res/values-af/strings.xml
index 9811194..444ffc2 100644
--- a/v7/mediarouter/res/values-af/strings.xml
+++ b/v7/mediarouter/res/values-af/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Stelsel"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Toestelle"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knoppie"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Uitsaai-knoppie. Ontkoppel"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Uitsaai-knoppie. Koppel tans"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Uitsaai-knoppie. Gekoppel"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Saai uit na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Vind tans toestelle"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ontkoppel"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Vou uit"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Vou in"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumkunswerk"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volumeglyer"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Geen media is gekies nie"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Geen inligting beskikbaar nie"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Saai tans skerm uit"</string>
diff --git a/v7/mediarouter/res/values-am/strings.xml b/v7/mediarouter/res/values-am/strings.xml
index 6f7931d..96a2c09 100644
--- a/v7/mediarouter/res/values-am/strings.xml
+++ b/v7/mediarouter/res/values-am/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ስርዓት"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"መሣሪያዎች"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"የCast አዝራር"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast አዝራር። ግንኙነት ተቋርጧል"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast አዝራር በማገናኘት ላይ"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast አዝራር። ተገናኝቷል"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast አድርግ ወደ"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"መሣሪያዎችን በማግኘት ላይ"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ግንኙነት አቋርጥ"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"አስፋ"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ሰብስብ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"የአልበም ስነ-ጥበብ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ተንሸራታች የድምፅ መቆጣጠሪያ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ምንም ማህደረመረጃ አልተመረጠም"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ምንም መረጃ አይገኝም"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ማያ ገጽን በመውሰድ ላይ"</string>
diff --git a/v7/mediarouter/res/values-ar/strings.xml b/v7/mediarouter/res/values-ar/strings.xml
index 29cab47..3caf4b6 100644
--- a/v7/mediarouter/res/values-ar/strings.xml
+++ b/v7/mediarouter/res/values-ar/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"النظام"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"الأجهزة"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"زر الإرسال"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"زر الإرسال. تم قطع الاتصال"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"زر الإرسال. جارٍ الاتصال"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"زر الإرسال. تم الاتصال"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"إرسال إلى"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"جارٍ البحث عن أجهزة"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"قطع الاتصال"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"توسيع"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"تصغير"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"صورة الألبوم"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"شريط تمرير مستوى الصوت"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"لم يتم اختيار أية وسائط"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"لا تتوفر أية معلومات"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"جارٍ إرسال الشاشة"</string>
diff --git a/v7/mediarouter/res/values-az-rAZ/strings.xml b/v7/mediarouter/res/values-az-rAZ/strings.xml
index 765520e..31574c3 100644
--- a/v7/mediarouter/res/values-az-rAZ/strings.xml
+++ b/v7/mediarouter/res/values-az-rAZ/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Yayım düyməsi"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Yayım düyməsi. Bağlantı kəsildi"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Yayım düyməsi. Qoşulur"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Yayım düyməsi. Qoşuldu"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Bura yayımlayın"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Cihazlar axtarılır"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Bağlantını kəsin"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Genişləndirin"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yığcamlaşdırın"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom incəsənəti"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Səs hərmi diyircəyi"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Heç bir media seçilməyib"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Əlçatan məlumat yoxdur"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekran yayımlanır"</string>
diff --git a/v7/mediarouter/res/values-b+sr+Latn/strings.xml b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
index 6c32754..8075d2e 100644
--- a/v7/mediarouter/res/values-b+sr+Latn/strings.xml
+++ b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme Prebaci"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme Prebaci. Veza je prekinuta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme Prebaci. Povezuje se"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme Prebaci. Povezan je"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Prebacujte na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Pronalaženje uređaja"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nema izabranih medija"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nisu dostupne nikakve informacije"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
diff --git a/v7/mediarouter/res/values-be-rBY/strings.xml b/v7/mediarouter/res/values-be-rBY/strings.xml
index de52c06..75c24d5 100644
--- a/v7/mediarouter/res/values-be-rBY/strings.xml
+++ b/v7/mediarouter/res/values-be-rBY/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Сістэма"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Прылады"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляцыі"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляцыі. Адключана"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляцыі. Ідзе падключэнне"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляцыі. Падключана"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Трансляваць на"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук прылад"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Адлучыць"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгарнуць"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згарнуць"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Вокладка альбома"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Паўзунок гучнасці"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медыяфайл не выбраны"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Інфармацыя адсутнічае"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экран трансляцыі"</string>
diff --git a/v7/mediarouter/res/values-bg/strings.xml b/v7/mediarouter/res/values-bg/strings.xml
index 036b31f..d756d6c 100644
--- a/v7/mediarouter/res/values-bg/strings.xml
+++ b/v7/mediarouter/res/values-bg/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Бутон за предаване"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Бутон за предаване. Връзката е прекратена"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Бутон за предаване. Свързва се"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Бутон за предаване. Установена е връзка"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Предаване към"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Търсят се устройства"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Прекратяване на връзката"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Разгъване"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Свиване"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Обложка на албума"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Плъзгач за силата на звука"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Няма избрана мултимедия"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Няма налична информация"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранът се предава"</string>
diff --git a/v7/mediarouter/res/values-bn-rBD/strings.xml b/v7/mediarouter/res/values-bn-rBD/strings.xml
index 0e3e491..b94b9af 100644
--- a/v7/mediarouter/res/values-bn-rBD/strings.xml
+++ b/v7/mediarouter/res/values-bn-rBD/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"সিস্টেম"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ডিভাইসগুলি"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"কাস্ট করার বোতাম"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"কাস্ট করার বোতাম৷ সংযোগ বিচ্ছিন্ন হয়েছে"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"কাস্ট করার বোতাম৷ সংযোগ করা হচ্ছে"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"কাস্ট করার বোতাম৷ সংযুক্ত হয়েছে"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"এতে কাস্ট করুন"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ডিভাইসগুলিকে খোঁজা হচ্ছে"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"সংযোগ বিচ্ছিন্ন করুন"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"প্রসারিত করুন"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"সঙ্কুচিত করুন"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"অ্যালবাম শৈলি"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ভলিউম স্লাইডার"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"কোনো মিডিয়া নির্বাচন করা হয়নি"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"কোনো তথ্য উপলব্ধ নেই"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"স্ক্রীন কাস্ট করা হচ্ছে"</string>
diff --git a/v7/mediarouter/res/values-bs-rBA/strings.xml b/v7/mediarouter/res/values-bs-rBA/strings.xml
index a6894f5..df4e3ef 100644
--- a/v7/mediarouter/res/values-bs-rBA/strings.xml
+++ b/v7/mediarouter/res/values-bs-rBA/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme za prebacivanje"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Dugme za prebacivanje. Veza je prekinuta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Dugme za prebacivanje. Povezivanje"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Dugme za prebacivanje. Povezan"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Prebacujte na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Omot albuma"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za jačinu zvuka"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nijedan medij nije odabran"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nema dostupnih informacija"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
diff --git a/v7/mediarouter/res/values-ca/strings.xml b/v7/mediarouter/res/values-ca/strings.xml
index 7fc51f7..400d424 100644
--- a/v7/mediarouter/res/values-ca/strings.xml
+++ b/v7/mediarouter/res/values-ca/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositius"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botó d\'emetre"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botó Emet. Desconnectat."</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botó Emet. S\'està connectant."</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botó Emet. Connectat."</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Emet a"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"S\'estan cercant dispositius"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconnecta"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Desplega"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Replega"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Imatge de l\'àlbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control lliscant de volum"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No s\'ha seleccionat cap fitxer multimèdia"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No hi ha informació disponible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emissió de pantalla"</string>
diff --git a/v7/mediarouter/res/values-cs/strings.xml b/v7/mediarouter/res/values-cs/strings.xml
index f5a286e..5a29bac 100644
--- a/v7/mediarouter/res/values-cs/strings.xml
+++ b/v7/mediarouter/res/values-cs/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zařízení"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Tlačítko odesílání"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tlačítko odesílání. Odpojeno"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tlačítko odesílání. Připojování"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tlačítko odesílání. Připojeno"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Odesílat do"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Hledání zařízení"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odpojit"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozbalit"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sbalit"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Obal alba"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Posuvník hlasitosti"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nebyla vybrána žádná média"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nejsou k dispozici žádné informace"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Odesílání obsahu obrazovky"</string>
diff --git a/v7/mediarouter/res/values-da/strings.xml b/v7/mediarouter/res/values-da/strings.xml
index 9e5f9a5..f33a4ee 100644
--- a/v7/mediarouter/res/values-da/strings.xml
+++ b/v7/mediarouter/res/values-da/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheder"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knap"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knap. Forbindelsen er afbrudt"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knap. Opretter forbindelse"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knap. Tilsluttet"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast til"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Finder enheder"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Afbryd"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Udvid"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skjul"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumgrafik"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Lydstyrkeskyder"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Der er ikke valgt nogen medier"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Der er ingen tilgængelige oplysninger"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skærmen castes"</string>
diff --git a/v7/mediarouter/res/values-de/strings.xml b/v7/mediarouter/res/values-de/strings.xml
index 91d764f..13686aa 100644
--- a/v7/mediarouter/res/values-de/strings.xml
+++ b/v7/mediarouter/res/values-de/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Geräte"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-Symbol"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Streaming-Schaltfläche. Nicht verbunden"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Streaming-Schaltfläche. Verbindung wird hergestellt"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Streaming-Schaltfläche. Verbunden"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Streamen auf"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Geräte werden gesucht."</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Verbindung trennen"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Maximieren"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Minimieren"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumcover"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Schieberegler für die Lautstärke"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Keine Medien ausgewählt"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Keine Informationen verfügbar"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Bildschirm wird gestreamt."</string>
diff --git a/v7/mediarouter/res/values-el/strings.xml b/v7/mediarouter/res/values-el/strings.xml
index 0a1a62f..3f45621 100644
--- a/v7/mediarouter/res/values-el/strings.xml
+++ b/v7/mediarouter/res/values-el/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Σύστημα"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Συσκευές"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Κουμπί Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Κουμπί μετάδοσης. Αποσυνδέθηκε"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Κουμπί μετάδοση. Σύνδεση σε εξέλιξη"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Κουμπί μετάδοσης. Συνδέθηκε"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Μετάδοση σε"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Εύρεση συσκευών"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Αποσύνδεση"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ανάπτυξη"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Σύμπτυξη"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Εξώφυλλο άλμπουμ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ρυθμιστικό έντασης ήχου"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Δεν έχουν επιλεγεί μέσα"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Δεν υπάρχουν διαθέσιμες πληροφορίες"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Μετάδοση οθόνης"</string>
diff --git a/v7/mediarouter/res/values-en-rAU/strings.xml b/v7/mediarouter/res/values-en-rAU/strings.xml
index d60689e..9201c1f 100644
--- a/v7/mediarouter/res/values-en-rAU/strings.xml
+++ b/v7/mediarouter/res/values-en-rAU/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
diff --git a/v7/mediarouter/res/values-en-rGB/strings.xml b/v7/mediarouter/res/values-en-rGB/strings.xml
index d60689e..9201c1f 100644
--- a/v7/mediarouter/res/values-en-rGB/strings.xml
+++ b/v7/mediarouter/res/values-en-rGB/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
diff --git a/v7/mediarouter/res/values-en-rIN/strings.xml b/v7/mediarouter/res/values-en-rIN/strings.xml
index d60689e..9201c1f 100644
--- a/v7/mediarouter/res/values-en-rIN/strings.xml
+++ b/v7/mediarouter/res/values-en-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast button"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast button. Disconnected"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast button. Connecting"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast button. Connected"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast to"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Finding devices"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Disconnect"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expand"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Collapse"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volume slider"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No media selected"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No info available"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Casting screen"</string>
diff --git a/v7/mediarouter/res/values-es-rUS/strings.xml b/v7/mediarouter/res/values-es-rUS/strings.xml
index 2318059..90aa823 100644
--- a/v7/mediarouter/res/values-es-rUS/strings.xml
+++ b/v7/mediarouter/res/values-es-rUS/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botón para transmitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón para transmitir (desconectado)"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón para transmitir (conectando)"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón para transmitir (conectado)"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir a"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mostrar"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ocultar"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Imagen del álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control deslizante del volumen"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No se seleccionó ningún contenido multimedia"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Sin información disponible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitiendo pantalla"</string>
diff --git a/v7/mediarouter/res/values-es/strings.xml b/v7/mediarouter/res/values-es/strings.xml
index 9f108fe..52a886f 100644
--- a/v7/mediarouter/res/values-es/strings.xml
+++ b/v7/mediarouter/res/values-es/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de enviar"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de enviar. Desconectado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de enviar. Conectando"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de enviar. Conectado"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Enviar a"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mostrar"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ocultar"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada del álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control deslizante de volumen"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"No se ha seleccionado ningún medio"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"No hay información disponible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Enviando pantalla"</string>
diff --git a/v7/mediarouter/res/values-et-rEE/strings.xml b/v7/mediarouter/res/values-et-rEE/strings.xml
index 3fab845..ce1847a 100644
--- a/v7/mediarouter/res/values-et-rEE/strings.xml
+++ b/v7/mediarouter/res/values-et-rEE/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Süsteem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Seadmed"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Ülekandenupp"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Ülekandenupp. Ühendus on katkestatud"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Ülekandenupp. Ühendamine"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Ülekandenupp. Ühendatud"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Ülekandmine seadmesse"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Seadmete otsimine"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Katkesta ühendus"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Laiendamine"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ahendamine"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumi kujundus"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Helitugevuse liugur"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Meediat pole valitud"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Teave puudub"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekraanikuva ülekandmine"</string>
diff --git a/v7/mediarouter/res/values-eu-rES/strings.xml b/v7/mediarouter/res/values-eu-rES/strings.xml
index bae67ed..c03d73b 100644
--- a/v7/mediarouter/res/values-eu-rES/strings.xml
+++ b/v7/mediarouter/res/values-eu-rES/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Gailuak"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Igorri botoia"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Igortzeko botoia. Deskonektatuta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Igortzeko botoia. Konektatzen"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Igortzeko botoia. Konektatuta"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Igorri hona"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Gailuak bilatzen"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Deskonektatu"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zabaldu"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Tolestu"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumaren azala"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Bolumenaren graduatzailea"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ez da hautatu multimedia-edukirik"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Ez dago informaziorik"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Pantaila igortzen"</string>
diff --git a/v7/mediarouter/res/values-fa/strings.xml b/v7/mediarouter/res/values-fa/strings.xml
index eb34931..7cc9fbc 100644
--- a/v7/mediarouter/res/values-fa/strings.xml
+++ b/v7/mediarouter/res/values-fa/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"سیستم"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"دستگاه‌ها"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"دکمه ارسال محتوا"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"دکمه فرستادن. ارتباط قطع شد"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"دکمه فرستادن. درحال مرتبط‌سازی"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"دکمه فرستادن. مرتبط شد"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ارسال محتوا به"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"پیدا کردن دستگاه‌ها"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"قطع ارتباط"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"بزرگ کردن"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"کوچک کردن"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"عکس روی جلد آلبوم"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"لغزنده میزان صدا"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"رسانه انتخاب نشده است"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"اطلاعات در دسترس نیست"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"درحال فرستادن صفحه"</string>
diff --git a/v7/mediarouter/res/values-fi/strings.xml b/v7/mediarouter/res/values-fi/strings.xml
index f37317a..2465802 100644
--- a/v7/mediarouter/res/values-fi/strings.xml
+++ b/v7/mediarouter/res/values-fi/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Järjestelmä"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Laitteet"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-painike"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-painike. Yhteys katkaistu"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-painike. Yhdistetään"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-painike. Yhdistetty"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Suoratoiston kohde"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Etsitään laitteita"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Katkaise yhteys"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Laajenna"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Tiivistä"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumin kansikuva"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Äänenvoimakkuuden liukusäädin"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ei valittua mediaa."</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Tietoja ei ole saatavilla"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Suoratoistetaan näyttöä"</string>
diff --git a/v7/mediarouter/res/values-fr-rCA/strings.xml b/v7/mediarouter/res/values-fr-rCA/strings.xml
index 5719479..fd1ea8c 100644
--- a/v7/mediarouter/res/values-fr-rCA/strings.xml
+++ b/v7/mediarouter/res/values-fr-rCA/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Bouton Diffuser"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Bouton Diffuser. Déconnecté"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Bouton Diffuser. Connexion en cours…"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Bouton Diffuser. Connecté"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Diffuser sur"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Se déconnecter"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Développer"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Réduire"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Image de l\'album"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Curseur de réglage du volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Aucun média sélectionné"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Aucune information disponible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Diffusion de l\'écran en cours"</string>
diff --git a/v7/mediarouter/res/values-fr/strings.xml b/v7/mediarouter/res/values-fr/strings.xml
index 6ce8329..8c96485 100644
--- a/v7/mediarouter/res/values-fr/strings.xml
+++ b/v7/mediarouter/res/values-fr/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Icône Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Icône Cast. Déconnecté"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Icône Cast. Connexion…"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Icône Cast. Connecté"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Caster sur"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils en cours…"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Déconnecter"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Développer"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Réduire"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Image de l\'album"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Curseur de volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Aucun média sélectionné"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Aucune information disponible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Diffusion de l\'écran en cours…"</string>
diff --git a/v7/mediarouter/res/values-gl-rES/strings.xml b/v7/mediarouter/res/values-gl-rES/strings.xml
index c922b68..d6d1888 100644
--- a/v7/mediarouter/res/values-gl-rES/strings.xml
+++ b/v7/mediarouter/res/values-gl-rES/strings.xml
@@ -19,16 +19,20 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botón de emitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botón de emitir. Desconectado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botón de emitir. Conectando"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botón de emitir. Conectado"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Emitir en"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Buscando dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
-    <string name="mr_controller_stop" msgid="4570331844078181931">"Parar de emitir"</string>
+    <string name="mr_controller_stop" msgid="4570331844078181931">"Deter emisión"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Pechar"</string>
     <string name="mr_controller_play" msgid="683634565969987458">"Reproduce"</string>
     <string name="mr_controller_pause" msgid="5451884435510905406">"Pausa"</string>
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ampliar"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Contraer"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Portada do álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Control desprazable do volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Non se seleccionaron recursos"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Non hai información dispoñible"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emisión de pantalla"</string>
diff --git a/v7/mediarouter/res/values-gu-rIN/strings.xml b/v7/mediarouter/res/values-gu-rIN/strings.xml
index e3be8fc..eb7a9b8 100644
--- a/v7/mediarouter/res/values-gu-rIN/strings.xml
+++ b/v7/mediarouter/res/values-gu-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"સિસ્ટમ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ઉપકરણો"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"કાસ્ટ કરો બટન"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"કાસ્ટ કરો બટન. ડિસ્કનેક્ટ કર્યું"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"કાસ્ટ કરો બટન. કનેક્ટ થઈ રહ્યું છે"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"કાસ્ટ કરો બટન. કનેક્ટ થયું"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"આના પર કાસ્ટ કરો"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ઉપકરણો શોધી રહ્યાં છીએ"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ડિસ્કનેક્ટ કરો"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"વિસ્તૃત કરો"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"સંકુચિત કરો"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"આલ્બમ કલા"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"વૉલ્યુમ સ્લાઇડર"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"કોઈ મીડિયા પસંદ કરેલ નથી"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"કોઈ માહિતી ઉપલબ્ધ નથી"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"સ્ક્રીનને કાસ્ટ કરી રહ્યાં છે"</string>
diff --git a/v7/mediarouter/res/values-hi/strings.xml b/v7/mediarouter/res/values-hi/strings.xml
index 9d0650b..e74967e 100644
--- a/v7/mediarouter/res/values-hi/strings.xml
+++ b/v7/mediarouter/res/values-hi/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"डिवाइस"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट करें बटन"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट करें बटन. डिस्कनेक्ट है"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट करें बटन. कनेक्ट हो रहा है"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट करें बटन. कनेक्ट है"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"इस पर कास्‍ट करें"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"डिवाइस ढूंढ रहा है"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिस्कनेक्ट करें"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तृत करें"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संक्षिप्त करें"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"एल्बम आर्ट"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"वॉल्यूम स्लाइडर"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"कोई मीडिया चयनित नहीं है"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"कोई जानकारी उपलब्‍ध नहीं"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्ट हो रही है"</string>
diff --git a/v7/mediarouter/res/values-hr/strings.xml b/v7/mediarouter/res/values-hr/strings.xml
index 371088b..f50d748 100644
--- a/v7/mediarouter/res/values-hr/strings.xml
+++ b/v7/mediarouter/res/values-hr/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sustav"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Gumb za emitiranje"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Gumb za emitiranje. Veza prekinuta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Gumb za emitiranje. Povezivanje"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Gumb za emitiranje. Povezan"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Emitiranje na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Traženje uređaja"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširivanje"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sažimanje"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Naslovnica albuma"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Klizač za glasnoću"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nije odabran nijedan medij"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Informacije nisu dostupne"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Emitiranje zaslona"</string>
diff --git a/v7/mediarouter/res/values-hu/strings.xml b/v7/mediarouter/res/values-hu/strings.xml
index a3d6990..6420985 100644
--- a/v7/mediarouter/res/values-hu/strings.xml
+++ b/v7/mediarouter/res/values-hu/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Rendszer"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Eszközök"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Átküldés gomb"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Átküldés gomb. Kapcsolat bontva"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Átküldés gomb. Csatlakozás"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Átküldés gomb. Csatlakoztatva"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Átküldés ide"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Eszközök keresése"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Leválasztás"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Kibontás"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Összecsukás"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Lemezborító"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Hangerőszabályzó"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nincs média kiválasztva"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nincs információ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Képernyőtartalom átküldése"</string>
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy-rAM/strings.xml
index a8c1cf3..6e46af8 100644
--- a/v7/mediarouter/res/values-hy-rAM/strings.xml
+++ b/v7/mediarouter/res/values-hy-rAM/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Համակարգ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Սարքեր"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Հեռարձակման կոճակ"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Հեռարձակման կոճակ: Սարքն անջատված է"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Հեռարձակման կոճակ: Սարքը կապակցվում է"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Հեռարձակման կոճակ: Սարքը կապակցված է"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Ընտրեք սարքը"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Սարքերի որոնում"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Անջատել"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Ընդարձակել"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Կոծկել"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Ալբոմի շապիկ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ձայնի ուժգնության կարգավորիչ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Մեդիա ֆայլեր չեն ընտրվել"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Տեղեկությունները հասանելի չեն"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Էկրանը հեռարձակվում է"</string>
diff --git a/v7/mediarouter/res/values-in/strings.xml b/v7/mediarouter/res/values-in/strings.xml
index 4bc0852..43e0c62 100644
--- a/v7/mediarouter/res/values-in/strings.xml
+++ b/v7/mediarouter/res/values-in/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Perangkat"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Tombol transmisi"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tombol transmisi. Terputus"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tombol transmisi. Menghubungkan"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tombol transmisi. Terhubung"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmisikan ke"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari perangkat"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Luaskan"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Ciutkan"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Sampul album"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Bilah geser volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Tidak ada media yang dipilih"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Tidak ada info yang tersedia"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmisi layar"</string>
diff --git a/v7/mediarouter/res/values-is-rIS/strings.xml b/v7/mediarouter/res/values-is-rIS/strings.xml
index 08e41e6..53de253 100644
--- a/v7/mediarouter/res/values-is-rIS/strings.xml
+++ b/v7/mediarouter/res/values-is-rIS/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Kerfi"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Tæki"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Útsendingarhnappur"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Útsendingarhnappur. Aftengt"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Útsendingarhnappur. Tengist"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Útsendingarhnappur. Tengt"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Senda út í"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Leitað að tækjum"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Aftengjast"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Stækka"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Minnka"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Plötuumslag"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Hljóðstyrkssleði"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Enginn miðill valinn"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Engar upplýsingar í boði"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skjár sendur út"</string>
diff --git a/v7/mediarouter/res/values-it/strings.xml b/v7/mediarouter/res/values-it/strings.xml
index 87b570c..df6bbfe 100644
--- a/v7/mediarouter/res/values-it/strings.xml
+++ b/v7/mediarouter/res/values-it/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivi"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Pulsante Trasmetti"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Pulsante Trasmetti. Disconnesso"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Pulsante Trasmetti. Connessione in corso"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Pulsante Trasmetti. Connesso"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Trasmetti a"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Ricerca di dispositivi in corso"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Scollega"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Espandi"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Comprimi"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Copertina"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Dispositivo di scorrimento del volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nessun contenuto multimediale selezionato"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nessuna informazione disponibile"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Trasmissione dello schermo in corso"</string>
diff --git a/v7/mediarouter/res/values-iw/strings.xml b/v7/mediarouter/res/values-iw/strings.xml
index 8b52adf..be705a6 100644
--- a/v7/mediarouter/res/values-iw/strings.xml
+++ b/v7/mediarouter/res/values-iw/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"מערכת"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"מכשירים"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"‏לחצן הפעלת Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"‏לחצן הפעלת Cast. מנותק"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"‏לחצן הפעלת Cast. מתחבר"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"‏לחצן הפעלת Cast. מחובר"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"העבר אל"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"מחפש מכשירים"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"נתק"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"הרחב"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"כווץ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"עטיפת אלבום"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"מחוון עוצמה"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"לא נבחרה מדיה"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"אין מידע זמין"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"העברת מסך מתבצעת"</string>
diff --git a/v7/mediarouter/res/values-ja/strings.xml b/v7/mediarouter/res/values-ja/strings.xml
index b126965..75d75d6 100644
--- a/v7/mediarouter/res/values-ja/strings.xml
+++ b/v7/mediarouter/res/values-ja/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"システム"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"端末"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"キャストアイコン"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"キャスト アイコン。接続解除済み"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"キャスト アイコン。接続中"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"キャスト アイコン。接続済み"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"キャストするデバイス"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"端末を検索しています"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"接続を解除"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"折りたたむ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"アルバムアート"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量スライダー"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"メディアが選択されていません"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"情報がありません"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"画面をキャストしています"</string>
diff --git a/v7/mediarouter/res/values-ka-rGE/strings.xml b/v7/mediarouter/res/values-ka-rGE/strings.xml
index 046e361..3bcc513 100644
--- a/v7/mediarouter/res/values-ka-rGE/strings.xml
+++ b/v7/mediarouter/res/values-ka-rGE/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"სისტემა"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"მოწყობილობები"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ტრანსლირების ღილაკი"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ტრანსლირების ღილაკი. გათიშული"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ტრანსლირების ღილაკი. მიმდინარეობს დაკავშირება"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ტრანსლირების ღილაკი. დაკავშირებული"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ტრანსლირებული"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"მიმდინარეობს მოწყობილობების მოძიება"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"კავშირის გაწყვეტა"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"გაშლა"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ჩაკეცვა"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ალბომის გარეკანი"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ხმის სლაიდერი"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"მედია არჩეული არ არის"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ინფორმაცია არ არის ხელმისაწვდომი"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"მიმდინარეობს ეკრანის გადაცემა"</string>
diff --git a/v7/mediarouter/res/values-kk-rKZ/strings.xml b/v7/mediarouter/res/values-kk-rKZ/strings.xml
index 5cf4e5a..43e75b7 100644
--- a/v7/mediarouter/res/values-kk-rKZ/strings.xml
+++ b/v7/mediarouter/res/values-kk-rKZ/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Жүйе"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Құрылғылар"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Трансляциялау түймесі"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"\"Трансляциялау\" түймесі. Ажыратулы"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"\"Трансляциялау\" түймесі. Қосылуда"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"\"Трансляциялау\" түймесі. Қосылды"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Келесіге трансляциялау"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Құрылғыларды табу"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ажырату"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Жаю"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Жию"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Альбом шебері"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Дыбыс деңгейінің жүгірткісі"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ешбір тасушы таңдалмаған"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Қол жетімді ақпарат жоқ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Экранды трансляциялау"</string>
diff --git a/v7/mediarouter/res/values-km-rKH/strings.xml b/v7/mediarouter/res/values-km-rKH/strings.xml
index fd05668..44d88f4 100644
--- a/v7/mediarouter/res/values-km-rKH/strings.xml
+++ b/v7/mediarouter/res/values-km-rKH/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ប្រព័ន្ធ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ឧបករណ៍"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ប៊ូតុងខាស"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ខាសប៊ូតុង៖ បានកាត់ផ្តាច់"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ខាសប៊ូតុង៖ កំពុងភ្ជាប់"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ខាសប៊ូតុង៖ បានភ្ជាប់ហើយ"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ខាសទៅ"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ស្វែងរកឧបករណ៍"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ផ្ដាច់"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ពង្រីក"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"បង្រួម"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ស្នាដៃសិល្បៈអាល់ប៊ុម"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"របារកម្រិតសំឡេង"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"គ្មានការជ្រើសមេឌៀទេ"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"មិនមានព័ត៌មានទេ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"កំពុងខាសអេក្រង់"</string>
diff --git a/v7/mediarouter/res/values-kn-rIN/strings.xml b/v7/mediarouter/res/values-kn-rIN/strings.xml
index 9cae5be..193d449 100644
--- a/v7/mediarouter/res/values-kn-rIN/strings.xml
+++ b/v7/mediarouter/res/values-kn-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ಸಿಸ್ಟಂ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ಸಾಧನಗಳು"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ಬಿತ್ತರಿಸು ಬಟನ್‌"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ಬಿತ್ತರಿಸು ಬಟನ್‌. ಸಂಪರ್ಕಿತಗೊಂಡಿದೆ"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ಇದಕ್ಕೆ ಬಿತ್ತರಿಸಿ"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ಸಾಧನಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸು"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ವಿಸ್ತರಿಸು"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ಸಂಕುಚಿಸು"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ಆಲ್ಬಮ್ ಕಲೆ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ವಾಲ್ಯೂಮ್ ಸ್ಲೈಡರ್"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ಯಾವುದೇ ಮಾಧ್ಯಮ ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ಯಾವುದೇ ಮಾಹಿತಿ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ಪರದೆಯನ್ನು ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
diff --git a/v7/mediarouter/res/values-ko/strings.xml b/v7/mediarouter/res/values-ko/strings.xml
index 7f53382..242560a 100644
--- a/v7/mediarouter/res/values-ko/strings.xml
+++ b/v7/mediarouter/res/values-ko/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"시스템"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"기기"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"전송 버튼"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"전송 버튼. 연결 해제됨"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"전송 버튼. 연결 중"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"전송 버튼. 연결됨"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"전송할 기기"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"기기를 찾는 중"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"연결 해제"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"펼치기"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"접기"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"앨범아트"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"볼륨 슬라이더"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"선택한 미디어 없음"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"정보가 없습니다."</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"화면 전송 중"</string>
diff --git a/v7/mediarouter/res/values-ky-rKG/strings.xml b/v7/mediarouter/res/values-ky-rKG/strings.xml
index 99201dc..0898ade 100644
--- a/v7/mediarouter/res/values-ky-rKG/strings.xml
+++ b/v7/mediarouter/res/values-ky-rKG/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Тутум"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Түзмөктөр"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Тышкы экранга чыгаруу баскычы"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Тышкы экранга чыгаруу баскычы. Түзмөк ажырап турат."</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Тышкы экранга чыгаруу баскычы. Түзмөк туташууда"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Тышкы экранга чыгаруу баскычы. Түзмөк туташып турат"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Төмөнкүгө чыгаруу"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Түзмөктөр изделүүдө"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ажыратуу"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Жайып көрсөтүү"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Жыйыштыруу"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Альбом мукабасы"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Үндү катуулатуучу сыдырма"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Бир да медиа файл тандалган жок"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Эч маалымат жок"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Тышкы экранга чыгарылууда"</string>
diff --git a/v7/mediarouter/res/values-lo-rLA/strings.xml b/v7/mediarouter/res/values-lo-rLA/strings.xml
index 2765364..65b3472 100644
--- a/v7/mediarouter/res/values-lo-rLA/strings.xml
+++ b/v7/mediarouter/res/values-lo-rLA/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ລະບົບ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ອຸປະກອນ"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ປຸ່ມ​ຄາ​ສ​ທ໌"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ປຸ່ມສົ່ງສັນຍານ. ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ປຸ່ມສົ່ງສັນຍານ. ກຳລັງເຊື່ອມຕໍ່"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ປຸ່ມສົ່ງສັນຍານ. ເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ຄາ​ສ​ທ໌​ຫາ"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ກຳລັງ​ຊອກ​ຫາ​ອຸ​ປະ​ກອນ"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ຕັດການເຊື່ອມຕໍ່"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ຂະຫຍາຍ"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ຫຍໍ້ລົງ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ໜ້າປົກອະລະບໍ້າ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ຕົວປັບລະດັບສຽງ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ບໍ່​ໄດ້​ເລືອກ​ມີ​ເດຍ​ໃດ​ໄວ້"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ບໍ່​ມີ​ຂໍ້​ມູນ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ການສົ່ງພາບໜ້າຈໍ"</string>
diff --git a/v7/mediarouter/res/values-lt/strings.xml b/v7/mediarouter/res/values-lt/strings.xml
index 208752f..35931cf 100644
--- a/v7/mediarouter/res/values-lt/strings.xml
+++ b/v7/mediarouter/res/values-lt/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Įrenginiai"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Perdavimo mygtukas"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Perdavimo mygtukas. Atsijungta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Perdavimo mygtukas. Prisijungiama"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Perdavimo mygtukas. Prisijungta"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Perduoti į"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Randami įrenginiai"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Atjungti"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Išskleisti"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sutraukti"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumo viršelis"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Garsumo šliaužiklis"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nepasirinkta jokia medija"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Informacija nepasiekiama"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Perduodamas ekranas"</string>
diff --git a/v7/mediarouter/res/values-lv/strings.xml b/v7/mediarouter/res/values-lv/strings.xml
index 832a3ba..c146174 100644
--- a/v7/mediarouter/res/values-lv/strings.xml
+++ b/v7/mediarouter/res/values-lv/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistēma"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Ierīces"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Apraides poga"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Apraides poga. Savienojums pārtraukts"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Apraides poga. Notiek savienojuma izveide"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Apraides poga. Savienojums izveidots"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Apraidīšana uz ierīci"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Notiek ierīču meklēšana"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Atvienot"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Izvērst"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Sakļaut"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albuma vāciņš"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Skaļuma slīdnis"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nav atlasīti multivides faili"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nav pieejama informācija"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Notiek ekrāna apraide"</string>
diff --git a/v7/mediarouter/res/values-mk-rMK/strings.xml b/v7/mediarouter/res/values-mk-rMK/strings.xml
index 726e285..6fd428f 100644
--- a/v7/mediarouter/res/values-mk-rMK/strings.xml
+++ b/v7/mediarouter/res/values-mk-rMK/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уреди"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Копчето за Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Копче за Cast. Исклучено"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Копче за Cast. Се поврзува"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Копче за Cast. Поврзано"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Емитувај на"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Наоѓање уреди"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Исклучи"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Собери"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Корица на албум"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Лизгач за јачина на звук"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Не се избрани медиуми"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нема достапни информации"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Екранот се емитува"</string>
diff --git a/v7/mediarouter/res/values-ml-rIN/strings.xml b/v7/mediarouter/res/values-ml-rIN/strings.xml
index b1d2cbe..63a37f8 100644
--- a/v7/mediarouter/res/values-ml-rIN/strings.xml
+++ b/v7/mediarouter/res/values-ml-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"സിസ്റ്റം"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ഉപകരണങ്ങൾ"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ടാപ്പുചെയ്യുക"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"കാസ്റ്റ് ബട്ടൺ. വിച്ഛേദിച്ചു"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"കാസ്റ്റ് ബട്ടൺ. കണക്‌റ്റുചെയ്യുന്നു"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"കാസ്റ്റ് ബട്ടൺ. കണക്റ്റുചെയ്തു"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ഇതിലേക്ക് കാസ്റ്റുചെയ്യുക"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ഉപകരണങ്ങൾ കണ്ടെത്തുന്നു"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"വിച്ഛേദിക്കുക"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"വികസിപ്പിക്കുക"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ചുരുക്കുക"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ആൽബം ആർട്ട്"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"വോളിയം സ്ലൈഡർ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"മീഡിയയൊന്നും തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"വിവരങ്ങളൊന്നും ലഭ്യമല്ല"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"സ്‌ക്രീൻ കാസ്റ്റുചെയ്യുന്നു"</string>
diff --git a/v7/mediarouter/res/values-mn-rMN/strings.xml b/v7/mediarouter/res/values-mn-rMN/strings.xml
index d07d314..1197bfe 100644
--- a/v7/mediarouter/res/values-mn-rMN/strings.xml
+++ b/v7/mediarouter/res/values-mn-rMN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Төхөөрөмжүүд"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Дамжуулах товчлуур"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Дамжуулах товчлуур. Салсан"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Дамжуулах товчлуур. Холбож байна"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Дамжуулах товчлуур. Холбогдсон"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Дамжуулах"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Төхөөрөмж хайж байна"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Салгах"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Дэлгэх"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Хураах"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Цомгийн зураг"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Дууны түвшин тааруулагч"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ямар ч медиа сонгоогүй"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Мэдээлэл байхгүй байна"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Дэлгэцийг дамжуулж байна"</string>
diff --git a/v7/mediarouter/res/values-mr-rIN/strings.xml b/v7/mediarouter/res/values-mr-rIN/strings.xml
index 4e24aff..40d7d66 100644
--- a/v7/mediarouter/res/values-mr-rIN/strings.xml
+++ b/v7/mediarouter/res/values-mr-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"डिव्हाइसेस"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट बटण"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट बटण. डिस्कनेक्ट केले"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट बटण. कनेक्ट करीत आहे"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट बटण. कनेक्ट केले"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"यावर कास्ट करा"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"डिव्हाइसेस शोधत आहे"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिस्‍कनेक्‍ट करा"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तृत करा"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संकुचित करा"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"अल्बम कला"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"व्हॉल्यूम स्लायडर"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"मीडिया निवडला नाही"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"कोणतीही माहिती उपलब्ध नाही"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्‍ट करीत आहे"</string>
diff --git a/v7/mediarouter/res/values-ms-rMY/strings.xml b/v7/mediarouter/res/values-ms-rMY/strings.xml
index 2a4cd1c..cc32b96 100644
--- a/v7/mediarouter/res/values-ms-rMY/strings.xml
+++ b/v7/mediarouter/res/values-ms-rMY/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Peranti"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Butang Hantar"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butang hantar. Sambungan diputuskan"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butang hantar. Menyambung"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butang hantar. Disambungkan"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Hantar ke"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari peranti"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Kembangkan"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Runtuhkan"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Seni album"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Peluncur kelantangan"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Tiada media dipilih"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Maklumat tidak tersedia"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Menghantar skrin"</string>
diff --git a/v7/mediarouter/res/values-my-rMM/strings.xml b/v7/mediarouter/res/values-my-rMM/strings.xml
index eca8835..e31cc92 100644
--- a/v7/mediarouter/res/values-my-rMM/strings.xml
+++ b/v7/mediarouter/res/values-my-rMM/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"စနစ်"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"စက်ပစ္စည်းများ"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ကာစ်တ်လုပ်ရန် ခလုတ်"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ကာစ်ခလုတ်။ ချိတ်ဆက်မထားပါ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ကာစ်ခလုတ်။ ချိတ်ဆက်နေသည်"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ကာစ်ခလုတ်။ ချိတ်ဆက်ထားသည်"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"သို့ ကာစ်တ်လုပ်ရန်"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"စက်ပစ္စည်းများ ရှာဖွေခြင်း"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ဆက်သွယ်မှု ဖြတ်ရန်"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ဖြန့်ချရန်၃"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ခေါက်သိမ်းရန်..."</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"အယ်လ်ဘမ်ပုံ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"အသံအတိုးအကျယ်ချိန်သည့် ဆလိုက်ဒါ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"မည်သည့်မီဒီမှ မရွေးချယ်ထားပါ"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"အချက်အလက် မရရှိနိုင်ပါ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"တည်းဖြတ်ရေး မျက်နှာပြင်"</string>
diff --git a/v7/mediarouter/res/values-nb/strings.xml b/v7/mediarouter/res/values-nb/strings.xml
index 27f9f03..97ca73c 100644
--- a/v7/mediarouter/res/values-nb/strings.xml
+++ b/v7/mediarouter/res/values-nb/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-ikonet"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knappen. Frakoblet"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knappen. Kobler til"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knappen. Tilkoblet"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Cast til"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Finner enheter"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Koble fra"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Utvid"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skjul"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumgrafikk"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Glidebryter for volum"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Du har ikke valgt noen medier"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Ingen informasjon er tilgjengelig"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Caster skjermen"</string>
diff --git a/v7/mediarouter/res/values-ne-rNP/strings.xml b/v7/mediarouter/res/values-ne-rNP/strings.xml
index 6abadbf..0e48b45 100644
--- a/v7/mediarouter/res/values-ne-rNP/strings.xml
+++ b/v7/mediarouter/res/values-ne-rNP/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"प्रणाली"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"उपकरणहरू"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast बटन"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast बटन। जडान विच्छेद भयो"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast बटन। जडान हुँदै"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast बटन। जडान भयो"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"यसमा Cast गर्नुहोस्"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"यन्त्रहरू पत्ता लगाउँदै"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"विच्छेद गर्नुहोस्"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"विस्तार गर्नुहोस्"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"संक्षिप्त पार्नुहोस्"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"एल्बम आर्ट"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"भोल्युमको स्लाइडर"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"कुनै मिडिया चयन भएको छैन"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"जानकारी उपलब्ध छैन"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रिन cast गर्दै"</string>
diff --git a/v7/mediarouter/res/values-nl/strings.xml b/v7/mediarouter/res/values-nl/strings.xml
index 4a9346d..5c899fd 100644
--- a/v7/mediarouter/res/values-nl/strings.xml
+++ b/v7/mediarouter/res/values-nl/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Systeem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Apparaten"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-icoon"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-icoon. Verbinding verbroken"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-icoon. Verbinding maken"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-icoon. Verbonden"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Casten naar"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Apparaten zoeken"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Loskoppelen"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Uitvouwen"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Samenvouwen"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albumhoes"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volumeschuifregelaar"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Geen media geselecteerd"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Geen informatie beschikbaar"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Scherm casten"</string>
diff --git a/v7/mediarouter/res/values-pa-rIN/strings.xml b/v7/mediarouter/res/values-pa-rIN/strings.xml
index 842a8b4..258529d 100644
--- a/v7/mediarouter/res/values-pa-rIN/strings.xml
+++ b/v7/mediarouter/res/values-pa-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ਸਿਸਟਮ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"ਡਿਵਾਈਸਾਂ"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ਕਾਸਟ ਬਟਨ"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ਕਾਸਟ ਬਟਨ। ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ਕਾਸਟ ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"ਇਸ ਨਾਲ ਕਾਸਟ ਕਰੋ"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"ਡਿਵਾਈਸਾਂ ਲੱਭ ਰਿਹਾ ਹੈ"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ਬੰਦ ਕਰੋ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ਐਲਬਮ ਆਰਟ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ਵੌਲਯੂਮ ਸਲਾਈਡਰ"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ਕੋਈ ਵੀ ਮੀਡੀਆ ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ਕੋਈ ਜਾਣਕਾਰੀ ਉਪਲਬਧ ਨਹੀਂ"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"ਸਕ੍ਰੀਨ ਜੋੜ ਰਿਹਾ ਹੈ"</string>
diff --git a/v7/mediarouter/res/values-pl/strings.xml b/v7/mediarouter/res/values-pl/strings.xml
index d66be1a..0320550 100644
--- a/v7/mediarouter/res/values-pl/strings.xml
+++ b/v7/mediarouter/res/values-pl/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Urządzenia"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Przycisk Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Przycisk Prześlij ekran. Rozłączono"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Przycisk Prześlij ekran. Łączę"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Przycisk Prześlij ekran. Połączono"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Przesyłaj na"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Znajdowanie urządzeń"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odłącz"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozwiń"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Zwiń"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Okładka albumu"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Suwak głośności"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nie wybrano multimediów"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Brak informacji"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Przesyłam ekran"</string>
diff --git a/v7/mediarouter/res/values-pt-rBR/strings.xml b/v7/mediarouter/res/values-pt-rBR/strings.xml
index 4d7e6cc..2fd84b1 100644
--- a/v7/mediarouter/res/values-pt-rBR/strings.xml
+++ b/v7/mediarouter/res/values-pt-rBR/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão \"Transmitir\". Desconectado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão \"Transmitir\". Conectando"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão \"Transmitir\". Conectado"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Localizando dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Recolher"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Arte do álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controle deslizante de volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhuma mídia selecionada"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitindo a tela"</string>
diff --git a/v7/mediarouter/res/values-pt-rPT/strings.xml b/v7/mediarouter/res/values-pt-rPT/strings.xml
index 0c68b92..b62f363 100644
--- a/v7/mediarouter/res/values-pt-rPT/strings.xml
+++ b/v7/mediarouter/res/values-pt-rPT/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão Transmitir. Desligado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão Transmitir. A ligar..."</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão Transmitir. Ligado"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"A localizar dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desassociar"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Reduzir"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Imagem do álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controlo de deslize do volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhum suporte multimédia selecionado"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"A transmitir o ecrã"</string>
diff --git a/v7/mediarouter/res/values-pt/strings.xml b/v7/mediarouter/res/values-pt/strings.xml
index 4d7e6cc..2fd84b1 100644
--- a/v7/mediarouter/res/values-pt/strings.xml
+++ b/v7/mediarouter/res/values-pt/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Botão Transmitir"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Botão \"Transmitir\". Desconectado"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Botão \"Transmitir\". Conectando"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Botão \"Transmitir\". Conectado"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmitir para"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Localizando dispositivos"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Desconectar"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Expandir"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Recolher"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Arte do álbum"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Controle deslizante de volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nenhuma mídia selecionada"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nenhuma informação disponível"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Transmitindo a tela"</string>
diff --git a/v7/mediarouter/res/values-ro/strings.xml b/v7/mediarouter/res/values-ro/strings.xml
index 9fe26a9..769a0ac 100644
--- a/v7/mediarouter/res/values-ro/strings.xml
+++ b/v7/mediarouter/res/values-ro/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispozitive"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Butonul de proiecție"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butonul de proiecție. Deconectat"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butonul de proiecție. Se conectează"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butonul de proiecție. Conectat"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Proiectați pe"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Se caută dispozitive"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Deconectați-vă"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Extindeți"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Restrângeți"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Grafica albumului"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Glisor pentru volum"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Niciun fișier media selectat"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nu sunt disponibile informații"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Se proiectează ecranul"</string>
diff --git a/v7/mediarouter/res/values-ru/strings.xml b/v7/mediarouter/res/values-ru/strings.xml
index 4607a8c..9bd2170 100644
--- a/v7/mediarouter/res/values-ru/strings.xml
+++ b/v7/mediarouter/res/values-ru/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляции"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляции. Устройство отключено."</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляции. Устройство подключается."</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляции. Устройство подключено."</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Выберите устройство"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Поиск устройств…"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Отключить"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Развернуть"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Свернуть"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Обложка"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Регулятор громкости"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медиафайл не выбран"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Данных нет"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Подключение к удаленному монитору"</string>
diff --git a/v7/mediarouter/res/values-si-rLK/strings.xml b/v7/mediarouter/res/values-si-rLK/strings.xml
index 144a0d5..baeac3e 100644
--- a/v7/mediarouter/res/values-si-rLK/strings.xml
+++ b/v7/mediarouter/res/values-si-rLK/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"පද්ධතිය"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"උපාංග"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"විකාශ බොත්තම"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"විකාශ බොත්තම. විසන්ධි කරන ලදී"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"විකාශ බොත්තම සම්බන්ධ කරමින්"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"විකාශ බොත්තම සම්බන්ධ කරන ලදී"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"විකාශය"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"උපාංග සෙවීම"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"විසන්ධි කරන්න"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"දිග හරින්න"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"හකුළන්න"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ඇල්බම කලාව"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"හඬ පරිමා ස්ලයිඩරය"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"මාධ්‍යය තෝරා නැත"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ලබා ගත හැකි තොරතුරු නොමැත"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"විකාශ තිරය"</string>
diff --git a/v7/mediarouter/res/values-sk/strings.xml b/v7/mediarouter/res/values-sk/strings.xml
index b546bde..4a0f4bf 100644
--- a/v7/mediarouter/res/values-sk/strings.xml
+++ b/v7/mediarouter/res/values-sk/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zariadenia"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Tlačidlo prenosu"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tlačidlo prenosu. Odpojené"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tlačidlo prenosu. Pripája sa"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tlačidlo prenosu. Pripojené"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Prenos do"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Vyhľadávanie zariadení"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Odpojiť"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Rozbaliť"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Zbaliť"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Obrázok albumu"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Posúvač hlasitosti"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nie sú vybrané žiadne médiá"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nie sú k dispozícii žiadne informácie"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prenáša sa obrazovka"</string>
diff --git a/v7/mediarouter/res/values-sl/strings.xml b/v7/mediarouter/res/values-sl/strings.xml
index 110c548..4ca2bdc 100644
--- a/v7/mediarouter/res/values-sl/strings.xml
+++ b/v7/mediarouter/res/values-sl/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Naprave"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Gumb za predvajanje"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Gumb za predvajanje. Povezava je prekinjena."</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Gumb za predvajanje. Vzpostavljanje povezave."</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Gumb za predvajanje. Povezava je vzpostavljena."</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Predvajanje prek:"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Iskanje naprav"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini povezavo"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Razširi"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Strni"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Naslovnica albuma"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Drsnik za glasnost"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ni izbrane predstavnosti"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Podatki niso na voljo"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Predvajanje zaslona"</string>
diff --git a/v7/mediarouter/res/values-sq-rAL/strings.xml b/v7/mediarouter/res/values-sq-rAL/strings.xml
index 8ed93c3..45e3a34 100644
--- a/v7/mediarouter/res/values-sq-rAL/strings.xml
+++ b/v7/mediarouter/res/values-sq-rAL/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistemi"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Pajisjet"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Butoni i transmetimit"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Butoni i transmetimit. Je i shkëputur"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Butoni i transmetimit. Po lidhet"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Butoni i transmetimit. Je i lidhur"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmeto te"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Gjetja e pajisjeve"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Shkëpute"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Zgjeroje"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Palose"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Kopertina e albumit"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Rrëshqitësi i volumit"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nuk u zgjodh asnjë media"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nuk jepet asnjë informacion"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Po transmeton ekranin"</string>
diff --git a/v7/mediarouter/res/values-sr/strings.xml b/v7/mediarouter/res/values-sr/strings.xml
index 5a72bd4..bddc045 100644
--- a/v7/mediarouter/res/values-sr/strings.xml
+++ b/v7/mediarouter/res/values-sr/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уређаји"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Дугме Пребаци"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Дугме Пребаци. Веза је прекинута"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Дугме Пребаци. Повезује се"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Дугме Пребаци. Повезан је"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Пребацујте на"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Проналажење уређаја"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Прекини везу"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Прошири"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Скупи"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Омот албума"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Клизач за јачину звука"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Нема изабраних медија"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Нису доступне никакве информације"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Пребацује се екран"</string>
diff --git a/v7/mediarouter/res/values-sv/strings.xml b/v7/mediarouter/res/values-sv/strings.xml
index 3724902..9597bf9 100644
--- a/v7/mediarouter/res/values-sv/strings.xml
+++ b/v7/mediarouter/res/values-sv/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Cast-knappen"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Cast-knappen. Frånkopplad"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Cast-knappen. Ansluter"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Cast-knappen. Ansluten"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Casta till"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Letar efter enheter"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Koppla från"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Utöka"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Komprimera"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Skivomslag"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Volymreglage"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Inga media har valts"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Det finns ingen information"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skärmen castas"</string>
diff --git a/v7/mediarouter/res/values-sw/strings.xml b/v7/mediarouter/res/values-sw/strings.xml
index f12fd5c..f7ae8f5 100644
--- a/v7/mediarouter/res/values-sw/strings.xml
+++ b/v7/mediarouter/res/values-sw/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Mfumo"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Vifaa"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Kitufe cha kutuma"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Kitufe cha kutuma. Kimeondolewa"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Kitufe cha kutuma. Kinaunganisha"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Kitufe cha kutuma. Kimeunganishwa"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Tuma kwenye"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Inatafuta vifaa"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ondoa"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Panua"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Kunja"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Sanaa ya albamu"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Kitelezi cha sauti"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Hakuna maudhui yaliyochaguliwa"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hakuna maelezo yaliyopatikana"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Inatuma skrini"</string>
diff --git a/v7/mediarouter/res/values-ta-rIN/strings.xml b/v7/mediarouter/res/values-ta-rIN/strings.xml
index c314178..0ef3fc3 100644
--- a/v7/mediarouter/res/values-ta-rIN/strings.xml
+++ b/v7/mediarouter/res/values-ta-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"அமைப்பு"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"சாதனங்கள்"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"திரையிடு பட்டன்"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"அனுப்புதல் பொத்தான். துண்டிக்கப்பட்டது"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"அனுப்புதல் பொத்தான். இணைக்கிறது"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"அனுப்புதல் பொத்தான். இணைக்கப்பட்டது"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"இதில் திரையிடு"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"சாதனங்களைத் தேடுகிறது"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"தொடர்பைத் துண்டி"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"விரிவாக்கு"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"சுருக்கு"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ஆல்பம் ஆர்ட்"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"ஒலியளவு ஸ்லைடர்"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"மீடியா எதுவும் தேர்ந்தெடுக்கப்படவில்லை"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"தகவல் எதுவுமில்லை"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"திரையை அனுப்புகிறீர்கள்"</string>
diff --git a/v7/mediarouter/res/values-te-rIN/strings.xml b/v7/mediarouter/res/values-te-rIN/strings.xml
index 59a4f19..c7ae34f 100644
--- a/v7/mediarouter/res/values-te-rIN/strings.xml
+++ b/v7/mediarouter/res/values-te-rIN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"సిస్టమ్"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"పరికరాలు"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ప్రసారం చేయి బటన్"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ప్రసార బటన్. డిస్‌కనెక్ట్ చేయబడింది"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ప్రసార బటన్. కనెక్ట్ చేస్తోంది"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ప్రసార బటన్. కనెక్ట్ చేయబడింది"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"దీనికి ప్రసారం చేయండి"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"పరికరాలను కనుగొంటోంది"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"డిస్‌కనెక్ట్ చేయి"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"విస్తరింపజేస్తుంది"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"కుదిస్తుంది"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ఆల్బమ్ ఆర్ట్"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"వాల్యూమ్ స్లయిడర్"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"మీడియా ఏదీ ఎంచుకోబడలేదు"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"సమాచారం అందుబాటులో లేదు"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"స్క్రీన్‌ను ప్రసారం చేస్తోంది"</string>
diff --git a/v7/mediarouter/res/values-th/strings.xml b/v7/mediarouter/res/values-th/strings.xml
index 1bfd091..219374c 100644
--- a/v7/mediarouter/res/values-th/strings.xml
+++ b/v7/mediarouter/res/values-th/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"ระบบ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"อุปกรณ์"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"ปุ่ม \"แคสต์\""</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"ปุ่ม \"แคสต์\" ยกเลิกการเชื่อมต่อ"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"ปุ่ม \"แคสต์\" กำลังเชื่อมต่อ"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"ปุ่ม \"แคสต์\" เชื่อมต่อแล้ว"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"แคสต์ไปยัง"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"กำลังค้นหาอุปกรณ์"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"ยกเลิกการเชื่อมต่อ"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"ขยาย"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"ยุบ"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"ปกอัลบั้ม"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"แถบเลื่อนปรับระดับเสียง"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"ไม่ได้เลือกสื่อไว้"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"ไม่มีข้อมูล"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"กำลังแคสต์หน้าจอ"</string>
diff --git a/v7/mediarouter/res/values-tl/strings.xml b/v7/mediarouter/res/values-tl/strings.xml
index 82396e1..e2bb7c7 100644
--- a/v7/mediarouter/res/values-tl/strings.xml
+++ b/v7/mediarouter/res/values-tl/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Mga Device"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Button na I-cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Button na I-cast. Nadiskonekta"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Button na I-cast. Kumokonekta"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Button na I-cast. Nakakonekta"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"I-cast sa"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Naghahanap ng mga device"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Idiskonekta"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Palawakin"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"I-collapse"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Album art"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Slider ng volume"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Walang piniling media"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Walang available na impormasyon"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Kina-cast ang screen"</string>
diff --git a/v7/mediarouter/res/values-tr/strings.xml b/v7/mediarouter/res/values-tr/strings.xml
index e639963..187682f 100644
--- a/v7/mediarouter/res/values-tr/strings.xml
+++ b/v7/mediarouter/res/values-tr/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Yayın düğmesi"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Yayınla düğmesi. Bağlantı kesildi"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Yayınla düğmesi. Bağlanıyor"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Yayınla düğmesi. Bağlandı"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Şuraya yayınla:"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Cihazlar bulunuyor"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Bağlantıyı kes"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Genişlet"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Daralt"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albüm kapağı"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ses düzeyi kaydırma çubuğu"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Medya seçilmedi"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Bilgi yok"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekran yayınlanıyor"</string>
diff --git a/v7/mediarouter/res/values-uk/strings.xml b/v7/mediarouter/res/values-uk/strings.xml
index a768e2c..8c2a16a 100644
--- a/v7/mediarouter/res/values-uk/strings.xml
+++ b/v7/mediarouter/res/values-uk/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Пристрої"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Кнопка трансляції"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Кнопка трансляції. Від’єднано"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Кнопка трансляції. Під’єднання"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Кнопка трансляції. Під’єднано"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Транслювати на"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Пошук пристроїв"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Відключити"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Розгорнути"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Згорнути"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Обкладинка альбому"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Повзунок гучності"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Медіа-файл не вибрано"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Немає даних"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Трансляція екрана"</string>
diff --git a/v7/mediarouter/res/values-ur-rPK/strings.xml b/v7/mediarouter/res/values-ur-rPK/strings.xml
index 5cb3b36..053373f 100644
--- a/v7/mediarouter/res/values-ur-rPK/strings.xml
+++ b/v7/mediarouter/res/values-ur-rPK/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"سسٹم"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"آلات"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"کاسٹ کرنے کا بٹن"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"کاسٹ کرنے کا بٹن۔ غیر منسلک ہے"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"کاسٹ کرنے کا بٹن۔ منسلک ہو رہا ہے"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"کاسٹ کرنے کا بٹن۔ منسلک ہے"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"اس میں کاسٹ کریں"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"آلات تلاش ہو رہے ہیں"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"غیر منسلک کریں"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"پھیلائیں"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"سکیڑیں"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"البم آرٹ"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"والیوم سلائیڈر"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"کوئی میڈیا منتخب نہیں ہے"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"کوئی معلومات دستیاب نہیں"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"سکرین کاسٹ ہو رہی ہے"</string>
diff --git a/v7/mediarouter/res/values-uz-rUZ/strings.xml b/v7/mediarouter/res/values-uz-rUZ/strings.xml
index 9955cdfd..9d1e455 100644
--- a/v7/mediarouter/res/values-uz-rUZ/strings.xml
+++ b/v7/mediarouter/res/values-uz-rUZ/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Tizim"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Qurilmalar"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Translatsiya tugmasi"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Translatsiya tugmasi. Uzildi"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Translatsiya tugmasi. Ulanmoqda"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Translatsiya tugmasi. Ulandi"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Quyidagiga translatsiya qilish:"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Qurilmalarni topish"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ulanishni uzish"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Yoyish"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Yig‘ish"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Albom muqovasi"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Ovoz balandligi slayderi"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Multimedia tanlamagan"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Hech qanday ma’lumot yo‘q"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Ekranni translatsiya qilish"</string>
diff --git a/v7/mediarouter/res/values-vi/strings.xml b/v7/mediarouter/res/values-vi/strings.xml
index 0080e3e..488b9f0 100644
--- a/v7/mediarouter/res/values-vi/strings.xml
+++ b/v7/mediarouter/res/values-vi/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Hệ thống"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Thiết bị"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Nút truyền"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Nút truyền. Đã ngắt kết nối"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Nút truyền. Đang kết nối"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Nút truyền. Đã kết nối"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Truyền tới"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Tìm thiết bị"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Ngắt kết nối"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Mở rộng"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Thu gọn"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Ảnh bìa album"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Thanh trượt âm lượng"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Không có phương tiện nào được chọn"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Không có thông tin nào"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Đang truyền màn hình"</string>
diff --git a/v7/mediarouter/res/values-zh-rCN/strings.xml b/v7/mediarouter/res/values-zh-rCN/strings.xml
index aabe727..c22a91c 100644
--- a/v7/mediarouter/res/values-zh-rCN/strings.xml
+++ b/v7/mediarouter/res/values-zh-rCN/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"系统"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"设备"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"投射按钮"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投射按钮。已断开连接"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投射按钮。正在连接"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投射按钮。已连接"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"投射到"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"正在查找设备"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"断开连接"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"展开"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"折叠"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"专辑封面"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑块"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"未选择任何媒体"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"没有任何相关信息"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投射屏幕"</string>
diff --git a/v7/mediarouter/res/values-zh-rHK/strings.xml b/v7/mediarouter/res/values-zh-rHK/strings.xml
index d01c823..d17469b 100644
--- a/v7/mediarouter/res/values-zh-rHK/strings.xml
+++ b/v7/mediarouter/res/values-zh-rHK/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"投放按鈕"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投放按鈕。已解除連接"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投放按鈕。正在連接"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投放按鈕。已連接"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"投放至"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"正在尋找裝置"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"中斷連線"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"收合"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"專輯封面"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑桿"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"尚未選擇媒體"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"沒有詳細資料"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投放螢幕"</string>
diff --git a/v7/mediarouter/res/values-zh-rTW/strings.xml b/v7/mediarouter/res/values-zh-rTW/strings.xml
index 68347e5..1a71c84 100644
--- a/v7/mediarouter/res/values-zh-rTW/strings.xml
+++ b/v7/mediarouter/res/values-zh-rTW/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"投放按鈕"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"投放按鈕;已中斷連線"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"投放按鈕;連線中"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"投放按鈕;已連線"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"投放到"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"正在尋找裝置"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"中斷連線"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"展開"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"收合"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"專輯封面"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"音量滑桿"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"未選取任何媒體"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"沒有可用的資訊"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"正在投放螢幕"</string>
diff --git a/v7/mediarouter/res/values-zu/strings.xml b/v7/mediarouter/res/values-zu/strings.xml
index e50acc8..860aa09 100644
--- a/v7/mediarouter/res/values-zu/strings.xml
+++ b/v7/mediarouter/res/values-zu/strings.xml
@@ -19,6 +19,9 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Isistimu"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Amadivayisi"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Inkinobho ye-Cast"</string>
+    <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Inkinobho yokusakaza. Kunqanyuliwe"</string>
+    <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Inkinobho yokusakaza. Kuyaxhunywa"</string>
+    <string name="mr_cast_button_connected" msgid="5088427771788648085">"Inkinobho yokusakaza. Kuxhunyiwe"</string>
     <string name="mr_chooser_title" msgid="414301941546135990">"Sakaza ku-"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Ithola amadivayisi"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Nqamula"</string>
@@ -29,6 +32,7 @@
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"Nweba"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Goqa"</string>
     <string name="mr_controller_album_art" msgid="6422801843540543585">"Ubuciko be-albhamu"</string>
+    <string name="mr_controller_volume_slider" msgid="2361785992211841709">"Isilayida sevolumu"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ayikho imidiya ekhethiwe"</string>
     <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Alukho ulwazi olutholakalayo"</string>
     <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Isikrini sokusakaza"</string>
diff --git a/v7/mediarouter/res/values/attrs.xml b/v7/mediarouter/res/values/attrs.xml
index b0f24ea..cf6a7b5 100644
--- a/v7/mediarouter/res/values/attrs.xml
+++ b/v7/mediarouter/res/values/attrs.xml
@@ -21,12 +21,13 @@
              and non-checked / non-checkable indicates
              that media is playing to the local device only. -->
         <attr name="externalRouteEnabledDrawable" format="reference" />
+        <!-- Tint to apply to the media route button -->
+        <attr name="buttonTint" format="color" />
 
         <attr name="android:minWidth" />
         <attr name="android:minHeight" />
     </declare-styleable>
 
-    <attr name="MediaRouteControllerWindowBackground" format="reference" />
     <attr name="mediaRouteButtonStyle" format="reference" />
     <attr name="mediaRouteCloseDrawable" format="reference" />
     <attr name="mediaRoutePlayDrawable" format="reference" />
@@ -36,9 +37,7 @@
     <attr name="mediaRouteTvIconDrawable" format="reference" />
     <attr name="mediaRouteSpeakerIconDrawable" format="reference" />
     <attr name="mediaRouteSpeakerGroupIconDrawable" format="reference" />
-    <attr name="mediaRouteChooserPrimaryTextStyle" format="reference" />
-    <attr name="mediaRouteChooserSecondaryTextStyle" format="reference" />
-    <attr name="mediaRouteControllerTitleTextStyle" format="reference" />
-    <attr name="mediaRouteControllerPrimaryTextStyle" format="reference" />
-    <attr name="mediaRouteControllerSecondaryTextStyle" format="reference" />
+    <attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
+
+    <attr name="mediaRouteTheme" format="reference" />
 </resources>
diff --git a/v7/mediarouter/res/values/styles.xml b/v7/mediarouter/res/values/styles.xml
index ea53cf3..e8e00e7 100644
--- a/v7/mediarouter/res/values/styles.xml
+++ b/v7/mediarouter/res/values/styles.xml
@@ -25,73 +25,9 @@
         <item name="externalRouteEnabledDrawable">@drawable/mr_button_light</item>
     </style>
 
-    <!-- MediaRouteChooserDialog text styles -->
-    <style name="Widget.MediaRouter.ChooserText" parent="">
-        <item name="android:fontFamily">sans-serif</item>
-        <item name="android:textStyle">normal</item>
-    </style>
+    <style name="TextAppearance.MediaRouter.Title" parent="TextAppearance.AppCompat.Title" />
 
-    <style name="Widget.MediaRouter.ChooserText.Primary">
-        <item name="android:textSize">16sp</item>
-    </style>
+    <style name="TextAppearance.MediaRouter.PrimaryText" parent="TextAppearance.AppCompat.Subhead" />
 
-    <style name="Widget.MediaRouter.ChooserText.Secondary">
-        <item name="android:textSize">14sp</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ChooserText.Primary.Dark">
-        <item name="android:textColor">#FFFFFFFF</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ChooserText.Primary.Light">
-        <item name="android:textColor">#DE000000</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ChooserText.Secondary.Dark">
-        <item name="android:textColor">#8AFFFFFF</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ChooserText.Secondary.Light">
-        <item name="android:textColor">#8A000000</item>
-    </style>
-
-    <!-- MediaRouteControllerDialog text styles -->
-    <style name="Widget.MediaRouter.ControllerText" parent="Widget.MediaRouter.ChooserText" />
-
-    <style name="Widget.MediaRouter.ControllerText.Title">
-        <item name="android:fontFamily">sans-serif-medium</item>
-        <item name="android:textSize">20sp</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Primary">
-        <item name="android:textSize">16sp</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Secondary">
-        <item name="android:textSize">14sp</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Title.Dark">
-        <item name="android:textColor">#FFFFFFFF</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Title.Light">
-        <item name="android:textColor">#DE000000</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Primary.Dark">
-        <item name="android:textColor">#FFFFFFFF</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Primary.Light">
-        <item name="android:textColor">#DE000000</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Secondary.Dark">
-        <item name="android:textColor">#FFFFFFFF</item>
-    </style>
-
-    <style name="Widget.MediaRouter.ControllerText.Secondary.Light">
-        <item name="android:textColor">#DE000000</item>
-    </style>
+    <style name="TextAppearance.MediaRouter.SecondaryText" parent="TextAppearance.AppCompat.Body1" />
 </resources>
diff --git a/v7/mediarouter/res/values/themes.xml b/v7/mediarouter/res/values/themes.xml
index a2f3eb5..1eb4bfd 100644
--- a/v7/mediarouter/res/values/themes.xml
+++ b/v7/mediarouter/res/values/themes.xml
@@ -16,11 +16,10 @@
 
 <resources>
 
-    <style name="Theme.MediaRouter" parent="">
-        <item name="windowNoTitle">false</item>
+    <style name="Theme.MediaRouter" parent="ThemeOverlay.AppCompat.Dark">
+        <item name="windowNoTitle">true</item>
         <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.MediaRouteButton</item>
 
-        <item name="MediaRouteControllerWindowBackground">@drawable/mr_dialog_material_background_dark</item>
         <item name="mediaRouteCloseDrawable">@drawable/mr_dialog_close_dark</item>
         <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_dark</item>
         <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_dark</item>
@@ -29,25 +28,18 @@
         <item name="mediaRouteTvIconDrawable">@drawable/ic_vol_type_tv_dark</item>
         <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_vol_type_speaker_dark</item>
         <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_vol_type_speaker_group_dark</item>
-        <item name="mediaRouteChooserPrimaryTextStyle">@style/Widget.MediaRouter.ChooserText.Primary.Dark</item>
-        <item name="mediaRouteChooserSecondaryTextStyle">@style/Widget.MediaRouter.ChooserText.Secondary.Dark</item>
-        <item name="mediaRouteControllerTitleTextStyle">@style/Widget.MediaRouter.ControllerText.Title.Dark</item>
-        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Dark</item>
-        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Dark</item>
+
+        <item name="mediaRouteControlPanelThemeOverlay">@null</item>
     </style>
 
     <style name="Theme.MediaRouter.LightControlPanel">
-        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_light</item>
-        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_light</item>
-        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_light</item>
-        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Light</item>
-        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Light</item>
+        <item name="mediaRouteControlPanelThemeOverlay">@style/ThemeOverlay.MediaRouter.Light</item>
     </style>
 
-    <style name="Theme.MediaRouter.Light">
+    <style name="Theme.MediaRouter.Light" parent="ThemeOverlay.AppCompat.Light">
+        <item name="windowNoTitle">true</item>
         <item name="mediaRouteButtonStyle">@style/Widget.MediaRouter.Light.MediaRouteButton</item>
 
-        <item name="MediaRouteControllerWindowBackground">@drawable/mr_dialog_material_background_light</item>
         <item name="mediaRouteCloseDrawable">@drawable/mr_dialog_close_light</item>
         <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_light</item>
         <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_light</item>
@@ -56,19 +48,24 @@
         <item name="mediaRouteTvIconDrawable">@drawable/ic_vol_type_tv_light</item>
         <item name="mediaRouteSpeakerIconDrawable">@drawable/ic_vol_type_speaker_light</item>
         <item name="mediaRouteSpeakerGroupIconDrawable">@drawable/ic_vol_type_speaker_group_light</item>
-        <item name="mediaRouteChooserPrimaryTextStyle">@style/Widget.MediaRouter.ChooserText.Primary.Light</item>
-        <item name="mediaRouteChooserSecondaryTextStyle">@style/Widget.MediaRouter.ChooserText.Secondary.Light</item>
-        <item name="mediaRouteControllerTitleTextStyle">@style/Widget.MediaRouter.ControllerText.Title.Light</item>
-        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Light</item>
-        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Light</item>
+
+        <item name="mediaRouteControlPanelThemeOverlay">@null</item>
     </style>
 
     <style name="Theme.MediaRouter.Light.DarkControlPanel">
+        <item name="mediaRouteControlPanelThemeOverlay">@style/ThemeOverlay.MediaRouter.Dark</item>
+    </style>
+
+    <style name="ThemeOverlay.MediaRouter.Dark" parent="ThemeOverlay.AppCompat.Dark">
         <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_dark</item>
         <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_dark</item>
         <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_dark</item>
-        <item name="mediaRouteControllerPrimaryTextStyle">@style/Widget.MediaRouter.ControllerText.Primary.Dark</item>
-        <item name="mediaRouteControllerSecondaryTextStyle">@style/Widget.MediaRouter.ControllerText.Secondary.Dark</item>
+
+    </style>
+    <style name="ThemeOverlay.MediaRouter.Light" parent="ThemeOverlay.AppCompat.Light">
+        <item name="mediaRoutePlayDrawable">@drawable/mr_media_play_light</item>
+        <item name="mediaRoutePauseDrawable">@drawable/mr_media_pause_light</item>
+        <item name="mediaRouteAudioTrackDrawable">@drawable/mr_vol_type_audiotrack_light</item>
     </style>
 
 </resources>
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
index ad8fc1b..4eb98aa 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteActionProvider.java
@@ -69,7 +69,7 @@
  * </pre><p>
  * Then configure the menu and set the route selector for the chooser.
  * </p><pre>
- * public class MyActivity extends ActionBarActivity {
+ * public class MyActivity extends AppCompatActivity {
  *     private MediaRouter mRouter;
  *     private MediaRouter.Callback mCallback;
  *     private MediaRouteSelector mSelector;
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
index 25288c0..150a3fd 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteButton.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -29,10 +30,9 @@
 import android.support.v4.app.FragmentManager;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.view.GravityCompat;
-import android.support.v7.media.MediaRouter;
 import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
 import android.support.v7.mediarouter.R;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -98,6 +98,7 @@
     private boolean mCheatSheetEnabled;
     private boolean mIsConnecting;
 
+    private ColorStateList mButtonTint;
     private int mMinWidth;
     private int mMinHeight;
 
@@ -129,6 +130,7 @@
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.MediaRouteButton, defStyleAttr, 0);
+        mButtonTint = a.getColorStateList(R.styleable.MediaRouteButton_buttonTint);
         setRemoteIndicatorDrawable(a.getDrawable(
                 R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
         mMinWidth = a.getDimensionPixelSize(
@@ -362,6 +364,10 @@
             mRemoteIndicator.setCallback(null);
             unscheduleDrawable(mRemoteIndicator);
         }
+        if (mButtonTint != null) {
+            d = DrawableCompat.wrap(d.mutate());
+            DrawableCompat.setTintList(d, mButtonTint);
+        }
         mRemoteIndicator = d;
         if (d != null) {
             d.setCallback(this);
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index d2f6d51..ac79f2d 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -69,6 +69,7 @@
     private final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
 
+    private TextView mTitleView;
     private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
     private ArrayList<MediaRouter.RouteInfo> mRoutes;
     private RouteAdapter mAdapter;
@@ -165,11 +166,20 @@
     }
 
     @Override
+    public void setTitle(CharSequence title) {
+        mTitleView.setText(title);
+    }
+
+    @Override
+    public void setTitle(int titleId) {
+        mTitleView.setText(titleId);
+    }
+
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.mr_chooser_dialog);
-        setTitle(R.string.mr_chooser_title);
 
         mRoutes = new ArrayList<>();
         mAdapter = new RouteAdapter(getContext(), mRoutes);
@@ -177,6 +187,7 @@
         mListView.setAdapter(mAdapter);
         mListView.setOnItemClickListener(mAdapter);
         mListView.setEmptyView(findViewById(android.R.id.empty));
+        mTitleView = (TextView) findViewById(R.id.mr_chooser_title);
 
         updateLayout();
     }
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index ee3d26f..961e37e 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -400,7 +400,8 @@
 
         mVolumeGroupList = (OverlayListView) findViewById(R.id.mr_volume_group_list);
         mGroupMemberRoutes = new ArrayList<MediaRouter.RouteInfo>();
-        mVolumeGroupAdapter = new VolumeGroupAdapter(mContext, mGroupMemberRoutes);
+        mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(),
+                mGroupMemberRoutes);
         mVolumeGroupList.setAdapter(mVolumeGroupAdapter);
         mGroupMemberRoutesAnimatingWithBitmap = new HashSet<>();
 
@@ -523,8 +524,12 @@
         mRouteNameTextView.setText(mRoute.getName());
         mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE);
         if (mCustomControlView == null && mArtIconIsLoaded) {
-            mArtView.setImageBitmap(mArtIconLoadedBitmap);
-            mArtView.setBackgroundColor(mArtIconBackgroundColor);
+            if (isBitmapRecycled(mArtIconLoadedBitmap)) {
+                Log.w(TAG, "Can't set artwork image with recycled bitmap: " + mArtIconLoadedBitmap);
+            } else {
+                mArtView.setImageBitmap(mArtIconLoadedBitmap);
+                mArtView.setBackgroundColor(mArtIconBackgroundColor);
+            }
             clearLoadedBitmap();
         }
         updateVolumeControlLayout();
@@ -532,6 +537,10 @@
         updateLayoutHeight(animate);
     }
 
+    private boolean isBitmapRecycled(Bitmap bitmap) {
+        return bitmap != null && bitmap.isRecycled();
+    }
+
     private boolean canShowPlaybackControlLayout() {
         return mCustomControlView == null && (mDescription != null || mState != null);
     }
@@ -1001,17 +1010,18 @@
                         | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
                 boolean supportsPause = (mState.getActions() & (PlaybackStateCompat.ACTION_PAUSE
                         | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
+                Context playPauseButtonContext = mPlayPauseButton.getContext();
                 if (isPlaying && supportsPause) {
                     mPlayPauseButton.setVisibility(View.VISIBLE);
                     mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
-                            mContext, R.attr.mediaRoutePauseDrawable));
-                    mPlayPauseButton.setContentDescription(mContext.getResources()
+                            playPauseButtonContext, R.attr.mediaRoutePauseDrawable));
+                    mPlayPauseButton.setContentDescription(playPauseButtonContext.getResources()
                             .getText(R.string.mr_controller_pause));
                 } else if (!isPlaying && supportsPlay) {
                     mPlayPauseButton.setVisibility(View.VISIBLE);
                     mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
-                            mContext, R.attr.mediaRoutePlayDrawable));
-                    mPlayPauseButton.setContentDescription(mContext.getResources()
+                            playPauseButtonContext, R.attr.mediaRoutePlayDrawable));
+                    mPlayPauseButton.setContentDescription(playPauseButtonContext.getResources()
                             .getText(R.string.mr_controller_play));
                 } else {
                     mPlayPauseButton.setVisibility(View.GONE);
@@ -1245,7 +1255,7 @@
         public View getView(final int position, View convertView, ViewGroup parent) {
             View v = convertView;
             if (v == null) {
-                v = LayoutInflater.from(mContext).inflate(
+                v = LayoutInflater.from(parent.getContext()).inflate(
                         R.layout.mr_controller_volume_item, parent, false);
             } else {
                 updateVolumeGroupItemHeight(v);
@@ -1262,7 +1272,7 @@
                 MediaRouteVolumeSlider volumeSlider =
                         (MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider);
                 MediaRouterThemeHelper.setVolumeSliderColor(
-                        mContext, volumeSlider, mVolumeGroupList);
+                        parent.getContext(), volumeSlider, mVolumeGroupList);
                 volumeSlider.setTag(route);
                 mVolumeSliderMap.put(route, volumeSlider);
                 volumeSlider.setHideThumb(!isEnabled);
@@ -1313,7 +1323,12 @@
         private long mStartTimeMillis;
 
         FetchArtTask() {
-            mIconBitmap = mDescription == null ? null : mDescription.getIconBitmap();
+            Bitmap bitmap = mDescription == null ? null : mDescription.getIconBitmap();
+            if (isBitmapRecycled(bitmap)) {
+                Log.w(TAG, "Can't fetch the given art bitmap because it's already recycled.");
+                bitmap = null;
+            }
+            mIconBitmap = bitmap;
             mIconUri = mDescription == null ? null : mDescription.getIconUri();
         }
 
@@ -1381,6 +1396,10 @@
                     }
                 }
             }
+            if (isBitmapRecycled(art)) {
+                Log.w(TAG, "Can't use recycled bitmap: " + art);
+                return null;
+            }
             if (art != null && art.getWidth() < art.getHeight()) {
                 // Portrait art requires dominant color as background color.
                 Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index 60c878f..23f3bad 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -55,6 +55,8 @@
      * @return The themed context.
      */
     public static Context createThemedContext(Context context, int style) {
+        // First, apply dialog property overlay.
+
         int theme;
         if (isLightTheme(context)) {
             if (getControllerColor(context, style) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
@@ -69,7 +71,12 @@
                 theme = R.style.Theme_MediaRouter;
             }
         }
-        return new ContextThemeWrapper(context, theme);
+        int mediaRouteThemeResId = getThemeResource(context, R.attr.mediaRouteTheme);
+        Context themedContext = new ContextThemeWrapper(context, theme);
+        if (mediaRouteThemeResId != 0) {
+            themedContext = new ContextThemeWrapper(themedContext, mediaRouteThemeResId);
+        }
+        return themedContext;
     }
 
     public static int getThemeResource(Context context, int attr) {
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index ab27c9e..daf73d0 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.media;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -58,8 +60,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
 /**
  * MediaRouter allows applications to control the routing of media channels
  * and streams from the current device to external speakers and destination devices.
@@ -296,6 +296,16 @@
     }
 
     /**
+     * Gets a bluetooth route for playing media content on the system.
+     *
+     * @return A bluetooth route, if exist, otherwise null.
+     */
+    public RouteInfo getBluetoothRoute() {
+        checkCallingThread();
+        return sGlobal.getBluetoothRoute();
+    }
+
+    /**
      * Gets the currently selected route.
      * <p>
      * The application should examine the route's
@@ -1054,6 +1064,30 @@
         }
 
         /**
+         * Returns true if this route is a bluetooth route.
+         *
+         * @return True if this route is a bluetooth route.
+         *
+         * @see MediaRouter#getBluetoothRoute
+         */
+        public boolean isBluetooth() {
+            checkCallingThread();
+            return sGlobal.getBluetoothRoute() == this;
+        }
+
+        /**
+         * Returns true if this route is the default route and the device speaker.
+         *
+         * @return True if this route is the default route and the device speaker.
+         */
+        public boolean isDeviceSpeaker() {
+            int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier(
+                    "default_audio_route_name", "string", "android");
+            return isDefault()
+                    && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName);
+        }
+
+        /**
          * Gets a list of {@link MediaControlIntent media control intent} filters that
          * describe the capabilities of this route and the media control actions that
          * it supports.
@@ -1904,6 +1938,7 @@
 
         private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
         private RouteInfo mDefaultRoute;
+        private RouteInfo mBluetoothRoute;
         RouteInfo mSelectedRoute;
         private RouteController mSelectedRouteController;
         // A map from route descriptor ID to RouteController for the member routes in the currently
@@ -2041,6 +2076,10 @@
             return mDefaultRoute;
         }
 
+        public RouteInfo getBluetoothRoute() {
+            return mBluetoothRoute;
+        }
+
         public RouteInfo getSelectedRoute() {
             if (mSelectedRoute == null) {
                 // This should never happen once the media router has been fully
@@ -2049,6 +2088,11 @@
                 throw new IllegalStateException("There is no currently selected route.  "
                         + "The media router has not yet been fully initialized.");
             }
+            // A workaround for making this method work properly.
+            if (android.os.Build.VERSION.SDK_INT >= 16 && android.os.Build.VERSION.SDK_INT < 25
+                    && RouteInfo.isSystemMediaRouteProvider(mSelectedRoute)) {
+                syncSystemRoutes();
+            }
             return mSelectedRoute;
         }
 
@@ -2066,6 +2110,11 @@
                 return;
             }
 
+            // A workaround for making this method work properly.
+            if (android.os.Build.VERSION.SDK_INT >= 16 && android.os.Build.VERSION.SDK_INT < 25
+                    && RouteInfo.isSystemMediaRouteProvider(route)) {
+                syncSystemRoutes();
+            }
             setSelectedRouteInternal(route, unselectReason);
         }
 
@@ -2200,6 +2249,35 @@
             }
         }
 
+        void syncSystemRoutes() {
+            Object routerObj = MediaRouterJellybean.getMediaRouter(mApplicationContext);
+            // If a2dp is enabled, this means a BT route is the selected route, otherwise
+            // the default route is the selected one.
+            boolean a2dpEnabled = MediaRouterJellybean.isBluetoothA2dpOn(routerObj);
+            Object selectedRouteObj = MediaRouterJellybean.getSelectedRoute(
+                    routerObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
+            Object defaultRouteObj = mSystemProvider.getDefaultRoute();
+
+            if (a2dpEnabled && selectedRouteObj == defaultRouteObj) {
+                // A BT route is the currently selected route, but MediaRouter think the default
+                // route is the selected one. By selecting the BT route via framework MediaRouter,
+                // MediaRouter could correct its selected route information.
+                for (Object routeObj : MediaRouterJellybean.getRoutes(routerObj)) {
+                    if (routeObj != defaultRouteObj) {
+                        MediaRouterJellybean.selectRoute(routerObj,
+                                MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
+                        break;
+                    }
+                }
+            } else if (!a2dpEnabled && selectedRouteObj != defaultRouteObj) {
+                // The default route is the currently selected route, but MediaRouter think a BT
+                // route is the selected one. By selecting the default route via framework
+                // MediaRouter, MediaRouter could correct its selected route information.
+                MediaRouterJellybean.selectRoute(routerObj,
+                        MediaRouterJellybean.ALL_ROUTE_TYPES, defaultRouteObj);
+            }
+        }
+
         void updateProviderDescriptor(MediaRouteProvider providerInstance,
                 MediaRouteProviderDescriptor descriptor) {
             int index = findProviderInfo(providerInstance);
@@ -2421,6 +2499,22 @@
                 }
             }
 
+            // Update bluetooth route.
+            if (mBluetoothRoute != null && !isRouteSelectable(mBluetoothRoute)) {
+                Log.i(TAG, "Clearing the bluetooth route because it "
+                        + "is no longer selectable: " + mBluetoothRoute);
+                mBluetoothRoute = null;
+            }
+            if (mBluetoothRoute == null && !mRoutes.isEmpty()) {
+                for (RouteInfo route : mRoutes) {
+                    if (isSystemBluetoothRoute(route) && isRouteSelectable(route)) {
+                        mBluetoothRoute = route;
+                        Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute);
+                        break;
+                    }
+                }
+            }
+
             // Update selected route.
             if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) {
                 Log.i(TAG, "Unselecting the current route because it "
@@ -2505,6 +2599,12 @@
                             SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
         }
 
+        private boolean isSystemBluetoothRoute(RouteInfo route) {
+            return route.getProviderInstance() == mSystemProvider
+                    && !route.mDescriptorId.equals(
+                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
+        }
+
         private void setSelectedRouteInternal(RouteInfo route, int unselectReason) {
             if (mSelectedRoute != route) {
                 if (mSelectedRoute != null) {
@@ -2740,7 +2840,6 @@
             public MediaSessionCompat.Token getToken() {
                 return mMsCompat.getSessionToken();
             }
-
         }
 
         private final class RemoteControlClientRecord
diff --git a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
index a31eb6f..5fcafa7 100644
--- a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
@@ -99,6 +99,10 @@
         public MediaRouter.RouteInfo getSystemRouteByDescriptorId(String id);
     }
 
+    protected Object getDefaultRoute() {
+        return null;
+    }
+
     /**
      * Legacy implementation for platform versions prior to Jellybean.
      */
@@ -652,6 +656,7 @@
                     MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
         }
 
+        @Override
         protected Object getDefaultRoute() {
             if (mGetDefaultRouteWorkaround == null) {
                 mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
index c689297..c21dad3 100644
--- a/v7/palette/Android.mk
+++ b/v7/palette/Android.mk
@@ -26,11 +26,14 @@
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-palette
-LOCAL_SDK_VERSION := 9
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
 LOCAL_MANIFEST_FILE := src/main/AndroidManifest.xml
-LOCAL_SHARED_ANDROID_LIBRARIES += android-support-compat android-support-core-utils
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+    android-support-compat \
+    android-support-core-utils \
+    android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
index c20875e..ca8e508 100644
--- a/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/BucketTests.java
@@ -27,8 +27,8 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
index 284eab3..d9ac12e 100644
--- a/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/ConsistencyTest.java
@@ -16,17 +16,16 @@
 
 package android.support.v7.graphics;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
+
+import static org.junit.Assert.assertEquals;
 
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
 
-import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
-import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 public class ConsistencyTest {
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
index 99cb9dd..fbcf6ae 100644
--- a/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/MaxColorsTest.java
@@ -20,8 +20,8 @@
 
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java b/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
index 0f55bca..efbcda4 100644
--- a/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
+++ b/v7/palette/src/androidTest/java/android/support/v7/graphics/SwatchTests.java
@@ -16,21 +16,22 @@
 
 package android.support.v7.graphics;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.graphics.Color;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static android.support.v4.graphics.ColorUtils.HSLToColor;
 import static android.support.v4.graphics.ColorUtils.calculateContrast;
 import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Color;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 public class SwatchTests {
 
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index ca4fe02..e751e1c 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -14,23 +14,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Build the resources separately because the constants built with the resources need to access
-# the latest SDK but the actual code needs to build against SDK 7.
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE := android-support-v7-preference-res
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,constants)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
-    android-support-v7-appcompat \
-    android-support-v7-recyclerview \
-    android-support-v4 \
-    android-support-annotations
-LOCAL_JAR_EXCLUDE_FILES := none
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
 # Here is the final static library that apps can link against.
 # Applications that use this library must specify
 #
@@ -38,18 +21,17 @@
 #       android-support-v7-preference \
 #       android-support-v7-appcompat \
 #       android-support-v7-recyclerview \
-#       android-support-v4 \
-#       android-support-annotations
+#       android-support-v4
 #
 # in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-preference
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v7-preference-res
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under,constants) \
+    $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-v7-appcompat \
     android-support-v7-recyclerview \
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index ed752ee..55fe020 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -22,11 +22,24 @@
     compile project(':support-v4')
     compile project(':support-appcompat-v7')
     compile project(':support-recyclerview-v7')
+
+    androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile ("com.android.support.test.espresso:espresso-core:${project.rootProject.ext.espressoVersion}") {
+        exclude module: 'support-annotations'
+    }
+    testCompile 'junit:junit:4.12'
 }
 
 android {
     compileSdkVersion project.ext.currentSdk
 
+    defaultConfig {
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 9
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
@@ -40,6 +53,8 @@
         // This is a *reset* so it replaces the default paths
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/src'
+        androidTest.res.srcDir 'tests/res'
+        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
     }
 
     compileOptions {
diff --git a/v7/preference/res/layout/preference_widget_seekbar.xml b/v7/preference/res/layout/preference_widget_seekbar.xml
new file mode 100644
index 0000000..30bc5ff
--- /dev/null
+++ b/v7/preference/res/layout/preference_widget_seekbar.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:minHeight="?android:attr/listPreferredItemHeight"
+              android:gravity="center_vertical"
+              android:paddingEnd="?android:attr/scrollbarSize"
+              android:clipChildren="false"
+              android:clipToPadding="false">
+
+    <ImageView
+            android:id="@+android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:minWidth="@dimen/preference_icon_minWidth"/>
+
+    <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dip"
+            android:layout_marginEnd="8dip"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="6dip"
+            android:layout_weight="1"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+
+        <TextView android:id="@+android:id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:textAppearance="?android:attr/textAppearanceMedium"
+                  android:ellipsize="marquee"
+                  android:fadingEdge="horizontal"/>
+
+        <TextView android:id="@+android:id/summary"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_below="@android:id/title"
+                  android:layout_alignStart="@android:id/title"
+                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="?android:attr/textColorSecondary"
+                  android:maxLines="4"/>
+
+        <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+        to the children of this container layout. Otherwise, the animated pressed state will also
+        play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+        The background of the SeekBar is also set to null to disable the ripple background -->
+        <android.support.v7.preference.UnPressableLinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/summary"
+                android:layout_alignStart="@android:id/title"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+            <SeekBar
+                    android:id="@+id/seekbar"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="wrap_content"
+                    android:paddingStart="@dimen/preference_seekbar_padding_start"
+                    android:paddingEnd="@dimen/preference_seekbar_padding_end"
+                    android:focusable="false"
+                    android:clickable="false"
+                    android:background="@null" />
+
+            <TextView android:id="@+id/seekbar_value"
+                      android:layout_width="@dimen/preference_seekbar_value_width"
+                      android:layout_height="match_parent"
+                      android:gravity="right|center_vertical"
+                      android:fontFamily="sans-serif-condensed"
+                      android:singleLine="true"
+                      android:textAppearance="?android:attr/textAppearanceMedium"
+                      android:ellipsize="marquee"
+                      android:fadingEdge="horizontal"/>
+        </android.support.v7.preference.UnPressableLinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/v7/preference/res/values/attrs.xml b/v7/preference/res/values/attrs.xml
index bd64109..baf2a03 100644
--- a/v7/preference/res/values/attrs.xml
+++ b/v7/preference/res/values/attrs.xml
@@ -254,4 +254,20 @@
         <attr name="selectableItemBackground" />
     </declare-styleable>
 
+    <declare-styleable name="SeekBarPreference">
+        <attr name="min" format="integer"/>
+        <attr name="android:max"/>
+        <attr name="android:layout" />
+        <!-- Attribute indicating whether the slider within this preference can be adjusted, that is
+        pressing left/right keys when this preference is focused will move the slider accordingly
+        (e.g. inline adjustable preferences). False, if the slider within the preference is
+        read-only and cannot be adjusted. By default, the seekbar is adjustable. -->
+        <attr name="adjustable" format="boolean" />
+        <!-- Flag indicating whether the TextView next to the seekbar that shows the current seekbar
+        value will be displayed. If true, the view is VISIBLE; if false, the view will be GONE.
+        By default, this view is VISIBLE. -->
+        <attr name="showSeekBarValue" format="boolean" />
+    </declare-styleable>
+
+
 </resources>
diff --git a/v7/preference/res/values/dimens.xml b/v7/preference/res/values/dimens.xml
new file mode 100644
index 0000000..4816e36
--- /dev/null
+++ b/v7/preference/res/values/dimens.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Minimum space to allocate to the left of a preference item for an icon.
+    This helps in aligning titles when some items have icons and some don't. When space is
+    at a premium, we don't pre-allocate any space. -->
+    <dimen name="preference_icon_minWidth">0dp</dimen>
+    <!-- The padding to the left of the seekbar view within a SeekBarPreference -->
+    <dimen name="preference_seekbar_padding_start">0dp</dimen>
+    <!-- The padding to the right of the seekbar view within a SeekBarPreference -->
+    <dimen name="preference_seekbar_padding_end">22dp</dimen>
+    <!-- The width of the TextView indicating the current value of the SeekBarPreference -->
+    <dimen name="preference_seekbar_value_width">36dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/v7/preference/res/values/styles.xml b/v7/preference/res/values/styles.xml
index 06b24fa..e61a361 100644
--- a/v7/preference/res/values/styles.xml
+++ b/v7/preference/res/values/styles.xml
@@ -49,6 +49,12 @@
         <item name="android:switchTextOff">@string/v7_preference_off</item>
     </style>
 
+    <style name="Preference.SeekBarPreference">
+        <item name="android:layout">@layout/preference_widget_seekbar</item>
+        <item name="adjustable">true</item>
+        <item name="showSeekBarValue">true</item>
+    </style>
+
     <style name="Preference.PreferenceScreen">
     </style>
 
diff --git a/v7/preference/res/values/themes.xml b/v7/preference/res/values/themes.xml
index bb7f496..af5f469 100644
--- a/v7/preference/res/values/themes.xml
+++ b/v7/preference/res/values/themes.xml
@@ -24,6 +24,7 @@
         <item name="preferenceInformationStyle">@style/Preference.Information</item>
         <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference</item>
         <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat</item>
+        <item name="seekBarPreferenceStyle">@style/Preference.SeekBarPreference</item>
         <item name="dialogPreferenceStyle">@style/Preference.DialogPreference</item>
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference</item>
         <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList</item>
diff --git a/v7/preference/src/android/support/v7/preference/Preference.java b/v7/preference/src/android/support/v7/preference/Preference.java
index f12fcbd..7d2bd3f 100644
--- a/v7/preference/src/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/android/support/v7/preference/Preference.java
@@ -140,6 +140,7 @@
 
     private List<Preference> mDependents;
 
+    private boolean mWasDetached;
     private boolean mBaseMethodCalled;
 
     private final View.OnClickListener mClickListener = new View.OnClickListener() {
@@ -246,10 +247,10 @@
         mKey = TypedArrayUtils.getString(a, R.styleable.Preference_key,
                 R.styleable.Preference_android_key);
 
-        mTitle = TypedArrayUtils.getString(a, R.styleable.Preference_title,
+        mTitle = TypedArrayUtils.getText(a, R.styleable.Preference_title,
                 R.styleable.Preference_android_title);
 
-        mSummary = TypedArrayUtils.getString(a, R.styleable.Preference_summary,
+        mSummary = TypedArrayUtils.getText(a, R.styleable.Preference_summary,
                 R.styleable.Preference_android_summary);
 
         mOrder = TypedArrayUtils.getInt(a, R.styleable.Preference_order,
@@ -668,6 +669,9 @@
      * @see #setIcon(Drawable)
      */
     public Drawable getIcon() {
+        if (mIcon == null && mIconResId != 0) {
+            mIcon = ContextCompat.getDrawable(mContext, mIconResId);
+        }
         return mIcon;
     }
 
@@ -1138,6 +1142,24 @@
      */
     public void onDetached() {
         unregisterDependency();
+        mWasDetached = true;
+    }
+
+    /**
+     * Returns true if {@link #onDetached()} was called. Used for handling the case when a
+     * preference was removed, modified, and re-added to a {@link PreferenceGroup}
+     * @hide
+     */
+    public final boolean wasDetached() {
+        return mWasDetached;
+    }
+
+    /**
+     * Clears the {@link #wasDetached()} status
+     * @hide
+     */
+    public final void clearWasDetached() {
+        mWasDetached = false;
     }
 
     private void registerDependency() {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
index 27d2a13..c8af738 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.support.annotation.RestrictTo;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.util.DiffUtil;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -149,10 +150,49 @@
             }
         }
 
+        final List<Preference> oldVisibleList = mPreferenceList;
         mPreferenceList = visiblePreferenceList;
         mPreferenceListInternal = fullPreferenceList;
 
-        notifyDataSetChanged();
+        final PreferenceManager preferenceManager = mPreferenceGroup.getPreferenceManager();
+        if (preferenceManager != null
+                && preferenceManager.getPreferenceComparisonCallback() != null) {
+            final PreferenceManager.PreferenceComparisonCallback comparisonCallback =
+                    preferenceManager.getPreferenceComparisonCallback();
+            final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
+                @Override
+                public int getOldListSize() {
+                    return oldVisibleList.size();
+                }
+
+                @Override
+                public int getNewListSize() {
+                    return visiblePreferenceList.size();
+                }
+
+                @Override
+                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+                    return comparisonCallback.arePreferenceItemsTheSame(
+                            oldVisibleList.get(oldItemPosition),
+                            visiblePreferenceList.get(newItemPosition));
+                }
+
+                @Override
+                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+                    return comparisonCallback.arePreferenceContentsTheSame(
+                            oldVisibleList.get(oldItemPosition),
+                            visiblePreferenceList.get(newItemPosition));
+                }
+            });
+
+            result.dispatchUpdatesTo(this);
+        } else {
+            notifyDataSetChanged();
+        }
+
+        for (final Preference preference : fullPreferenceList) {
+            preference.clearWasDetached();
+        }
     }
 
     private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceManager.java b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
index 34adf6d..7af430f 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceManager.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
@@ -18,10 +18,12 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.RestrictTo;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.SharedPreferencesCompat;
 import android.support.v4.os.BuildCompat;
+import android.text.TextUtils;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
@@ -91,6 +93,7 @@
      */
     private PreferenceScreen mPreferenceScreen;
 
+    private PreferenceComparisonCallback mPreferenceComparisonCallback;
     private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
     private OnDisplayPreferenceDialogListener mOnDisplayPreferenceDialogListener;
     private OnNavigateToScreenListener mOnNavigateToScreenListener;
@@ -384,7 +387,6 @@
      *            parameter set to true.
      */
     public static void setDefaultValues(Context context, int resId, boolean readAgain) {
-
         // Use the default shared preferences name and mode
         setDefaultValues(context, getDefaultSharedPreferencesName(context),
                 getDefaultSharedPreferencesMode(), resId, readAgain);
@@ -484,6 +486,15 @@
         return mContext;
     }
 
+    public PreferenceComparisonCallback getPreferenceComparisonCallback() {
+        return mPreferenceComparisonCallback;
+    }
+
+    public void setPreferenceComparisonCallback(
+            PreferenceComparisonCallback preferenceComparisonCallback) {
+        mPreferenceComparisonCallback = preferenceComparisonCallback;
+    }
+
     public OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener() {
         return mOnDisplayPreferenceDialogListener;
     }
@@ -536,6 +547,102 @@
     }
 
     /**
+     * Callback class to be used by the {@link android.support.v7.widget.RecyclerView.Adapter}
+     * associated with the {@link PreferenceScreen}, used to determine when two {@link Preference}
+     * objects are semantically and visually the same.
+     */
+    public static abstract class PreferenceComparisonCallback {
+        /**
+         * Called to determine if two {@link Preference} objects represent the same item
+         *
+         * @param p1 {@link Preference} object to compare
+         * @param p2 {@link Preference} object to compare
+         * @return {@code true} if the objects represent the same item
+         */
+        public abstract boolean arePreferenceItemsTheSame(Preference p1, Preference p2);
+
+        /**
+         * Called to determine if two {@link Preference} objects will display the same data
+         *
+         * @param p1 {@link Preference} object to compare
+         * @param p2 {@link Preference} object to compare
+         * @return {@code true} if the objects are visually identical
+         */
+        public abstract boolean arePreferenceContentsTheSame(Preference p1, Preference p2);
+    }
+
+    /**
+     * A basic implementation of {@link PreferenceComparisonCallback} suitable for use with the
+     * default {@link Preference} classes. If the {@link PreferenceScreen} contains custom
+     * {@link Preference} subclasses, you must override
+     * {@link #arePreferenceContentsTheSame(Preference, Preference)}
+     */
+    public static class SimplePreferenceComparisonCallback extends PreferenceComparisonCallback {
+        /**
+         * {@inheritDoc}
+         *
+         * <p>This method will not be able to track replaced {@link Preference} objects if they
+         * do not have a unique key.</p>
+         *
+         * @see Preference#setKey(String)
+         */
+        @Override
+        public boolean arePreferenceItemsTheSame(Preference p1, Preference p2) {
+            return p1.getId() == p2.getId();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * <p>The result of this method is only valid for the default {@link Preference} objects,
+         * and custom subclasses which do not override
+         * {@link Preference#onBindViewHolder(PreferenceViewHolder)}. This method also assumes
+         * that if a preference object is being replaced by a new instance, the old instance was
+         * not modified after being removed from its containing {@link PreferenceGroup}.</p>
+         */
+        @Override
+        public boolean arePreferenceContentsTheSame(Preference p1, Preference p2) {
+            if (p1.getClass() != p2.getClass()) {
+                return false;
+            }
+            if (p1 == p2 && p1.wasDetached()) {
+                // Defensively handle the case where a preference was removed, updated and re-added.
+                // Hopefully this is rare.
+                return false;
+            }
+            if (!TextUtils.equals(p1.getTitle(), p2.getTitle())) {
+                return false;
+            }
+            if (!TextUtils.equals(p1.getSummary(), p2.getSummary())) {
+                return false;
+            }
+            final Drawable p1Icon = p1.getIcon();
+            final Drawable p2Icon = p2.getIcon();
+            if (p1Icon != p2Icon && (p1Icon == null || !p1Icon.equals(p2Icon))) {
+                return false;
+            }
+            if (p1.isEnabled() != p2.isEnabled()) {
+                return false;
+            }
+            if (p1.isSelectable() != p2.isSelectable()) {
+                return false;
+            }
+            if (p1 instanceof TwoStatePreference) {
+                if (((TwoStatePreference) p1).isChecked()
+                        != ((TwoStatePreference) p2).isChecked()) {
+                    return false;
+                }
+            }
+            if (p1 instanceof DropDownPreference && p1 != p2) {
+                // Different object, must re-bind spinner adapter
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    /**
      * Interface definition for a callback to be invoked when a
      * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is
      * clicked.
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
index 97a907a..262fcdf 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
@@ -17,6 +17,7 @@
 package android.support.v7.preference;
 
 import android.support.annotation.IdRes;
+import android.support.annotation.RestrictTo;
 import android.support.v7.widget.RecyclerView;
 import android.util.SparseArray;
 import android.view.View;
@@ -31,7 +32,9 @@
     private boolean mDividerAllowedAbove;
     private boolean mDividerAllowedBelow;
 
-    /* package */ PreferenceViewHolder(View itemView) {
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.TESTS)
+    public PreferenceViewHolder(View itemView) {
         super(itemView);
 
         // Pre-cache the views that we know in advance we'll want to find
diff --git a/v7/preference/src/android/support/v7/preference/SeekBarPreference.java b/v7/preference/src/android/support/v7/preference/SeekBarPreference.java
new file mode 100644
index 0000000..6bfb523
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/SeekBarPreference.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.preference;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+/**
+ * Preference based on android.preference.SeekBarPreference but uses support v7 preference as base.
+ * It contains a title and a seekbar and an optional seekbar value TextView. The actual preference
+ * layout is customizable by setting {@code android:layout} on the preference widget layout or
+ * {@code seekBarPreferenceStyle} attribute.
+ * The seekbar within the preference can be defined adjustable or not by setting {@code
+ * adjustable} attribute. If adjustable, the preference will be responsive to DPAD left/right keys.
+ * Otherwise, it skips those keys.
+ * The seekbar value view can be shown or disabled by setting {@code showSeekBarValue} attribute
+ * to true or false, respectively.
+ * Other SeekBar specific attributes (e.g. {@code title, summary, defaultValue, min, max}) can be
+ * set directly on the preference widget layout.
+ */
+public class SeekBarPreference extends Preference {
+
+    private int mSeekBarValue;
+    private int mMin;
+    private int mMax;
+    private boolean mTrackingTouch;
+    private SeekBar mSeekBar;
+    private TextView mSeekBarValueTextView;
+    private boolean mAdjustable; // whether the seekbar should respond to the left/right keys
+    private boolean mShowSeekBarValue; // whether to show the seekbar value TextView next to the bar
+
+    private static final String TAG = "SeekBarPreference";
+
+    /**
+     * Listener reacting to the SeekBar changing value by the user
+     */
+    private OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() {
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (fromUser && !mTrackingTouch) {
+                syncValueInternal(seekBar);
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+            mTrackingTouch = true;
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            mTrackingTouch = false;
+            if (seekBar.getProgress() + mMin != mSeekBarValue) {
+                syncValueInternal(seekBar);
+            }
+        }
+    };
+
+    /**
+     * Listener reacting to the user pressing DPAD left/right keys if {@code
+     * adjustable} attribute is set to true; it transfers the key presses to the SeekBar
+     * to be handled accordingly.
+     */
+    private View.OnKeyListener mSeekBarKeyListener = new View.OnKeyListener() {
+        @Override
+        public boolean onKey(View v, int keyCode, KeyEvent event) {
+            if (event.getAction() != KeyEvent.ACTION_DOWN) {
+                return false;
+            }
+            if (!mAdjustable && (keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
+                    keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
+                // Right or left keys are pressed when in non-adjustable mode; Skip the keys.
+                return false;
+            }
+
+            // We don't want to propagate the click keys down to the seekbar view since it will create
+            // the ripple effect for the thumb.
+            if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
+                return false;
+            }
+
+            if (mSeekBar == null) {
+                Log.e(TAG, "SeekBar view is null and hence cannot be adjusted.");
+                return false;
+            }
+            return mSeekBar.onKeyDown(keyCode, event);
+        }
+    };
+
+    public SeekBarPreference(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
+
+        /**
+         * The ordering of these two statements are important. If we want to set max first, we need
+         * to perform the same steps by changing min/max to max/min as following:
+         * mMax = a.getInt(...) and setMin(...).
+         */
+        mMin = a.getInt(R.styleable.SeekBarPreference_min, 0);
+        setMax(a.getInt(R.styleable.SeekBarPreference_android_max, 100));
+        mAdjustable = a.getBoolean(R.styleable.SeekBarPreference_adjustable, true);
+        mShowSeekBarValue = a.getBoolean(R.styleable.SeekBarPreference_showSeekBarValue, true);
+        a.recycle();
+    }
+
+    public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public SeekBarPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.seekBarPreferenceStyle);
+    }
+
+    public SeekBarPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+        view.itemView.setOnKeyListener(mSeekBarKeyListener);
+        mSeekBar = (SeekBar) view.findViewById(R.id.seekbar);
+        mSeekBarValueTextView = (TextView) view.findViewById(R.id.seekbar_value);
+        if (mShowSeekBarValue) {
+            mSeekBarValueTextView.setVisibility(View.VISIBLE);
+        } else {
+            mSeekBarValueTextView.setVisibility(View.GONE);
+            mSeekBarValueTextView = null;
+        }
+
+        if (mSeekBar == null) {
+            Log.e(TAG, "SeekBar view is null in onBindViewHolder.");
+            return;
+        }
+        mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
+        mSeekBar.setMax(mMax - mMin);
+        mSeekBar.setProgress(mSeekBarValue - mMin);
+        if (mSeekBarValueTextView != null) {
+            mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
+        }
+        mSeekBar.setEnabled(isEnabled());
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setValue(restoreValue ? getPersistedInt(mSeekBarValue)
+                : (Integer) defaultValue);
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getInt(index, 0);
+    }
+
+    public void setMin(int min) {
+        if (min > mMax) {
+            min = mMax;
+        }
+        if (min != mMin) {
+            mMin = min;
+            notifyChanged();
+        }
+    }
+
+    public int getMin() {
+        return mMin;
+    }
+
+    public void setMax(int max) {
+        if (max < mMin) {
+            max = mMin;
+        }
+        if (max != mMax) {
+            mMax = max;
+            notifyChanged();
+        }
+    }
+
+    public int getMax() {
+        return mMax;
+    }
+
+    public void setAdjustable(boolean adjustable) {
+        mAdjustable = adjustable;
+    }
+
+    public boolean isAdjustable() {
+        return mAdjustable;
+    }
+
+    public void setValue(int seekBarValue) {
+        setValueInternal(seekBarValue, true);
+    }
+
+    private void setValueInternal(int seekBarValue, boolean notifyChanged) {
+        if (seekBarValue < mMin) {
+            seekBarValue = mMin;
+        }
+        if (seekBarValue > mMax) {
+            seekBarValue = mMax;
+        }
+
+        if (seekBarValue != mSeekBarValue) {
+            mSeekBarValue = seekBarValue;
+            if (mSeekBarValueTextView != null) {
+                mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
+            }
+            persistInt(seekBarValue);
+            if (notifyChanged) {
+                notifyChanged();
+            }
+        }
+    }
+
+    public int getValue() {
+        return mSeekBarValue;
+    }
+
+    /**
+     * Persist the seekBar's seekbar value if callChangeListener
+     * returns true, otherwise set the seekBar's value to the stored value
+     */
+    private void syncValueInternal(SeekBar seekBar) {
+        int seekBarValue = mMin + seekBar.getProgress();
+        if (seekBarValue != mSeekBarValue) {
+            if (callChangeListener(seekBarValue)) {
+                setValueInternal(seekBarValue, false);
+            } else {
+                seekBar.setProgress(mSeekBarValue - mMin);
+            }
+        }
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (isPersistent()) {
+            // No need to save instance state since it's persistent
+            return superState;
+        }
+
+        // Save the instance state
+        final SavedState myState = new SavedState(superState);
+        myState.seekBarValue = mSeekBarValue;
+        myState.min = mMin;
+        myState.max = mMax;
+        return myState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (!state.getClass().equals(SavedState.class)) {
+            // Didn't save state for us in onSaveInstanceState
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        // Restore the instance state
+        SavedState myState = (SavedState) state;
+        super.onRestoreInstanceState(myState.getSuperState());
+        mSeekBarValue = myState.seekBarValue;
+        mMin = myState.min;
+        mMax = myState.max;
+        notifyChanged();
+    }
+
+    /**
+     * SavedState, a subclass of {@link BaseSavedState}, will store the state
+     * of MyPreference, a subclass of Preference.
+     * <p>
+     * It is important to always call through to super methods.
+     */
+    private static class SavedState extends BaseSavedState {
+        int seekBarValue;
+        int min;
+        int max;
+
+        public SavedState(Parcel source) {
+            super(source);
+
+            // Restore the click counter
+            seekBarValue = source.readInt();
+            min = source.readInt();
+            max = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+
+            // Save the click counter
+            dest.writeInt(seekBarValue);
+            dest.writeInt(min);
+            dest.writeInt(max);
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @SuppressWarnings("unused")
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+}
diff --git a/v7/preference/src/android/support/v7/preference/UnPressableLinearLayout.java b/v7/preference/src/android/support/v7/preference/UnPressableLinearLayout.java
new file mode 100644
index 0000000..7129cd7
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/UnPressableLinearLayout.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v7.preference;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.support.annotation.RestrictTo;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+/**
+ * Custom LinearLayout that does not propagate the pressed state down to its children.
+ * By default, the pressed state is propagated to all the children that are not clickable
+ * or long-clickable.
+ * @hide
+ */
+@RestrictTo(GROUP_ID)
+public class UnPressableLinearLayout extends LinearLayout {
+    public UnPressableLinearLayout(Context context) {
+        this(context, null);
+    }
+
+    public UnPressableLinearLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void dispatchSetPressed(boolean pressed) {
+        // Skip dispatching the pressed key state to the children so that they don't trigger any
+        // pressed state animation on their stateful drawables.
+    }
+}
diff --git a/v7/preference/tests/AndroidManifest.xml b/v7/preference/tests/AndroidManifest.xml
new file mode 100644
index 0000000..d47fd5b
--- /dev/null
+++ b/v7/preference/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<!--
+  Copyright (C) 2016 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.support.v7.preference.tests">
+
+    <uses-sdk
+        android:minSdkVersion="9"
+        android:targetSdkVersion="24"
+        tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
+                android.support.test.espresso, android.support.test.espresso.idling" />
+
+    <application/>
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="android.support.v7.preference.tests" />
+
+</manifest>
diff --git a/v7/preference/tests/NO_DOCS b/v7/preference/tests/NO_DOCS
new file mode 100644
index 0000000..db956bc
--- /dev/null
+++ b/v7/preference/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
\ No newline at end of file
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/SimplePreferenceComparisonCallbackTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/SimplePreferenceComparisonCallbackTest.java
new file mode 100644
index 0000000..64c65fa
--- /dev/null
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/SimplePreferenceComparisonCallbackTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.support.v7.preference.tests;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.drawable.Drawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.TwoStatePreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SimplePreferenceComparisonCallbackTest {
+
+    private Preference mPref1;
+    private Preference mPref2;
+    private PreferenceManager.PreferenceComparisonCallback mComparisonCallback;
+
+    @Before
+    public void setup() throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        mPref1 = new Preference(context);
+        mPref2 = new Preference(context);
+        mComparisonCallback = new PreferenceManager.SimplePreferenceComparisonCallback();
+    }
+
+    /**
+     * Basic sanity test, all fields blank should compare the same
+     * @throws Exception
+     */
+    @Test
+    public void testNull() throws Exception {
+        assertTrue("Compare all null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    /**
+     * Two different classes should not compare the same
+     * @throws Exception
+     */
+    @Test
+    public void testClassComparison() throws Exception {
+        final Preference checkboxPreference =
+                new CheckBoxPreference(InstrumentationRegistry.getTargetContext());
+        assertFalse("Compare class",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, checkboxPreference));
+    }
+
+    /**
+     * Same instance, but detached and reattached should not compare the same
+     * @throws Exception
+     */
+    @Test
+    public void testDetached() throws Exception {
+        mPref1.onDetached();
+        mPref1.onAttached();
+        assertFalse("Compare same, detached",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref1));
+    }
+
+    /**
+     * Title differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testTitleComparison() throws Exception {
+        mPref1.setTitle("value 1");
+
+        assertFalse("Compare non-null to null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+        assertFalse("Compare null to non-null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref2, mPref1));
+
+        mPref2.setTitle("value 1");
+
+        assertTrue("Compare identical",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setTitle("value 2");
+
+        assertFalse("Compare different",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    /**
+     * Summary differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testSummaryComparison() throws Exception {
+        mPref1.setSummary("value 1");
+
+        assertFalse("Compare non-null to null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+        assertFalse("Compare null to non-null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref2, mPref1));
+
+        mPref2.setSummary("value 1");
+
+        assertTrue("Compare identical",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setSummary("value 2");
+
+        assertFalse("Compare different",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    private static class ComparisonDrawable extends Drawable {
+
+        private final int mId;
+
+        public ComparisonDrawable(int id) {
+            mId = id;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {}
+
+        @Override
+        public void setAlpha(int alpha) {}
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {}
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof ComparisonDrawable && ((ComparisonDrawable)o).getId() == mId;
+        }
+
+        @Override
+        public int hashCode() {
+            return mId;
+        }
+    }
+
+    /**
+     * Icon differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testIconComparison() throws Exception {
+        final Drawable drawable1 = new ComparisonDrawable(1);
+        final Drawable drawable1a = new ComparisonDrawable(1);
+        final Drawable drawable2 = new ComparisonDrawable(2);
+
+        mPref1.setIcon(drawable1);
+
+        assertFalse("Compare non-null to null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+        assertFalse("Compare null to non-null",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref2, mPref1));
+
+        mPref2.setIcon(drawable1);
+
+        assertTrue("Compare aliased",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setIcon(drawable1a);
+
+        assertTrue("Compare equal",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setIcon(drawable2);
+
+        assertFalse("Compare unequal",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    /**
+     * Enabled differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testEnabledComparison() throws Exception {
+        mPref1.setEnabled(true);
+        mPref2.setEnabled(true);
+
+        assertTrue("Compare enabled",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setEnabled(false);
+
+        assertFalse("Compare enabled/disabled",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+        assertFalse("Compare disable/enabled",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref2, mPref1));
+
+        mPref1.setEnabled(false);
+
+        assertTrue("Compare disabled",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    /**
+     * Selectable differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testSelectableComparison() throws Exception {
+        mPref1.setSelectable(true);
+        mPref2.setSelectable(true);
+
+        assertTrue("Compare selectable",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+
+        mPref2.setSelectable(false);
+
+        assertFalse("Compare selectable/unselectable",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+        assertFalse("Compare unselectable/selectable",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref2, mPref1));
+
+        mPref1.setSelectable(false);
+
+        assertTrue("Compare unselectable",
+                mComparisonCallback.arePreferenceContentsTheSame(mPref1, mPref2));
+    }
+
+    /**
+     * For {@link TwoStatePreference} objects, checked state differences should be detected
+     * @throws Exception
+     */
+    @Test
+    public void testTwoStateComparison() throws Exception {
+        final TwoStatePreference checkbox1 =
+                new CheckBoxPreference(InstrumentationRegistry.getTargetContext());
+        final TwoStatePreference checkbox2 =
+                new CheckBoxPreference(InstrumentationRegistry.getTargetContext());
+
+        checkbox1.setChecked(true);
+        checkbox2.setChecked(true);
+
+        assertTrue("Compare checked",
+                mComparisonCallback.arePreferenceContentsTheSame(checkbox1, checkbox2));
+
+        checkbox2.setChecked(false);
+
+        assertFalse("Compare checked/unchecked",
+                mComparisonCallback.arePreferenceContentsTheSame(checkbox1, checkbox2));
+        assertFalse("Compare unchecked/checked",
+                mComparisonCallback.arePreferenceContentsTheSame(checkbox2, checkbox1));
+
+        checkbox1.setChecked(false);
+
+        assertTrue("Compare unchecked",
+                mComparisonCallback.arePreferenceContentsTheSame(checkbox1, checkbox2));
+    }
+
+    /**
+     * {@link DropDownPreference} is a special case, the pref object will need to re-bind the
+     * spinner when recycled, so distinct instances are never evaluated as equal
+     * @throws Exception
+     */
+    @Test
+    public void testDropDownComparison() throws Exception {
+        final Preference dropdown1 =
+                new DropDownPreference(InstrumentationRegistry.getTargetContext());
+        final Preference dropdown2 =
+                new DropDownPreference(InstrumentationRegistry.getTargetContext());
+
+        assertTrue("Compare aliased drop down pref",
+                mComparisonCallback.arePreferenceContentsTheSame(dropdown1, dropdown1));
+        assertFalse("Compare distinct drop down prefs",
+                mComparisonCallback.arePreferenceContentsTheSame(dropdown1, dropdown2));
+    }
+}
diff --git a/v7/recyclerview/Android.mk b/v7/recyclerview/Android.mk
index 52572fe..e434ab2 100644
--- a/v7/recyclerview/Android.mk
+++ b/v7/recyclerview/Android.mk
@@ -18,17 +18,15 @@
 # Applications that use this library must specify
 #
 #   LOCAL_STATIC_ANDROID_LIBRARIES := \
-#       android-support-v7-recycler-view \
+#       android-support-v7-recyclerview \
 #       android-support-compat \
-#       android-support-core-ui \
-#       android-support-annotations
+#       android-support-core-ui
 #
 # in their makefiles to include the resources and their dependencies in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE := android-support-v7-recyclerview
-LOCAL_SDK_VERSION := 9
-LOCAL_SDK_RES_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
+LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_SHARED_ANDROID_LIBRARIES := \
@@ -37,5 +35,5 @@
     android-support-annotations
 LOCAL_JAR_EXCLUDE_FILES := none
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
 include $(BUILD_STATIC_JAVA_LIBRARY)
-
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index a5db437..793fb95 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -14,6 +14,9 @@
     }
     testCompile 'junit:junit:4.12'
     testCompile "org.mockito:mockito-core:1.9.5"
+    testCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
+        exclude module: 'support-annotations'
+    }
     androidTestCompile "org.mockito:mockito-core:1.9.5"
     androidTestCompile "com.google.dexmaker:dexmaker:1.2"
     androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/BatchingListUpdateCallbackTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/BatchingListUpdateCallbackTest.java
index 851bad6..bb3e6ea 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/BatchingListUpdateCallbackTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/BatchingListUpdateCallbackTest.java
@@ -15,13 +15,16 @@
  */
 package android.support.v7.util;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import static org.mockito.Mockito.*;
 
 @RunWith(JUnit4.class)
 @SmallTest
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
index bf9c7b8..9315a19 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/DiffUtilTest.java
@@ -22,8 +22,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.support.annotation.Nullable;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
 
 import org.hamcrest.CoreMatchers;
 import org.junit.Rule;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListBatchedCallbackTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListBatchedCallbackTest.java
index ccffd70..3ace217 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListBatchedCallbackTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListBatchedCallbackTest.java
@@ -20,7 +20,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
index f53b5b2..da3c957 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.util;
 
+import android.support.test.filters.SmallTest;
+
 import junit.framework.TestCase;
 
 import org.junit.Before;
@@ -23,8 +25,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import android.test.suitebuilder.annotation.SmallTest;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
index a5a8e33..c303c60 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
@@ -16,6 +16,14 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.RecyclerView.ViewHolder;
+
+import android.support.test.filters.SmallTest;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
 import junit.framework.AssertionFailedError;
 import junit.framework.TestResult;
 
@@ -24,12 +32,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -37,8 +39,6 @@
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static android.support.v7.widget.RecyclerView.*;
-
 @RunWith(JUnit4.class)
 @SmallTest
 public class AdapterHelperTest extends AndroidTestCase {
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
index 884cdd5..f3cd131 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
@@ -16,6 +16,18 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
+import static android.support.v7.widget.AdapterHelper.UpdateOp.MOVE;
+import static android.support.v7.widget.AdapterHelper.UpdateOp.REMOVE;
+import static android.support.v7.widget.AdapterHelper.UpdateOp.UPDATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.support.v7.widget.AdapterHelper.UpdateOp;
+import android.util.Log;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -27,16 +39,6 @@
 import java.util.Random;
 import java.util.Set;
 
-import android.support.v7.widget.AdapterHelper.UpdateOp;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.MOVE;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.REMOVE;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.UPDATE;
-import static org.junit.Assert.*;
-
 @RunWith(JUnit4.class)
 @SmallTest
 public class OpReorderTest {
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
index d8ff22a..4a224a4 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
@@ -16,6 +16,19 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_POST;
+import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.filters.SmallTest;
+import android.support.v4.util.Pair;
+import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.View;
+
 import junit.framework.TestCase;
 
 import org.junit.Before;
@@ -23,24 +36,11 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.View;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_POST;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
-import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-
 @SuppressWarnings("ConstantConditions")
 @RunWith(JUnit4.class)
 @SmallTest
diff --git a/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java b/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
index 47e9722..c133a98 100644
--- a/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/AdapterHelper.java
@@ -23,8 +23,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import static android.support.v7.widget.RecyclerView.*;
-
 /**
  * Helper class that can enqueue and process adapter update operations.
  * <p>
@@ -138,7 +136,7 @@
         int type = -1;
         for (int position = op.positionStart; position < tmpEnd; position++) {
             boolean typeChanged = false;
-            ViewHolder vh = mCallback.findViewHolder(position);
+            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
             if (vh != null || canFindInPreLayout(position)) {
                 // If a ViewHolder exists or this is a newly added item, we can defer this update
                 // to post layout stage.
@@ -191,7 +189,7 @@
         int tmpEnd = op.positionStart + op.itemCount;
         int type = -1;
         for (int position = op.positionStart; position < tmpEnd; position++) {
-            ViewHolder vh = mCallback.findViewHolder(position);
+            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
             if (vh != null || canFindInPreLayout(position)) { // deferred
                 if (type == POSITION_TYPE_INVISIBLE) {
                     UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
@@ -756,9 +754,9 @@
     /**
      * Contract between AdapterHelper and RecyclerView.
      */
-    static interface Callback {
+    interface Callback {
 
-        ViewHolder findViewHolder(int position);
+        RecyclerView.ViewHolder findViewHolder(int position);
 
         void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
index 0afa405..dd75b19 100644
--- a/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/ChildHelper.java
@@ -200,16 +200,16 @@
      * This can be used to find a disappearing view by position.
      *
      * @param position The adapter position of the item.
-     * @param type     View type, can be {@link RecyclerView#INVALID_TYPE}.
-     * @return         A hidden view with a valid ViewHolder that matches the position and type.
+     * @return         A hidden view with a valid ViewHolder that matches the position.
      */
-    View findHiddenNonRemovedView(int position, int type) {
+    View findHiddenNonRemovedView(int position) {
         final int count = mHiddenViews.size();
         for (int i = 0; i < count; i++) {
             final View view = mHiddenViews.get(i);
             RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
-            if (holder.getLayoutPosition() == position && !holder.isInvalid() && !holder.isRemoved()
-                    && (type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
+            if (holder.getLayoutPosition() == position
+                    && !holder.isInvalid()
+                    && !holder.isRemoved()) {
                 return view;
             }
         }
diff --git a/v7/recyclerview/src/android/support/v7/widget/GapWorker.java b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
new file mode 100644
index 0000000..3631e7d
--- /dev/null
+++ b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.support.v4.os.TraceCompat;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
+
+class GapWorker implements Runnable {
+
+    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
+
+    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
+    long mPostTimeNs;
+    long mFrameIntervalNs;
+
+    static class Task {
+        public boolean immediate;
+        public int viewVelocity;
+        public int distanceToItem;
+        public RecyclerView view;
+        public int position;
+
+        public void clear() {
+            immediate = false;
+            viewVelocity = 0;
+            distanceToItem = 0;
+            view = null;
+            position = 0;
+        }
+    }
+
+    /**
+     * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects
+     * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared
+     * in between calls.
+     */
+    private ArrayList<Task> mTasks = new ArrayList<>();
+
+    /**
+     * Prefetch information associated with a specfic RecyclerView.
+     */
+    static class PrefetchRegistryImpl implements RecyclerView.PrefetchRegistry {
+        private int mPrefetchDx;
+        private int mPrefetchDy;
+        int[] mPrefetchArray;
+
+        int mCount;
+
+        void setPrefetchVector(int dx, int dy) {
+            mPrefetchDx = dx;
+            mPrefetchDy = dy;
+        }
+
+        void collectPrefetchPositionsFromView(RecyclerView view) {
+            mCount = 0;
+            if (mPrefetchArray != null) {
+                Arrays.fill(mPrefetchArray, -1);
+            }
+
+            final RecyclerView.LayoutManager layout = view.mLayout;
+            if (view.mAdapter != null
+                    && layout != null
+                    && layout.isItemPrefetchEnabled()
+                    && !view.hasPendingAdapterUpdates()) {
+                layout.collectPrefetchPositions(mPrefetchDx, mPrefetchDy, view.mState, this);
+                if (mCount > layout.mPrefetchMaxCountObserved) {
+                    layout.mPrefetchMaxCountObserved = mCount;
+                    view.mRecycler.updateViewCacheSize();
+                }
+            }
+        }
+
+        @Override
+        public void addPosition(int layoutPosition, int pixelDistance) {
+            if (pixelDistance < 0) {
+                throw new IllegalArgumentException("Pixel distance must be non-negative");
+            }
+
+            // allocate or expand array as needed, doubling when needed
+            final int storagePosition = mCount * 2;
+            if (mPrefetchArray == null) {
+                mPrefetchArray = new int[4];
+                Arrays.fill(mPrefetchArray, -1);
+            } else if (storagePosition >= mPrefetchArray.length) {
+                final int[] oldArray = mPrefetchArray;
+                mPrefetchArray = new int[storagePosition * 2];
+                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
+            }
+
+            // add position
+            mPrefetchArray[storagePosition] = layoutPosition;
+            mPrefetchArray[storagePosition + 1] = pixelDistance;
+
+            mCount++;
+        }
+
+        boolean lastPrefetchIncludedPosition(int position) {
+            if (mPrefetchArray != null) {
+                final int count = mCount * 2;
+                for (int i = 0; i < count; i += 2) {
+                    if (mPrefetchArray[i] == position) return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Called when prefetch indices are no longer valid for cache prioritization.
+         */
+        void clearPrefetchPositions() {
+            if (mPrefetchArray != null) {
+                Arrays.fill(mPrefetchArray, -1);
+            }
+        }
+    }
+
+    public void add(RecyclerView recyclerView) {
+        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
+            throw new IllegalStateException("RecyclerView already present in worker list!");
+        }
+        mRecyclerViews.add(recyclerView);
+    }
+
+    public void remove(RecyclerView recyclerView) {
+        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
+        if (RecyclerView.DEBUG && !removeSuccess) {
+            throw new IllegalStateException("RecyclerView removal failed!");
+        }
+    }
+
+    /**
+     * Schedule a prefetch immediately after the current traversal.
+     */
+    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
+        if (recyclerView.isAttachedToWindow()) {
+            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
+                throw new IllegalStateException("attempting to post unregistered view!");
+            }
+            if (mPostTimeNs == 0) {
+                mPostTimeNs = recyclerView.getNanoTime();
+                recyclerView.post(this);
+            }
+        }
+
+        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
+    }
+
+    static Comparator<Task> sTaskComparator = new Comparator<Task>() {
+        @Override
+        public int compare(Task lhs, Task rhs) {
+            // first, prioritize non-cleared tasks
+            if ((lhs.view == null) != (rhs.view == null)) {
+                return lhs.view == null ? 1 : -1;
+            }
+
+            // then prioritize immediate
+            if (lhs.immediate != rhs.immediate) {
+                return lhs.immediate ? -1 : 1;
+            }
+
+            // then prioritize _highest_ view velocity
+            int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity;
+            if (deltaViewVelocity != 0) return deltaViewVelocity;
+
+            // then prioritize _lowest_ distance to item
+            int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem;
+            if (deltaDistanceToItem != 0) return deltaDistanceToItem;
+
+            return 0;
+        }
+    };
+
+    private void buildTaskList() {
+        // Update PrefetchRegistry in each view
+        final int viewCount = mRecyclerViews.size();
+        int totalTaskCount = 0;
+        for (int i = 0; i < viewCount; i++) {
+            RecyclerView view = mRecyclerViews.get(i);
+            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view);
+            totalTaskCount += view.mPrefetchRegistry.mCount;
+        }
+
+        // Populate task list from prefetch data...
+        mTasks.ensureCapacity(totalTaskCount);
+        int totalTaskIndex = 0;
+        for (int i = 0; i < viewCount; i++) {
+            RecyclerView view = mRecyclerViews.get(i);
+            PrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
+            final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
+                    + Math.abs(prefetchRegistry.mPrefetchDy);
+            for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
+                final Task task;
+                if (totalTaskIndex >= mTasks.size()) {
+                    task = new Task();
+                    mTasks.add(task);
+                } else {
+                    task = mTasks.get(totalTaskIndex);
+                }
+                final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
+
+                task.immediate = distanceToItem <= viewVelocity;
+                task.viewVelocity = viewVelocity;
+                task.distanceToItem = distanceToItem;
+                task.view = view;
+                task.position = prefetchRegistry.mPrefetchArray[j];
+
+                totalTaskIndex++;
+            }
+        }
+
+        // ... and priority sort
+        Collections.sort(mTasks, sTaskComparator);
+    }
+
+    static boolean isPrefetchPositionAttached(RecyclerView view, int position) {
+        final int childCount = view.mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View attachedView = view.mChildHelper.getUnfilteredChildAt(i);
+            RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView);
+            // Note: can use mPosition here because adapter doesn't have pending updates
+            if (holder.mPosition == position && !holder.isInvalid()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void flushTasksWithDeadline(long deadlineNs) {
+        for (int i = 0; i < mTasks.size(); i++) {
+            final Task task = mTasks.get(i);
+            if (task.view == null) {
+                // abort, only empty Tasks left
+                return;
+            }
+
+            if (isPrefetchPositionAttached(task.view, task.position)) {
+                // don't attempt to prefetch attached views
+                continue;
+            }
+
+            RecyclerView.Recycler recycler = task.view.mRecycler;
+            RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
+                    task.position, false, task.immediate ? RecyclerView.FOREVER_NS : deadlineNs);
+
+            if (holder != null) {
+                if (holder.isBound()) {
+                    // Only give the view a chance to go into the cache if binding succeeded
+                    // Note that we must use public method, since item may need cleanup
+                    recycler.recycleView(holder.itemView);
+                } else {
+                    // Didn't bind, so we can't cache the view, but it will stay in the pool until
+                    // next prefetch/traversal. If a View fails to bind, it means we didn't have
+                    // enough time prior to the deadline (and won't for other instances of this
+                    // type, during this GapWorker prefetch pass).
+                    recycler.addViewHolderToRecycledViewPool(holder);
+                }
+            }
+            task.clear();
+        }
+    }
+
+    void prefetch(long deadlineNs) {
+        buildTaskList();
+        flushTasksWithDeadline(deadlineNs);
+    }
+
+    @Override
+    public void run() {
+        try {
+            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
+
+            if (mRecyclerViews.isEmpty()) {
+                // abort - no work to do
+                return;
+            }
+
+            // Query last vsync so we can predict next one. Note that drawing time not yet
+            // valid in animation/input callbacks, so query it here to be safe.
+            long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(
+                    mRecyclerViews.get(0).getDrawingTime());
+            if (lastFrameVsyncNs == 0) {
+                // abort - couldn't get last vsync for estimating next
+                return;
+            }
+
+            // TODO: consider rebasing deadline if frame was already dropped due to long UI work.
+            // Next frame will still wait for VSYNC, so we can still use the gap if it exists.
+            long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs;
+
+            prefetch(nextFrameNs);
+
+            // TODO: consider rescheduling self, if there's more work to do
+        } finally {
+            mPostTimeNs = 0;
+            TraceCompat.endSection();
+        }
+    }
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index 6365017..c4a72d3 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -505,24 +505,18 @@
     }
 
     @Override
-    int getItemPrefetchCount() {
-        return mSpanCount;
-    }
-
-    @Override
-    int gatherPrefetchIndicesForLayoutState(RecyclerView.State state, LayoutState layoutState,
-                int[] outIndices) {
+    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
+            RecyclerView.PrefetchRegistry prefetchRegistry) {
         int remainingSpan = mSpanCount;
         int count = 0;
         while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
             final int pos = layoutState.mCurrentPosition;
-            outIndices[count] = pos;
+            prefetchRegistry.addPosition(pos, layoutState.mScrollingOffset);
             final int spanSize = mSpanSizeLookup.getSpanSize(pos);
             remainingSpan -= spanSize;
             layoutState.mCurrentPosition += layoutState.mItemDirection;
             count++;
         }
-        return count;
     }
 
     @Override
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index d21406c..1f4682f 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -1182,34 +1182,28 @@
                 && mOrientationHelper.getEnd() == 0;
     }
 
-    @Override
-    int getItemPrefetchCount() {
-        return 1;
-    }
-
-    int gatherPrefetchIndicesForLayoutState(RecyclerView.State state, LayoutState layoutState,
-            int[] outIndices) {
+    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
+            RecyclerView.PrefetchRegistry prefetchRegistry) {
         final int pos = layoutState.mCurrentPosition;
         if (pos >= 0 && pos < state.getItemCount()) {
-            outIndices[0] = pos;
-            return 1;
+            prefetchRegistry.addPosition(pos, layoutState.mScrollingOffset);
         }
-        return 0;
     }
 
+    /** @hide */
     @Override
-    int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+    public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+            RecyclerView.PrefetchRegistry prefetchRegistry) {
         int delta = (mOrientation == HORIZONTAL) ? dx : dy;
         if (getChildCount() == 0 || delta == 0) {
             // can't support this scroll, so don't bother prefetching
-            return 0;
+            return;
         }
 
-
         final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
         final int absDy = Math.abs(delta);
         updateLayoutState(layoutDirection, absDy, true, state);
-        return gatherPrefetchIndicesForLayoutState(state, mLayoutState, outIndices);
+        collectPrefetchPositionsForLayoutState(state, mLayoutState, prefetchRegistry);
     }
 
     int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/PagerSnapHelper.java b/v7/recyclerview/src/android/support/v7/widget/PagerSnapHelper.java
new file mode 100644
index 0000000..3190f91
--- /dev/null
+++ b/v7/recyclerview/src/android/support/v7/widget/PagerSnapHelper.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import android.graphics.PointF;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+/**
+ * Implementation of the {@link SnapHelper} supporting pager style snapping in either vertical or
+ * horizontal orientation.
+ *
+ * <p>
+ *
+ * PagerSnapHelper can help achieve a similar behavior to {@link android.support.v4.view.ViewPager}.
+ * Set both {@link RecyclerView} and {@link android.support.v7.widget.RecyclerView.Adapter} to have
+ * MATCH_PARENT height and width and then attach PagerSnapHelper to the {@link RecyclerView} using
+ * {@link #attachToRecyclerView(RecyclerView)}.
+ */
+public class PagerSnapHelper extends SnapHelper {
+    private static final int MAX_SCROLL_ON_FLING_DURATION = 100; // ms
+
+    // Orientation helpers are lazily created per LayoutManager.
+    @Nullable
+    private OrientationHelper mVerticalHelper;
+    @Nullable
+    private OrientationHelper mHorizontalHelper;
+
+    @Nullable
+    @Override
+    public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
+            @NonNull View targetView) {
+        int[] out = new int[2];
+        if (layoutManager.canScrollHorizontally()) {
+            out[0] = distanceToCenter(layoutManager, targetView,
+                    getHorizontalHelper(layoutManager));
+        } else {
+            out[0] = 0;
+        }
+
+        if (layoutManager.canScrollVertically()) {
+            out[1] = distanceToCenter(layoutManager, targetView,
+                    getVerticalHelper(layoutManager));
+        } else {
+            out[1] = 0;
+        }
+        return out;
+    }
+
+    @Nullable
+    @Override
+    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
+        if (layoutManager.canScrollVertically()) {
+            return findCenterView(layoutManager, getVerticalHelper(layoutManager));
+        } else if (layoutManager.canScrollHorizontally()) {
+            return findCenterView(layoutManager, getHorizontalHelper(layoutManager));
+        }
+        return null;
+    }
+
+    @Override
+    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
+            int velocityY) {
+        final int itemCount = layoutManager.getItemCount();
+        if (itemCount == 0) {
+            return RecyclerView.NO_POSITION;
+        }
+
+        View mStartMostChildView = null;
+        if (layoutManager.canScrollVertically()) {
+            mStartMostChildView = findStartView(layoutManager, getVerticalHelper(layoutManager));
+        } else if (layoutManager.canScrollHorizontally()) {
+            mStartMostChildView = findStartView(layoutManager, getHorizontalHelper(layoutManager));
+        }
+
+        if (mStartMostChildView == null) {
+            return RecyclerView.NO_POSITION;
+        }
+        final int centerPosition = layoutManager.getPosition(mStartMostChildView);
+        if (centerPosition == RecyclerView.NO_POSITION) {
+            return RecyclerView.NO_POSITION;
+        }
+
+        final boolean forwardDirection;
+        if (layoutManager.canScrollHorizontally()) {
+            forwardDirection = velocityX > 0;
+        } else {
+            forwardDirection = velocityY > 0;
+        }
+        boolean reverseLayout = false;
+        if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
+            RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
+                    (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager;
+            PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
+            if (vectorForEnd != null) {
+                reverseLayout = vectorForEnd.x < 0 || vectorForEnd.y < 0;
+            }
+        }
+        return reverseLayout
+                ? (forwardDirection ? centerPosition - 1 : centerPosition)
+                : (forwardDirection ? centerPosition + 1 : centerPosition);
+    }
+
+    @Override
+    protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
+        if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
+            return null;
+        }
+        return new LinearSmoothScroller(mRecyclerView.getContext()) {
+            @Override
+            protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
+                int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
+                        targetView);
+                final int dx = snapDistances[0];
+                final int dy = snapDistances[1];
+                final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
+                if (time > 0) {
+                    action.update(dx, dy, time, mDecelerateInterpolator);
+                }
+            }
+
+            @Override
+            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
+                return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
+            }
+
+            @Override
+            protected int calculateTimeForScrolling(int dx) {
+                return Math.min(MAX_SCROLL_ON_FLING_DURATION, super.calculateTimeForScrolling(dx));
+            }
+        };
+    }
+
+    private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
+            @NonNull View targetView, OrientationHelper helper) {
+        final int childCenter = helper.getDecoratedStart(targetView)
+                + (helper.getDecoratedMeasurement(targetView) / 2);
+        final int containerCenter;
+        if (layoutManager.getClipToPadding()) {
+            containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
+        } else {
+            containerCenter = helper.getEnd() / 2;
+        }
+        return childCenter - containerCenter;
+    }
+
+    /**
+     * Return the child view that is currently closest to the center of this parent.
+     *
+     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
+     *                      {@link RecyclerView}.
+     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
+     *
+     * @return the child view that is currently closest to the center of this parent.
+     */
+    @Nullable
+    private View findCenterView(RecyclerView.LayoutManager layoutManager,
+            OrientationHelper helper) {
+        int childCount = layoutManager.getChildCount();
+        if (childCount == 0) {
+            return null;
+        }
+
+        View closestChild = null;
+        final int center;
+        if (layoutManager.getClipToPadding()) {
+            center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
+        } else {
+            center = helper.getEnd() / 2;
+        }
+        int absClosest = Integer.MAX_VALUE;
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = layoutManager.getChildAt(i);
+            int childCenter = helper.getDecoratedStart(child)
+                    + (helper.getDecoratedMeasurement(child) / 2);
+            int absDistance = Math.abs(childCenter - center);
+
+            /** if child center is closer than previous closest, set it as closest  **/
+            if (absDistance < absClosest) {
+                absClosest = absDistance;
+                closestChild = child;
+            }
+        }
+        return closestChild;
+    }
+
+    /**
+     * Return the child view that is currently closest to the start of this parent.
+     *
+     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
+     *                      {@link RecyclerView}.
+     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
+     *
+     * @return the child view that is currently closest to the start of this parent.
+     */
+    @Nullable
+    private View findStartView(RecyclerView.LayoutManager layoutManager,
+            OrientationHelper helper) {
+        int childCount = layoutManager.getChildCount();
+        if (childCount == 0) {
+            return null;
+        }
+
+        View closestChild = null;
+        int startest = Integer.MAX_VALUE;
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = layoutManager.getChildAt(i);
+            int childStart = helper.getDecoratedStart(child);
+
+            /** if child is more to start than previous closest, set it as closest  **/
+            if (childStart < startest) {
+                startest = childStart;
+                closestChild = child;
+            }
+        }
+        return closestChild;
+    }
+
+    @NonNull
+    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
+        if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) {
+            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
+        }
+        return mVerticalHelper;
+    }
+
+    @NonNull
+    private OrientationHelper getHorizontalHelper(
+            @NonNull RecyclerView.LayoutManager layoutManager) {
+        if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) {
+            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
+        }
+        return mHorizontalHelper;
+    }
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 46da1b8..1463722 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -17,6 +17,8 @@
 
 package android.support.v7.widget;
 
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Observable;
@@ -57,7 +59,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.FocusFinder;
@@ -76,14 +77,8 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-import static android.support.v7.widget.AdapterHelper.Callback;
-import static android.support.v7.widget.AdapterHelper.UpdateOp;
 
 /**
  * A flexible view for providing a limited window into a large data set.
@@ -185,7 +180,13 @@
      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
      */
-    private static final boolean ALLOW_PREFETCHING = Build.VERSION.SDK_INT >= 21;
+    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
+
+    /**
+     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
+     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
+     */
+    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
 
     static final boolean DISPATCH_TEMP_DETACH = false;
     public static final int HORIZONTAL = 0;
@@ -252,7 +253,7 @@
     /**
      * RecyclerView is attempting to pre-populate off screen views.
      */
-    private static final String TRACE_PREFETCH_TAG = "RV Prefetch";
+    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
 
     /**
      * RecyclerView is creating a new View.
@@ -402,6 +403,8 @@
      */
     public static final int SCROLL_STATE_SETTLING = 2;
 
+    static final long FOREVER_NS = Long.MAX_VALUE;
+
     // Touch/scrolling handling
 
     private int mScrollState = SCROLL_STATE_IDLE;
@@ -421,9 +424,9 @@
 
     final ViewFlinger mViewFlinger = new ViewFlinger();
 
-    private static final long MIN_PREFETCH_TIME_NANOS = TimeUnit.MILLISECONDS.toNanos(4);
-    static long sFrameIntervalNanos = 0;
-    ViewPrefetcher mViewPrefetcher = ALLOW_PREFETCHING ? new ViewPrefetcher() : null;
+    GapWorker mGapWorker;
+    GapWorker.PrefetchRegistryImpl mPrefetchRegistry =
+            ALLOW_THREAD_GAP_WORK ? new GapWorker.PrefetchRegistryImpl() : null;
 
     final State mState = new State();
 
@@ -765,7 +768,7 @@
     }
 
     void initAdapterManager() {
-        mAdapterHelper = new AdapterHelper(new Callback() {
+        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
             @Override
             public ViewHolder findViewHolder(int position) {
                 final ViewHolder vh = findViewHolderForPosition(position, true);
@@ -803,30 +806,30 @@
             }
 
             @Override
-            public void onDispatchFirstPass(UpdateOp op) {
+            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
                 dispatchUpdate(op);
             }
 
-            void dispatchUpdate(UpdateOp op) {
+            void dispatchUpdate(AdapterHelper.UpdateOp op) {
                 switch (op.cmd) {
-                    case UpdateOp.ADD:
+                    case AdapterHelper.UpdateOp.ADD:
                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
                         break;
-                    case UpdateOp.REMOVE:
+                    case AdapterHelper.UpdateOp.REMOVE:
                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
                         break;
-                    case UpdateOp.UPDATE:
+                    case AdapterHelper.UpdateOp.UPDATE:
                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
                                 op.payload);
                         break;
-                    case UpdateOp.MOVE:
+                    case AdapterHelper.UpdateOp.MOVE:
                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
                         break;
                 }
             }
 
             @Override
-            public void onDispatchSecondPass(UpdateOp op) {
+            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
                 dispatchUpdate(op);
             }
 
@@ -1573,8 +1576,9 @@
 
         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
         // of the visible items is affected and if not, just ignore the change.
-        if (mAdapterHelper.hasAnyUpdateTypes(UpdateOp.UPDATE) && !mAdapterHelper
-                .hasAnyUpdateTypes(UpdateOp.ADD | UpdateOp.REMOVE | UpdateOp.MOVE)) {
+        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
+                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
+                        | AdapterHelper.UpdateOp.MOVE)) {
             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
             eatRequestLayout();
             mAdapterHelper.preProcess();
@@ -1919,6 +1923,18 @@
      * @param dy Pixels to scroll vertically
      */
     public void smoothScrollBy(int dx, int dy) {
+        smoothScrollBy(dx, dy, null);
+    }
+
+    /**
+     * Animate a scroll by the given amount of pixels along either axis.
+     *
+     * @param dx Pixels to scroll horizontally
+     * @param dy Pixels to scroll vertically
+     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
+     *                     {@code null}, RecyclerView is going to use the default interpolator.
+     */
+    public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
         if (mLayout == null) {
             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
                     "Call setLayoutManager with a non-null argument.");
@@ -1934,7 +1950,7 @@
             dy = 0;
         }
         if (dx != 0 || dy != 0) {
-            mViewFlinger.smoothScrollBy(dx, dy);
+            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
         }
     }
 
@@ -2224,6 +2240,10 @@
                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
                 final View found = ff.findNextFocus(this, focused, absDir);
                 needsFocusFailureLayout = found == null;
+                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+                    direction = absDir;
+                }
             }
             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
@@ -2231,6 +2251,10 @@
                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
                 final View found = ff.findNextFocus(this, focused, absDir);
                 needsFocusFailureLayout = found == null;
+                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+                    direction = absDir;
+                }
             }
             if (needsFocusFailureLayout) {
                 consumePendingUpdateOperations();
@@ -2385,18 +2409,25 @@
             mLayout.dispatchAttachedToWindow(this);
         }
         mPostedAnimatorRunner = false;
-        if (ALLOW_PREFETCHING && sFrameIntervalNanos == 0) {
-            // We only query the display/refresh rate once, since it's an expensive binder call
-            float refreshRate = 60.0f;
-            Display display = ViewCompat.getDisplay(this);
-            if (!isInEditMode() && display != null) {
-                float displayRefreshRate = display.getRefreshRate();
-                if (displayRefreshRate >= 30.0f) {
-                    // break 60 fps assumption if data appears good
-                    refreshRate = displayRefreshRate;
+
+        if (ALLOW_THREAD_GAP_WORK) {
+            // Register with gap worker
+            mGapWorker = GapWorker.sGapWorker.get();
+            if (mGapWorker == null) {
+                mGapWorker = new GapWorker();
+
+                // break 60 fps assumption if data from display appears valid
+                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
+                Display display = ViewCompat.getDisplay(this);
+                float refreshRate = 60.0f;
+                if (display != null
+                        && display.getRefreshRate() >= 30.0f) {
+                    refreshRate = display.getRefreshRate();
                 }
+                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
+                GapWorker.sGapWorker.set(mGapWorker);
             }
-            sFrameIntervalNanos = (long) (1000000000 / refreshRate);
+            mGapWorker.add(this);
         }
     }
 
@@ -2414,6 +2445,12 @@
         mPendingAccessibilityImportanceChange.clear();
         removeCallbacks(mItemAnimatorRunner);
         mViewInfoStore.onDetach();
+
+        if (ALLOW_THREAD_GAP_WORK) {
+            // Unregister with gap worker
+            mGapWorker.remove(this);
+            mGapWorker = null;
+        }
     }
 
     /**
@@ -2765,8 +2802,8 @@
                             vtev)) {
                         getParent().requestDisallowInterceptTouchEvent(true);
                     }
-                    if (ALLOW_PREFETCHING) {
-                        mViewPrefetcher.postFromTraversal(dx, dy);
+                    if (mGapWorker != null) {
+                        mGapWorker.postFromTraversal(this, dx, dy);
                     }
                 }
             } break;
@@ -3227,7 +3264,9 @@
         // only recover focus if RV itself has the focus or the focused view is hidden
         if (!isFocused()) {
             final View focusedChild = getFocusedChild();
-            if (focusedChild == null || !mChildHelper.isHidden(focusedChild)) {
+            if (focusedChild == null || (!mChildHelper.isHidden(focusedChild)
+                    // on API 15, this happens :/.
+                    && focusedChild.getParent() == this && focusedChild.hasFocus())) {
                 return;
             }
         }
@@ -4444,98 +4483,11 @@
                 || mAdapterHelper.hasPendingUpdates();
     }
 
-    /**
-     * Runs prefetch work immediately after a traversal, in the downtime while the UI thread is
-     * waiting for VSYNC.
-     */
-    class ViewPrefetcher implements Runnable {
-
-        long mPostTimeNanos;
-        private int mDx;
-        private int mDy;
-
-        int[] mItemPrefetchArray;
-
-        /**
-         * Schedule a prefetch immediately after the current traversal.
-         */
-        public void postFromTraversal(int dx, int dy) {
-            if (ALLOW_PREFETCHING
-                    && mAdapter != null
-                    && mLayout != null
-                    && mLayout.getItemPrefetchCount() > 0) {
-                mDx = dx;
-                mDy = dy;
-                mPostTimeNanos = System.nanoTime();
-                RecyclerView.this.post(this);
-            }
-        }
-
-        public boolean lastPrefetchIncludedPosition(int position) {
-            if (mItemPrefetchArray != null) {
-                for (int i = 0; i < mItemPrefetchArray.length; i++) {
-                    if (mItemPrefetchArray[i] == position) return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Called when prefetch indices are no longer valid for cache prioritization.
-         */
-        public void clearPrefetchPositions() {
-            if (mItemPrefetchArray != null) {
-                Arrays.fill(mItemPrefetchArray, -1);
-            }
-        }
-
-        @Override
-        public void run() {
-            try {
-                TraceCompat.beginSection(TRACE_PREFETCH_TAG);
-                final int prefetchCount = mLayout.getItemPrefetchCount();
-                if (mAdapter == null
-                        || mLayout == null
-                        || !mLayout.isItemPrefetchEnabled()
-                        || prefetchCount < 1
-                        || hasPendingAdapterUpdates()) {
-                    // abort - no work
-                    return;
-                }
-
-                // Query last vsync so we can predict next one. Note that drawing time not yet
-                // valid in animation/input callbacks, so query it here to be safe.
-                long lastFrameVsyncNanos = TimeUnit.MILLISECONDS.toNanos(getDrawingTime());
-                if (lastFrameVsyncNanos == 0 || sFrameIntervalNanos == 0) {
-                    // abort - couldn't get info for estimating next vsync
-                    return;
-                }
-
-                long nowNanos = System.nanoTime();
-                long nextFrameNanos = lastFrameVsyncNanos + sFrameIntervalNanos;
-                if (nowNanos - mPostTimeNanos > sFrameIntervalNanos
-                        || nextFrameNanos - nowNanos < MIN_PREFETCH_TIME_NANOS) {
-                    // abort - Executing either too far after post, or too near next scheduled vsync
-                    return;
-                }
-
-                if (mItemPrefetchArray == null || mItemPrefetchArray.length < prefetchCount) {
-                    mItemPrefetchArray = new int[prefetchCount];
-                }
-                Arrays.fill(mItemPrefetchArray, -1);
-                int viewCount = mLayout.gatherPrefetchIndices(mDx, mDy, mState, mItemPrefetchArray);
-                mRecycler.prefetch(mItemPrefetchArray, viewCount);
-            } finally {
-                TraceCompat.endSection();
-            }
-        }
-    }
-
-    private class ViewFlinger implements Runnable {
+    class ViewFlinger implements Runnable {
         private int mLastFlingX;
         private int mLastFlingY;
         private ScrollerCompat mScroller;
-        private Interpolator mInterpolator = sQuinticInterpolator;
+        Interpolator mInterpolator = sQuinticInterpolator;
 
 
         // When set to true, postOnAnimation callbacks are delayed until the run method completes
@@ -4645,13 +4597,13 @@
 
                 if (scroller.isFinished() || !fullyConsumedAny) {
                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
-                    if (ALLOW_PREFETCHING) {
-                        mViewPrefetcher.clearPrefetchPositions();
+                    if (ALLOW_THREAD_GAP_WORK) {
+                        mPrefetchRegistry.clearPrefetchPositions();
                     }
                 } else {
                     postOnAnimation();
-                    if (ALLOW_PREFETCHING) {
-                        mViewPrefetcher.postFromTraversal(dx, dy);
+                    if (mGapWorker != null) {
+                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
                     }
                 }
             }
@@ -4736,6 +4688,11 @@
             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
         }
 
+        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
+            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
+                    interpolator == null ? sQuinticInterpolator : interpolator);
+        }
+
         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
             if (mInterpolator != interpolator) {
                 mInterpolator = interpolator;
@@ -4848,20 +4805,43 @@
      *
      */
     public static class RecycledViewPool {
-        private SparseArray<ArrayList<ViewHolder>> mScrap =
-                new SparseArray<ArrayList<ViewHolder>>();
-        private SparseIntArray mMaxScrap = new SparseIntArray();
-        private int mAttachCount = 0;
-
         private static final int DEFAULT_MAX_SCRAP = 5;
 
+        /**
+         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
+         *
+         * Note that this tracks running averages of create/bind time across all RecyclerViews
+         * (and, indirectly, Adapters) that use this pool.
+         *
+         * 1) This enables us to track average create and bind times across multiple adapters. Even
+         * though create (and especially bind) may behave differently for different Adapter
+         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
+         *
+         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
+         * false for all other views of its type for the same deadline. This prevents items
+         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
+         */
+        static class ScrapData {
+            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
+            int mMaxScrap = DEFAULT_MAX_SCRAP;
+            long mCreateRunningAverageNs = 0;
+            long mBindRunningAverageNs = 0;
+        }
+        SparseArray<ScrapData> mScrap = new SparseArray<>();
+
+        private int mAttachCount = 0;
+
         public void clear() {
-            mScrap.clear();
+            for (int i = 0; i < mScrap.size(); i++) {
+                ScrapData data = mScrap.valueAt(i);
+                data.mScrapHeap.clear();
+            }
         }
 
         public void setMaxRecycledViews(int viewType, int max) {
-            mMaxScrap.put(viewType, max);
-            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mMaxScrap = max;
+            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
             if (scrapHeap != null) {
                 while (scrapHeap.size() > max) {
                     scrapHeap.remove(scrapHeap.size() - 1);
@@ -4869,13 +4849,18 @@
             }
         }
 
+        /**
+         * Returns the current number of Views held by the RecycledViewPool of the given view type.
+         */
+        public int getRecycledViewCount(int viewType) {
+            return getScrapDataForType(viewType).mScrapHeap.size();
+        }
+
         public ViewHolder getRecycledView(int viewType) {
-            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
-            if (scrapHeap != null && !scrapHeap.isEmpty()) {
-                final int index = scrapHeap.size() - 1;
-                final ViewHolder scrap = scrapHeap.get(index);
-                scrapHeap.remove(index);
-                return scrap;
+            final ScrapData scrapData = mScrap.get(viewType);
+            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
+                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
+                return scrapHeap.remove(scrapHeap.size() - 1);
             }
             return null;
         }
@@ -4883,7 +4868,7 @@
         int size() {
             int count = 0;
             for (int i = 0; i < mScrap.size(); i ++) {
-                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
+                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
                 if (viewHolders != null) {
                     count += viewHolders.size();
                 }
@@ -4893,8 +4878,8 @@
 
         public void putRecycledView(ViewHolder scrap) {
             final int viewType = scrap.getItemViewType();
-            final ArrayList scrapHeap = getScrapHeapForType(viewType);
-            if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
+            final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
+            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
                 return;
             }
             if (DEBUG && scrapHeap.contains(scrap)) {
@@ -4904,6 +4889,35 @@
             scrapHeap.add(scrap);
         }
 
+        long runningAverage(long oldAverage, long newValue) {
+            if (oldAverage == 0) {
+                return newValue;
+            }
+            return (oldAverage / 8 * 7) + (newValue / 8);
+        }
+
+        void factorInCreateTime(int viewType, long createTimeNs) {
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mCreateRunningAverageNs = runningAverage(
+                    scrapData.mCreateRunningAverageNs, createTimeNs);
+        }
+
+        void factorInBindTime(int viewType, long bindTimeNs) {
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mBindRunningAverageNs = runningAverage(
+                    scrapData.mBindRunningAverageNs, bindTimeNs);
+        }
+
+        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
+            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
+            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
+        }
+
+        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
+            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
+            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
+        }
+
         void attach(Adapter adapter) {
             mAttachCount++;
         }
@@ -4937,16 +4951,28 @@
             }
         }
 
-        private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
-            ArrayList<ViewHolder> scrap = mScrap.get(viewType);
-            if (scrap == null) {
-                scrap = new ArrayList<>();
-                mScrap.put(viewType, scrap);
-                if (mMaxScrap.indexOfKey(viewType) < 0) {
-                    mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
-                }
+        private ScrapData getScrapDataForType(int viewType) {
+            ScrapData scrapData = mScrap.get(viewType);
+            if (scrapData == null) {
+                scrapData = new ScrapData();
+                mScrap.put(viewType, scrapData);
             }
-            return scrap;
+            return scrapData;
+        }
+    }
+
+
+    /**
+     * Time base for deadline-aware work scheduling. Overridable for testing.
+     *
+     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
+     * isn't relevant.
+     */
+    long getNanoTime() {
+        if (ALLOW_THREAD_GAP_WORK) {
+            return System.nanoTime();
+        } else {
+            return 0;
         }
     }
 
@@ -4975,7 +5001,7 @@
         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
         int mViewCacheMax = DEFAULT_CACHE_SIZE;
 
-        private RecycledViewPool mRecyclerPool;
+        RecycledViewPool mRecyclerPool;
 
         private ViewCacheExtension mViewCacheExtension;
 
@@ -5001,11 +5027,9 @@
         }
 
         void updateViewCacheSize() {
-            int extraCache = 0;
-            if (mLayout != null && ALLOW_PREFETCHING) {
-                extraCache = mLayout.isItemPrefetchEnabled() ? mLayout.getItemPrefetchCount() : 0;
-            }
+            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
             mViewCacheMax = mRequestedCacheMax + extraCache;
+
             // first, try the views that can be recycled
             for (int i = mCachedViews.size() - 1;
                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
@@ -5058,6 +5082,38 @@
         }
 
         /**
+         * Attempts to bind view, and account for relevant timing information. If
+         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
+         *
+         * @param holder Holder to be bound.
+         * @param offsetPosition Position of item to be bound.
+         * @param position Pre-layout position of item to be bound.
+         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
+         *                   complete. If FOREVER_NS is passed, this method will not fail to
+         *                   bind the holder.
+         * @return
+         */
+        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
+                int position, long deadlineNs) {
+            holder.mOwnerRecyclerView = RecyclerView.this;
+            final int viewType = holder.getItemViewType();
+            long startBindNs = getNanoTime();
+            if (deadlineNs != FOREVER_NS
+                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
+                // abort - we have a deadline we can't meet
+                return false;
+            }
+            mAdapter.bindViewHolder(holder, offsetPosition);
+            long endBindNs = getNanoTime();
+            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
+            attachAccessibilityDelegate(holder.itemView);
+            if (mState.isPreLayout()) {
+                holder.mPreLayoutPosition = position;
+            }
+            return true;
+        }
+
+        /**
          * Binds the given View to the position. The View can be a View previously retrieved via
          * {@link #getViewForPosition(int)} or created by
          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
@@ -5085,12 +5141,7 @@
                         + "position " + position + "(offset:" + offsetPosition + ")."
                         + "state:" + mState.getItemCount());
             }
-            holder.mOwnerRecyclerView = RecyclerView.this;
-            mAdapter.bindViewHolder(holder, offsetPosition);
-            attachAccessibilityDelegate(view);
-            if (mState.isPreLayout()) {
-                holder.mPreLayoutPosition = position;
-            }
+            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
 
             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
             final LayoutParams rvLayoutParams;
@@ -5157,23 +5208,47 @@
         }
 
         View getViewForPosition(int position, boolean dryRun) {
+            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
+        }
+
+        /**
+         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
+         * cache, the RecycledViewPool, or creating it directly.
+         * <p>
+         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
+         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
+         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
+         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
+         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
+         *
+         * @param position Position of ViewHolder to be returned.
+         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
+         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
+         *                   complete. If FOREVER_NS is passed, this method will not fail to
+         *                   create/bind the holder if needed.
+         *
+         * @return ViewHolder for requested position
+         */
+        @Nullable
+        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
+                boolean dryRun, long deadlineNs) {
             if (position < 0 || position >= mState.getItemCount()) {
                 throw new IndexOutOfBoundsException("Invalid item position " + position
                         + "(" + position + "). Item count:" + mState.getItemCount());
             }
-            boolean fromScrap = false;
+            boolean fromScrapOrHiddenOrCache = false;
             ViewHolder holder = null;
             // 0) If there is a changed scrap, try to find from there
             if (mState.isPreLayout()) {
                 holder = getChangedScrapViewForPosition(position);
-                fromScrap = holder != null;
+                fromScrapOrHiddenOrCache = holder != null;
             }
-            // 1) Find from scrap by position
+            // 1) Find by position from scrap/hidden list/cache
             if (holder == null) {
-                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
+                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
                 if (holder != null) {
                     if (!validateViewHolderForOffsetPosition(holder)) {
-                        // recycle this scrap
+                        // recycle holder (and unscrap if relevant) since it can't be used
                         if (!dryRun) {
                             // we would like to recycle this but need to make sure it is not used by
                             // animation logic etc.
@@ -5188,7 +5263,7 @@
                         }
                         holder = null;
                     } else {
-                        fromScrap = true;
+                        fromScrapOrHiddenOrCache = true;
                     }
                 }
             }
@@ -5201,13 +5276,14 @@
                 }
 
                 final int type = mAdapter.getItemViewType(offsetPosition);
-                // 2) Find from scrap via stable ids, if exists
+                // 2) Find from scrap/cache via stable ids, if exists
                 if (mAdapter.hasStableIds()) {
-                    holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
+                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
+                            type, dryRun);
                     if (holder != null) {
                         // update position
                         holder.mPosition = offsetPosition;
-                        fromScrap = true;
+                        fromScrapOrHiddenOrCache = true;
                     }
                 }
                 if (holder == null && mViewCacheExtension != null) {
@@ -5227,12 +5303,10 @@
                         }
                     }
                 }
-                if (holder == null) { // fallback to recycler
-                    // try recycler.
-                    // Head to the shared pool.
+                if (holder == null) { // fallback to pool
                     if (DEBUG) {
-                        Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
-                                + "pool");
+                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
+                                + position + ") fetching from shared pool");
                     }
                     holder = getRecycledViewPool().getRecycledView(type);
                     if (holder != null) {
@@ -5243,9 +5317,17 @@
                     }
                 }
                 if (holder == null) {
+                    long start = getNanoTime();
+                    if (deadlineNs != FOREVER_NS
+                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
+                        // abort - we have a deadline we can't meet
+                        return null;
+                    }
                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
+                    long end = getNanoTime();
+                    mRecyclerPool.factorInCreateTime(type, end - start);
                     if (DEBUG) {
-                        Log.d(TAG, "getViewForPosition created new ViewHolder");
+                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
                     }
                 }
             }
@@ -5253,7 +5335,7 @@
             // This is very ugly but the only place we can grab this information
             // before the View is rebound and returned to the LayoutManager for post layout ops.
             // We don't need this in pre-layout since the VH is not updated by the LM.
-            if (fromScrap && !mState.isPreLayout() && holder
+            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
                 if (mState.mRunSimpleAnimations) {
@@ -5276,13 +5358,7 @@
                             + " come here only in pre-layout. Holder: " + holder);
                 }
                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
-                holder.mOwnerRecyclerView = RecyclerView.this;
-                mAdapter.bindViewHolder(holder, offsetPosition);
-                attachAccessibilityDelegate(holder.itemView);
-                bound = true;
-                if (mState.isPreLayout()) {
-                    holder.mPreLayoutPosition = position;
-                }
+                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
             }
 
             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
@@ -5297,8 +5373,8 @@
                 rvLayoutParams = (LayoutParams) lp;
             }
             rvLayoutParams.mViewHolder = holder;
-            rvLayoutParams.mPendingInvalidate = fromScrap && bound;
-            return holder.itemView;
+            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
+            return holder;
         }
 
         private void attachAccessibilityDelegate(View itemView) {
@@ -5382,8 +5458,8 @@
                 recycleCachedViewAt(i);
             }
             mCachedViews.clear();
-            if (ALLOW_PREFETCHING) {
-                mViewPrefetcher.clearPrefetchPositions();
+            if (ALLOW_THREAD_GAP_WORK) {
+                mPrefetchRegistry.clearPrefetchPositions();
             }
         }
 
@@ -5456,14 +5532,14 @@
                     }
 
                     int targetCacheIndex = cachedViewSize;
-                    if (ALLOW_PREFETCHING
+                    if (ALLOW_THREAD_GAP_WORK
                             && cachedViewSize > 0
-                            && !mViewPrefetcher.lastPrefetchIncludedPosition(holder.mPosition)) {
+                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
                         // when adding the view, skip past most recently prefetched views
                         int cacheIndex = cachedViewSize - 1;
                         while (cacheIndex >= 0) {
                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
-                            if (!mViewPrefetcher.lastPrefetchIncludedPosition(cachedPos)) {
+                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
                                 break;
                             }
                             cacheIndex--;
@@ -5602,15 +5678,13 @@
         }
 
         /**
-         * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
-         * ViewHolder's type matches the provided type.
+         * Returns a view for the position either from attach scrap, hidden children, or cache.
          *
          * @param position Item position
-         * @param type View type
          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
          * @return a ViewHolder that can be re-used for this position.
          */
-        ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
+        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
             final int scrapCount = mAttachedScrap.size();
 
             // Try first for an exact, non-invalid match from scrap.
@@ -5618,19 +5692,13 @@
                 final ViewHolder holder = mAttachedScrap.get(i);
                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
-                    if (type != INVALID_TYPE && holder.getItemViewType() != type) {
-                        Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
-                                " wrong view type! (found " + holder.getItemViewType() +
-                                " but expected " + type + ")");
-                        break;
-                    }
                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                     return holder;
                 }
             }
 
             if (!dryRun) {
-                View view = mChildHelper.findHiddenNonRemovedView(position, type);
+                View view = mChildHelper.findHiddenNonRemovedView(position);
                 if (view != null) {
                     // This View is good to be used. We just need to unhide, detach and move to the
                     // scrap list.
@@ -5654,14 +5722,14 @@
             for (int i = 0; i < cacheSize; i++) {
                 final ViewHolder holder = mCachedViews.get(i);
                 // invalid view holders may be in cache if adapter has stable ids as they can be
-                // retrieved via getScrapViewForId
+                // retrieved via getScrapOrCachedViewForId
                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
                     if (!dryRun) {
                         mCachedViews.remove(i);
                     }
                     if (DEBUG) {
-                        Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
-                                ") found match in cache: " + holder);
+                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
+                                + ") found match in cache: " + holder);
                     }
                     return holder;
                 }
@@ -5669,7 +5737,7 @@
             return null;
         }
 
-        ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
+        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
             // Look in our attached views first
             final int count = mAttachedScrap.size();
             for (int i = count - 1; i >= 0; i--) {
@@ -5715,6 +5783,7 @@
                         return holder;
                     } else if (!dryRun) {
                         recycleCachedViewAt(i);
+                        return null;
                     }
                 }
             }
@@ -5905,42 +5974,6 @@
                 }
             }
         }
-
-        boolean isPrefetchPositionAttached(int position) {
-            final int childCount = mChildHelper.getUnfilteredChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View attachedView = mChildHelper.getUnfilteredChildAt(i);
-                ViewHolder holder = getChildViewHolderInt(attachedView);
-                // TODO: consider ignoring if holder isInvalid
-                // Note: can use mPosition here because adapter doesn't have pending updates
-                if (holder.mPosition == position) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        void prefetch(int[] itemPrefetchArray, int viewCount) {
-            if (viewCount == 0) return;
-
-            int childPosition = itemPrefetchArray[viewCount - 1];
-            if (childPosition < 0) {
-                throw new IllegalArgumentException("Recycler requested to prefetch invalid view "
-                        + childPosition);
-            }
-
-            View prefetchView = null;
-            if (!isPrefetchPositionAttached(childPosition)) {
-                // only prefetch if child not already attached
-                prefetchView = getViewForPosition(childPosition);
-            }
-            if (viewCount > 1) {
-                prefetch(itemPrefetchArray, viewCount - 1);
-            }
-            if (prefetchView != null) {
-                recycleView(prefetchView);
-            }
-        }
     }
 
     /**
@@ -6589,6 +6622,12 @@
         private boolean mItemPrefetchEnabled = true;
 
         /**
+         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
+         * requested by a {@link #collectPrefetchPositions(int, int, State, PrefetchRegistry)} call.
+         */
+        int mPrefetchMaxCountObserved;
+
+        /**
          * These measure specs might be the measure specs that were passed into RecyclerView's
          * onMeasure method OR fake measure specs created by the RecyclerView.
          * For example, when a layout is run, RecyclerView always sets these specs to be
@@ -6890,6 +6929,7 @@
         public final void setItemPrefetchEnabled(boolean enabled) {
             if (enabled != mItemPrefetchEnabled) {
                 mItemPrefetchEnabled = enabled;
+                mPrefetchMaxCountObserved = 0;
                 if (mRecyclerView != null) {
                     mRecyclerView.mRecycler.updateViewCacheSize();
                 }
@@ -6908,11 +6948,28 @@
             return mItemPrefetchEnabled;
         }
 
-        int getItemPrefetchCount() { return 0; }
-
-        int gatherPrefetchIndices(int dx, int dy, State state, int[] outIndices) {
-            return 0;
-        }
+        /**
+         * Gather all positions from the LayoutManager to be prefetched.
+         *
+         * <p>If item prefetch is enabled, this method is called in between traversals to gather
+         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
+         * traversals.</p>
+         *
+         * <p>The LayoutManager should call {@link PrefetchRegistry#addPosition(int, int)} for each
+         * item to be prepared, and these positions will have their ViewHolders created and bound
+         * in advance of being needed by a scroll or layout.</p>
+         *
+         * @param dx X movement component.
+         * @param dy Y movement component.
+         * @param state State of RecyclerView
+         * @param prefetchRegistry PrefetchRegistry to add prefetch entries into.
+         *
+         * @see #isItemPrefetchEnabled()
+         *
+         * @hide
+         */
+        public void collectPrefetchPositions(int dx, int dy, State state,
+                PrefetchRegistry prefetchRegistry) {}
 
         void dispatchAttachedToWindow(RecyclerView view) {
             mIsAttachedToWindow = true;
@@ -8223,11 +8280,11 @@
             int size = Math.max(0, parentSize - padding);
             int resultSize = 0;
             int resultMode = 0;
-            if (childDimension >= 0) {
-                resultSize = childDimension;
-                resultMode = MeasureSpec.EXACTLY;
-            } else if (canScroll) {
-                 if (childDimension == LayoutParams.MATCH_PARENT){
+            if (canScroll) {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                     switch (parentMode) {
                         case MeasureSpec.AT_MOST:
                         case MeasureSpec.EXACTLY:
@@ -8244,7 +8301,10 @@
                     resultMode = MeasureSpec.UNSPECIFIED;
                 }
             } else {
-                if (childDimension == LayoutParams.MATCH_PARENT) {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                     resultSize = size;
                     resultMode = parentMode;
                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
@@ -9336,6 +9396,24 @@
     }
 
     /**
+     * Interface for LayoutManagers to request items to be prefetched, based on position, with
+     * specified distance from viewport, which indicates priority.
+     *
+     * @hide
+     */
+    public interface PrefetchRegistry {
+        /**
+         * Requests an an item to be prefetched, based on position, with a specified distance,
+         * indicating priority.
+         *
+         * @param layoutPosition Position of the item to prefetch.
+         * @param pixelDistance Distance from the current viewport to the bounds of the item,
+         *                      must be non-negative.
+         */
+        void addPosition(int layoutPosition, int pixelDistance);
+    }
+
+    /**
      * An ItemDecoration allows the application to add a special drawing and layout offset
      * to specific item views from the adapter's data set. This can be useful for drawing dividers
      * between items, highlights, visual grouping boundaries and more.
@@ -10033,6 +10111,8 @@
          *
          * @param recyclable Whether this item is available to be recycled. Default value
          * is true.
+         *
+         * @see #isRecyclable()
          */
         public final void setIsRecyclable(boolean recyclable) {
             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
@@ -10055,9 +10135,9 @@
         }
 
         /**
-         * @see {@link #setIsRecyclable(boolean)}
-         *
          * @return true if this item is available to be recycled, false otherwise.
+         *
+         * @see #setIsRecyclable(boolean)
          */
         public final boolean isRecyclable() {
             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
diff --git a/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java b/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
index 37197e4..d63045b 100644
--- a/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/SnapHelper.java
@@ -19,11 +19,11 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider;
 import android.util.DisplayMetrics;
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.Scroller;
-import android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider;
 
 /**
  * Class intended to support snapping for a {@link RecyclerView}.
@@ -206,7 +206,7 @@
      * @return a {@link LinearSmoothScroller} which will handle the scrolling.
      */
     @Nullable
-    private LinearSmoothScroller createSnapScroller(LayoutManager layoutManager) {
+    protected LinearSmoothScroller createSnapScroller(LayoutManager layoutManager) {
         if (!(layoutManager instanceof ScrollVectorProvider)) {
             return null;
         }
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index eba10ca..7040bcc 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -212,6 +212,12 @@
      */
     private boolean mSmoothScrollbarEnabled = true;
 
+    /**
+     * Temporary array used (solely in {@link #collectPrefetchPositions}) for stashing and sorting
+     * distances to views being prefetched.
+     */
+    private int[] mPrefetchDistances;
+
     private final Runnable mCheckForGapsRunnable = new Runnable() {
         @Override
         public void run() {
@@ -2065,29 +2071,44 @@
         requestLayout();
     }
 
+    /** @hide */
     @Override
-    int getItemPrefetchCount() {
-        return mSpanCount;
-    }
-
-    @Override
-    int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+    public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+            RecyclerView.PrefetchRegistry prefetchRegistry) {
+        /* This method uses the simplifying assumption that the next N items (where N = span count)
+         * will be assigned, one-to-one, to spans, where ordering is based on which span  extends
+         * least beyond the viewport.
+         *
+         * While this simplified model will be incorrect in some cases, it's difficult to know
+         * item heights, or whether individual items will be full span prior to construction.
+         *
+         * While this greedy estimation approach may underestimate the distance to prefetch items,
+         * it's very unlikely to overestimate them, so distances can be conservatively used to know
+         * the soonest (in terms of scroll distance) a prefetched view may come on screen.
+         */
         int delta = (mOrientation == HORIZONTAL) ? dx : dy;
         if (getChildCount() == 0 || delta == 0) {
             // can't support this scroll, so don't bother prefetching
-            return 0;
+            return;
         }
         prepareLayoutStateForDelta(delta, state);
-        int remainingSpan = mSpanCount;
-        int count = 0;
-        while (count < mSpanCount && mLayoutState.hasMore(state) && remainingSpan > 0) {
-            final int pos = mLayoutState.mCurrentPosition;
-            outIndices[count] = pos;
-            remainingSpan--;
-            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
-            count++;
+
+        // build sorted list of distances to end of each span (though we don't care which is which)
+        if (mPrefetchDistances == null || mPrefetchDistances.length < mSpanCount) {
+            mPrefetchDistances = new int[mSpanCount];
         }
-        return count;
+        for (int i = 0; i < mSpanCount; i++) {
+            mPrefetchDistances[i] = mLayoutState.mItemDirection == LAYOUT_START
+                    ? mLayoutState.mStartLine - mSpans[i].getStartLine(mLayoutState.mStartLine)
+                    : mSpans[i].getEndLine(mLayoutState.mEndLine) - mLayoutState.mEndLine;
+        }
+        Arrays.sort(mPrefetchDistances, 0, mSpanCount);
+
+        // then assign them in order to the next N views (where N = span count)
+        for (int i = 0; i < mSpanCount && mLayoutState.hasMore(state); i++) {
+            prefetchRegistry.addPosition(mLayoutState.mCurrentPosition, mPrefetchDistances[i]);
+            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
+        }
     }
 
     void prepareLayoutStateForDelta(int delta, RecyclerView.State state) {
@@ -2104,8 +2125,7 @@
         updateLayoutState(referenceChildPosition, state);
         setLayoutStateDirection(layoutDir);
         mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
-        final int absDt = Math.abs(delta);
-        mLayoutState.mAvailable = absDt;
+        mLayoutState.mAvailable = Math.abs(delta);
     }
 
     int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
@@ -2136,12 +2156,12 @@
         return totalScroll;
     }
 
-    private int getLastChildPosition() {
+    int getLastChildPosition() {
         final int childCount = getChildCount();
         return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1));
     }
 
-    private int getFirstChildPosition() {
+    int getFirstChildPosition() {
         final int childCount = getChildCount();
         return childCount == 0 ? 0 : getPosition(getChildAt(0));
     }
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
index 11fd3ed..1bb4fc3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
@@ -16,23 +16,31 @@
 
 package android.support.v7.util;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.support.annotation.UiThread;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v7.widget.TestActivity;
+import android.util.SparseBooleanArray;
+
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import android.support.annotation.UiThread;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.SparseBooleanArray;
-
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.*;
 
 @MediumTest
 @RunWith(JUnit4.class)
-public class AsyncListUtilTest extends BaseThreadedTest {
+public class AsyncListUtilTest {
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
 
     private static final int TILE_SIZE = 10;
 
@@ -42,21 +50,20 @@
     AsyncListUtil<String> mAsyncListUtil;
 
     @Before
-    public final void setupCallbacks() throws Exception {
+    public final void setup() throws Throwable {
         mDataCallback = new TestDataCallback();
         mViewCallback = new TestViewCallback();
         mDataCallback.expectTiles(0, 10, 20);
-        super.setUp();
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mAsyncListUtil = new AsyncListUtil<>(
+                        String.class, TILE_SIZE, mDataCallback, mViewCallback);
+            }
+        });
         mDataCallback.waitForTiles("initial load");
     }
 
-    @Override
-    @UiThread
-    protected void setUpUi() {
-        mAsyncListUtil = new AsyncListUtil<String>(
-                String.class, TILE_SIZE, mDataCallback, mViewCallback);
-    }
-
     @After
     public void tearDown() throws Exception {
         /// Wait a little extra to catch spurious messages.
@@ -83,7 +90,8 @@
         scrollAndExpectTiles(40, "scroll up a little, no new tiles loaded");
     }
 
-    @Test
+    // This test is disabled as it is flaky.
+    // @Test
     public void tileCaching() throws Throwable {
         scrollAndExpectTiles(25, "next screen", 30, 40);
 
@@ -148,7 +156,7 @@
     }
 
     private void refreshOnUiThread() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAsyncListUtil.refresh();
@@ -160,7 +168,7 @@
                                              final int expectedCount,
                                              final int position,
                                              final int count) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertEquals(message, expectedCount, getLoadedItemCount(position, count));
@@ -169,7 +177,7 @@
     }
 
     private void scrollOnUiThread(final int position) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mViewCallback.scrollTo(position);
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/BaseThreadedTest.java b/v7/recyclerview/tests/src/android/support/v7/util/BaseThreadedTest.java
deleted file mode 100644
index deb6054..0000000
--- a/v7/recyclerview/tests/src/android/support/v7/util/BaseThreadedTest.java
+++ /dev/null
@@ -1,69 +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.v7.util;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-
-import android.app.Instrumentation;
-import android.support.annotation.UiThread;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v7.widget.TestActivity;
-import android.test.ActivityInstrumentationTestCase2;
-
-abstract public class BaseThreadedTest {
-    @Rule
-    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
-
-    public final void setUp() throws Exception{
-        try {
-            getInstrumentation().runOnMainSync(new Runnable() {
-                @Override
-                public void run() {
-                    setUpUi();
-                }
-            });
-        } catch (Throwable throwable) {
-            Assert.fail(throwable.getMessage());
-        }
-    }
-
-    public Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
-    public void runTestOnUiThread(final Runnable test) {
-        final Throwable[] error = new Throwable[1];
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    test.run();
-                } catch (Throwable t) {
-                    error[0] = t;
-                }
-            }
-        });
-        Assert.assertNull(error[0]);
-    }
-
-    @UiThread
-    protected abstract void setUpUi();
-}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java b/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
index 9aa9d11..2387fb3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
@@ -16,18 +16,18 @@
 
 package android.support.v7.util;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MessageQueueTest {
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java b/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java
new file mode 100644
index 0000000..796a4ae
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/util/PollingCheck.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.util;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.Callable;
+
+public abstract class PollingCheck {
+    private static final long TIME_SLICE = 50;
+    private long mTimeout = 3000;
+
+    public interface PollingCheckCondition {
+        boolean canProceed();
+    }
+
+    public PollingCheck() {
+    }
+
+    public PollingCheck(long timeout) {
+        mTimeout = timeout;
+    }
+
+    protected abstract boolean check();
+
+    public void run() {
+        if (check()) {
+            return;
+        }
+
+        long timeout = mTimeout;
+        while (timeout > 0) {
+            try {
+                Thread.sleep(TIME_SLICE);
+            } catch (InterruptedException e) {
+                Assert.fail("unexpected InterruptedException");
+            }
+
+            if (check()) {
+                return;
+            }
+
+            timeout -= TIME_SLICE;
+        }
+
+        Assert.fail("unexpected timeout");
+    }
+
+    public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
+            throws Exception {
+        while (timeout > 0) {
+            if (condition.call()) {
+                return;
+            }
+
+            Thread.sleep(TIME_SLICE);
+            timeout -= TIME_SLICE;
+        }
+
+        Assert.fail(message.toString());
+    }
+
+    public static void waitFor(final PollingCheckCondition condition) {
+        new PollingCheck() {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+        }.run();
+    }
+
+    public static void waitFor(long timeout, final PollingCheckCondition condition) {
+        new PollingCheck(timeout) {
+            @Override
+            protected boolean check() {
+                return condition.canProceed();
+            }
+        }.run();
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
index 8091f29..08b367b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
@@ -16,87 +16,98 @@
 
 package android.support.v7.util;
 
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.MatcherAssert.*;
-import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.os.Looper;
-import android.support.annotation.UiThread;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.TestActivity;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
+@RunWith(AndroidJUnit4.class)
 @MediumTest
-public class ThreadUtilTest extends BaseThreadedTest {
+public class ThreadUtilTest {
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
+
     Map<String, LockedObject> results = new HashMap<>();
 
     ThreadUtil.MainThreadCallback<Integer> mMainThreadProxy;
     ThreadUtil.BackgroundCallback<Integer> mBackgroundProxy;
 
     @Before
-    public void init() throws Exception {
-        super.setUp();
-    }
+    public void setup() throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ThreadUtil<Integer> threadUtil = new MessageThreadUtil<>();
 
-    @Override
-    @UiThread
-    public void setUpUi() {
-        ThreadUtil<Integer> threadUtil = new MessageThreadUtil<>();
+                mMainThreadProxy = threadUtil.getMainThreadProxy(
+                        new ThreadUtil.MainThreadCallback<Integer>() {
+                            @Override
+                            public void updateItemCount(int generation, int itemCount) {
+                                assertMainThread();
+                                setResultData("updateItemCount", generation, itemCount);
+                            }
 
-        mMainThreadProxy = threadUtil.getMainThreadProxy(
-                new ThreadUtil.MainThreadCallback<Integer>() {
-                    @Override
-                    public void updateItemCount(int generation, int itemCount) {
-                        assertMainThread();
-                        setResultData("updateItemCount", generation, itemCount);
-                    }
+                            @Override
+                            public void addTile(int generation, TileList.Tile<Integer> data) {
+                                assertMainThread();
+                                setResultData("addTile", generation, data);
+                            }
 
-                    @Override
-                    public void addTile(int generation, TileList.Tile<Integer> data) {
-                        assertMainThread();
-                        setResultData("addTile", generation, data);
-                    }
+                            @Override
+                            public void removeTile(int generation, int position) {
+                                assertMainThread();
+                                setResultData("removeTile", generation, position);
+                            }
+                        });
 
-                    @Override
-                    public void removeTile(int generation, int position) {
-                        assertMainThread();
-                        setResultData("removeTile", generation, position);
-                    }
-                });
+                mBackgroundProxy = threadUtil.getBackgroundProxy(
+                        new ThreadUtil.BackgroundCallback<Integer>() {
+                            @Override
+                            public void refresh(int generation) {
+                                assertBackgroundThread();
+                                setResultData("refresh", generation);
+                            }
 
-        mBackgroundProxy = threadUtil.getBackgroundProxy(
-                new ThreadUtil.BackgroundCallback<Integer>() {
-                    @Override
-                    public void refresh(int generation) {
-                        assertBackgroundThread();
-                        setResultData("refresh", generation);
-                    }
+                            @Override
+                            public void updateRange(int rangeStart, int rangeEnd, int extRangeStart,
+                                    int extRangeEnd, int scrollHint) {
+                                assertBackgroundThread();
+                                setResultData("updateRange", rangeStart, rangeEnd,
+                                        extRangeStart, extRangeEnd, scrollHint);
+                            }
 
-                    @Override
-                    public void updateRange(int rangeStart, int rangeEnd, int extRangeStart,
-                                            int extRangeEnd, int scrollHint) {
-                        assertBackgroundThread();
-                        setResultData("updateRange", rangeStart, rangeEnd,
-                                extRangeStart, extRangeEnd, scrollHint);
-                    }
+                            @Override
+                            public void loadTile(int position, int scrollHint) {
+                                assertBackgroundThread();
+                                setResultData("loadTile", position, scrollHint);
+                            }
 
-                    @Override
-                    public void loadTile(int position, int scrollHint) {
-                        assertBackgroundThread();
-                        setResultData("loadTile", position, scrollHint);
-                    }
-
-                    @Override
-                    public void recycleTile(TileList.Tile<Integer> data) {
-                        assertBackgroundThread();
-                        setResultData("recycleTile", data);
-                    }
-                });
+                            @Override
+                            public void recycleTile(TileList.Tile<Integer> data) {
+                                assertBackgroundThread();
+                                setResultData("recycleTile", data);
+                            }
+                        });
+            }
+        });
     }
 
     @Test
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java b/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
index 308d01b..a2ed04c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
@@ -17,12 +17,15 @@
 
 package android.support.v7.util;
 
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.util.TileList;
-import android.test.suitebuilder.annotation.SmallTest;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
 
-import static org.hamcrest.MatcherAssert.*;
-import static org.hamcrest.CoreMatchers.*;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
index 3806168..0a28c2b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
@@ -16,25 +16,27 @@
 
 package android.support.v7.widget;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.content.Context;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.util.AsyncListUtil;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.content.Context;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.util.AsyncListUtil;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
 import java.util.BitSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.*;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -89,7 +91,7 @@
         mDataCallback.expectTilesInRange(rangeStart, rangeSize);
         mAdapter.expectItemsInRange(rangeStart, rangeSize);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAsyncListUtil = new AsyncListUtil<>(
@@ -149,7 +151,7 @@
     }
 
     void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.scrollToPositionWithOffset(position, offset);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
index 9109c87..8d93553 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
@@ -15,10 +15,19 @@
  */
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static org.junit.Assert.assertEquals;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
-import android.view.ViewGroup;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -27,14 +36,6 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static org.junit.Assert.assertEquals;
-
-import org.hamcrest.CoreMatchers;
-import org.hamcrest.MatcherAssert;
-
 public class BaseGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
 
     static final String TAG = "GridLayoutManagerTest";
@@ -128,12 +129,12 @@
 
         @Override
         public String toString() {
-            return "Config{" +
-                    "mSpanCount=" + mSpanCount +
-                    ", mOrientation=" + (mOrientation == GridLayoutManager.HORIZONTAL ? "h" : "v") +
-                    ", mItemCount=" + mItemCount +
-                    ", mReverseLayout=" + mReverseLayout +
-                    '}';
+            return "Config{"
+                    + "mSpanCount=" + mSpanCount
+                    + ",mOrientation=" + (mOrientation == GridLayoutManager.HORIZONTAL ? "h" : "v")
+                    + ",mItemCount=" + mItemCount
+                    + ",mReverseLayout=" + mReverseLayout
+                    + '}';
         }
 
         public Config reverseLayout(boolean reverseLayout) {
@@ -317,9 +318,10 @@
         }
 
         @Override
-        int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            return super.gatherPrefetchIndices(dx, dy, state, outIndices);
+            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
         }
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
index 54190f1..9bea02c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -149,7 +148,7 @@
             }
         };
         mLayoutManager.expectLayouts(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -178,7 +177,7 @@
     }
 
     void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.scrollToPositionWithOffset(position, offset);
@@ -336,14 +335,14 @@
 
         @Override
         public String toString() {
-            return "Config{" +
-                    "mStackFromEnd=" + mStackFromEnd +
-                    ", mOrientation=" + mOrientation +
-                    ", mReverseLayout=" + mReverseLayout +
-                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
-                    ", mItemCount=" + mItemCount +
-                    ", wrap=" + mWrap +
-                    '}';
+            return "Config{"
+                    + "mStackFromEnd=" + mStackFromEnd
+                    + ",mOrientation=" + mOrientation
+                    + ",mReverseLayout=" + mReverseLayout
+                    + ",mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach
+                    + ",mItemCount=" + mItemCount
+                    + ",wrap=" + mWrap
+                    + '}';
         }
 
         public Config wrap(boolean wrap) {
@@ -598,7 +597,7 @@
 
         Map<Item, Rect> collectChildCoordinates() throws Throwable {
             final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     final int childCount = getChildCount();
@@ -636,9 +635,10 @@
         }
 
         @Override
-        int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            return super.gatherPrefetchIndices(dx, dy, state, outIndices);
+            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
         }
     }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewAnimationsTest.java
index 57c09fa..3c9fd4d 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewAnimationsTest.java
@@ -16,6 +16,11 @@
 package android.support.v7.widget;
 
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
@@ -28,7 +33,6 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.*;
 
 /**
  * Base class for animation related tests.
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 0373251..17c4d71 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -18,11 +18,16 @@
 
 import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
 
-import org.hamcrest.CoreMatchers;
-import org.hamcrest.MatcherAssert;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.app.Instrumentation;
 import android.graphics.Rect;
@@ -31,6 +36,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.recyclerview.test.R;
 import android.support.v7.recyclerview.test.SameActivityTestRule;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -39,6 +45,12 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -49,12 +61,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-import android.support.v7.recyclerview.test.R;
-
-import static org.junit.Assert.*;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
 
 abstract public class BaseRecyclerViewInstrumentationTest {
 
@@ -114,7 +120,7 @@
 
     void setHasTransientState(final View view, final boolean value) {
         try {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     ViewCompat.setHasTransientState(view, value);
@@ -136,7 +142,7 @@
     }
 
     void setAdapter(final RecyclerView.Adapter adapter) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setAdapter(adapter);
@@ -147,7 +153,7 @@
     public View focusSearch(final View focused, final int direction)
             throws Throwable {
         final View[] result = new View[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 View view = focused.focusSearch(direction);
@@ -168,7 +174,7 @@
 
     void swapAdapter(final RecyclerView.Adapter adapter,
             final boolean removeAndRecycleExistingViews) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -244,7 +250,7 @@
         if (!isMainThread()) {
             getInstrumentation().waitForIdleSync();
         }
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -267,7 +273,7 @@
 
     void waitForAnimations(int seconds) throws Throwable {
         final CountDownLatch latch = new CountDownLatch(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.mItemAnimator
@@ -286,7 +292,7 @@
 
     public void waitForIdleScroll(final RecyclerView recyclerView) throws Throwable {
         final CountDownLatch latch = new CountDownLatch(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() {
@@ -310,16 +316,12 @@
 
     public boolean requestFocus(final View view, boolean waitForScroll) throws Throwable {
         final boolean[] result = new boolean[1];
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    result[0] = view.requestFocus();
-                }
-            });
-        } catch (Throwable throwable) {
-            fail(throwable.getMessage());
-        }
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                result[0] = view.requestFocus();
+            }
+        });
         if (waitForScroll && result[0]) {
             waitForIdleScroll(mRecyclerView);
         }
@@ -375,7 +377,7 @@
             });
         }
         mAdapterHelper = recyclerView.mAdapterHelper;
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getActivity().getContainer().addView(recyclerView);
@@ -387,58 +389,46 @@
         return getActivity().getContainer();
     }
 
-    public void requestLayoutOnUIThread(final View view) {
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    view.requestLayout();
-                }
-            });
-        } catch (Throwable throwable) {
-            Log.e(TAG, "", throwable);
-        }
+    protected void requestLayoutOnUIThread(final View view) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                view.requestLayout();
+            }
+        });
     }
 
-    public void scrollBy(final int dt) {
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
-                        mRecyclerView.scrollBy(dt, 0);
-                    } else {
-                        mRecyclerView.scrollBy(0, dt);
-                    }
-
+    protected void scrollBy(final int dt) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
+                    mRecyclerView.scrollBy(dt, 0);
+                } else {
+                    mRecyclerView.scrollBy(0, dt);
                 }
-            });
-        } catch (Throwable throwable) {
-            Log.e(TAG, "", throwable);
-        }
+
+            }
+        });
     }
 
-    public void smoothScrollBy(final int dt) {
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
-                        mRecyclerView.smoothScrollBy(dt, 0);
-                    } else {
-                        mRecyclerView.smoothScrollBy(0, dt);
-                    }
-
+    protected void smoothScrollBy(final int dt) throws Throwable {
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
+                    mRecyclerView.smoothScrollBy(dt, 0);
+                } else {
+                    mRecyclerView.smoothScrollBy(0, dt);
                 }
-            });
-        } catch (Throwable throwable) {
-            Log.e(TAG, "", throwable);
-        }
+
+            }
+        });
         getInstrumentation().waitForIdleSync();
     }
 
     void scrollToPosition(final int position) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.getLayoutManager().scrollToPosition(position);
@@ -468,7 +458,7 @@
                     }
                 };
         final AtomicBoolean addedListener = new AtomicBoolean(false);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 RecyclerView.ViewHolder viewHolderForAdapterPosition =
@@ -482,7 +472,7 @@
 
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollToPosition(position);
@@ -497,7 +487,7 @@
             Log.d(TAG, "SMOOTH scrolling done");
         }
         if (addedListener.get()) {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mRecyclerView.removeOnChildAttachStateChangeListener(listener);
@@ -508,7 +498,7 @@
     }
 
     void freezeLayout(final boolean freeze) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setLayoutFrozen(freeze);
@@ -517,7 +507,7 @@
     }
 
     public void setVisibility(final View view, final int visibility) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 view.setVisibility(visibility);
@@ -895,7 +885,7 @@
             for (int[] tuple : startCountTuples) {
                 tuple[1] = -tuple[1];
             }
-            new AddRemoveRunnable(startCountTuples).runOnMainThread();
+            mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples));
         }
 
         @Override
@@ -922,7 +912,8 @@
 
         public void addAndNotify(final int count) throws Throwable {
             assertEquals(0, mItems.size());
-            new AddRemoveRunnable(DEFAULT_ITEM_PREFIX, new int[]{0, count}).runOnMainThread();
+            mActivityRule.runOnUiThread(
+                    new AddRemoveRunnable(DEFAULT_ITEM_PREFIX, new int[]{0, count}));
         }
 
         public void resetItemsTo(final List<Item> testItems) throws Throwable {
@@ -930,7 +921,7 @@
                 deleteAndNotify(0, mItems.size());
             }
             mItems = testItems;
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     notifyItemRangeInserted(0, testItems.size());
@@ -943,11 +934,11 @@
         }
 
         public void addAndNotify(final int[]... startCountTuples) throws Throwable {
-            new AddRemoveRunnable(startCountTuples).runOnMainThread();
+            mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples));
         }
 
         public void dispatchDataSetChanged() throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     notifyDataSetChanged();
@@ -956,7 +947,7 @@
         }
 
         public void changeAndNotify(final int start, final int count) throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     notifyItemRangeChanged(start, count);
@@ -966,7 +957,7 @@
 
         public void changeAndNotifyWithPayload(final int start, final int count,
                 final Object payload) throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     notifyItemRangeChanged(start, count, payload);
@@ -975,7 +966,7 @@
         }
 
         public void changePositionsAndNotify(final int... positions) throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     for (int i = 0; i < positions.length; i += 1) {
@@ -992,7 +983,7 @@
          * item to index 1, then remove an item from index 2 (updated index 2)
          */
         public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable {
-            new AddRemoveRunnable(startCountTuples).runOnMainThread();
+            mActivityRule.runOnUiThread(new AddRemoveRunnable(startCountTuples));
         }
 
         @Override
@@ -1018,7 +1009,7 @@
          */
         public void moveItem(final int from, final int to, final boolean notifyChange)
                 throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     moveInUIThread(from, to);
@@ -1033,7 +1024,7 @@
          * Uses notifyItemMoved
          */
         public void moveAndNotify(final int from, final int to) throws Throwable {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     moveInUIThread(from, to);
@@ -1076,14 +1067,6 @@
                 this("new item ", startCountTuples);
             }
 
-            public void runOnMainThread() throws Throwable {
-                if (Looper.myLooper() == Looper.getMainLooper()) {
-                    run();
-                } else {
-                    runTestOnUiThread(this);
-                }
-            }
-
             @Override
             public void run() {
                 for (int[] tuple : mStartCountTuples) {
@@ -1117,24 +1100,6 @@
         return Looper.myLooper() == Looper.getMainLooper();
     }
 
-    public void runTestOnUiThread(final Runnable r) throws Throwable {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            r.run();
-        } else {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        r.run();
-                    } catch (Throwable t) {
-                        postExceptionToInstrumentation(t);
-                    }
-                }
-            });
-            checkForMainThreadException();
-        }
-    }
-
     static class TargetTuple {
 
         final int mPosition;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
index d872eca..ea88391 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
@@ -1,11 +1,28 @@
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LayoutState.LAYOUT_END;
+import static android.support.v7.widget.LayoutState.LAYOUT_START;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
+import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
+import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.graphics.Rect;
 import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -17,27 +34,11 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static android.support.v7.widget.LayoutState.LAYOUT_END;
-import static android.support.v7.widget.LayoutState.LAYOUT_START;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
-import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
-import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import org.hamcrest.CoreMatchers;
-import org.hamcrest.MatcherAssert;
-
-public class BaseStaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
+abstract class BaseStaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
 
     protected static final boolean DEBUG = false;
     protected static final int AVG_ITEM_PER_VIEW = 3;
-    protected static final String TAG = "StaggeredGridLayoutManagerTest";
+    protected static final String TAG = "SGLM_TEST";
     volatile WrappedLayoutManager mLayoutManager;
     GridTestAdapter mAdapter;
 
@@ -124,7 +125,7 @@
     protected void waitForMainThread(int count) throws Throwable {
         final AtomicInteger i = new AtomicInteger(count);
         while (i.get() > 0) {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     i.decrementAndGet();
@@ -245,7 +246,7 @@
 
     protected void scrollToPositionWithOffset(final int position, final int offset)
             throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.scrollToPositionWithOffset(position, offset);
@@ -441,13 +442,13 @@
 
         @Override
         public String toString() {
-            return "[CONFIG:" +
-                    " span:" + mSpanCount + "," +
-                    " orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,") +
-                    " reverse:" + (mReverseLayout ? "T" : "F") +
-                    " itemCount:" + mItemCount +
-                    " wrapContent:" + mWrap +
-                    " gap strategy: " + gapStrategyName(mGapStrategy);
+            return "[CONFIG:"
+                    + "span:" + mSpanCount
+                    + ",orientation:" + (mOrientation == HORIZONTAL ? "horz," : "vert,")
+                    + ",reverse:" + (mReverseLayout ? "T" : "F")
+                    + ",itemCount:" + mItemCount
+                    + ",wrapContent:" + mWrap
+                    + ",gap_strategy:" + gapStrategyName(mGapStrategy);
         }
 
         protected static String gapStrategyName(int gapStrategy) {
@@ -455,9 +456,9 @@
                 case GAP_HANDLING_NONE:
                     return "none";
                 case GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS:
-                    return "move spans";
+                    return "move_spans";
             }
-            return "gap strategy: unknown";
+            return "gap_strategy:unknown";
         }
 
         @Override
@@ -720,21 +721,20 @@
 
         Map<Item, Rect> collectChildCoordinates() throws Throwable {
             final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
+                    final int start = mPrimaryOrientation.getStartAfterPadding();
+                    final int end = mPrimaryOrientation.getEndAfterPadding();
                     final int childCount = getChildCount();
                     for (int i = 0; i < childCount; i++) {
                         View child = getChildAt(i);
-                        // do it if and only if child is visible
-                        if (child.getRight() < 0 || child.getBottom() < 0 ||
-                                child.getLeft() >= getWidth() || child.getTop() >= getHeight()) {
-                            // invisible children may be drawn in cases like scrolling so we should
-                            // ignore them
+                        // ignore child if it fits the recycling constraints
+                        if (mPrimaryOrientation.getDecoratedStart(child) >= end
+                                || mPrimaryOrientation.getDecoratedEnd(child) < start) {
                             continue;
                         }
-                        LayoutParams lp = (LayoutParams) child
-                                .getLayoutParams();
+                        LayoutParams lp = (LayoutParams) child.getLayoutParams();
                         TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
                         items.put(vh.mBoundItem, getViewBounds(child));
                     }
@@ -805,9 +805,10 @@
         }
 
         @Override
-        int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            return super.gatherPrefetchIndices(dx, dy, state, outIndices);
+            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
         }
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
index 2dd16ef..a0c1a84 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
@@ -15,8 +15,6 @@
  */
 package android.support.v7.widget;
 
-import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
-
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -24,7 +22,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
-import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.support.annotation.Nullable;
@@ -37,16 +34,10 @@
 import android.widget.TextView;
 
 import org.hamcrest.CoreMatchers;
-import org.junit.Test;
-
-import org.hamcrest.CoreMatchers;
-import org.hamcrest.MatcherAssert;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Class to test any generic wrap content behavior.
@@ -540,8 +531,8 @@
         public String toString() {
             return "WrapContentConfig{"
                     + "unlimitedWidth=" + unlimitedWidth
-                    + ", unlimitedHeight=" + unlimitedHeight
-                    + ", padding=" + padding
+                    + ",unlimitedHeight=" + unlimitedHeight
+                    + ",padding=" + padding
                     + '}';
         }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
index 3f90a1a..de9cb52 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
new file mode 100644
index 0000000..0bd67f7
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class CacheUtils {
+    static void verifyPositionsPrefetched(RecyclerView view, int dx, int dy,
+            Integer[]... positionData) {
+        RecyclerView.PrefetchRegistry prefetchRegistry = mock(RecyclerView.PrefetchRegistry.class);
+        view.mLayout.collectPrefetchPositions(
+                dx, dy, view.mState, prefetchRegistry);
+
+        verify(prefetchRegistry, times(positionData.length)).addPosition(anyInt(), anyInt());
+        for (Integer[] aPositionData : positionData) {
+            verify(prefetchRegistry).addPosition(aPositionData[0], aPositionData[1]);
+        }
+    }
+
+    private static void verifyCacheContainsPosition(RecyclerView view, int position) {
+        for (int i = 0; i < view.mRecycler.mCachedViews.size(); i++) {
+            if (view.mRecycler.mCachedViews.get(i).mPosition == position) return;
+        }
+        fail("Cache does not contain position " + position);
+    }
+
+    /**
+     * Asserts that the positions passed are all resident in the view's cache.
+     */
+    static void verifyCacheContainsPositions(RecyclerView view, Integer... positions) {
+        for (Integer position : positions) {
+            verifyCacheContainsPosition(view, position);
+        }
+    }
+
+    /**
+     * Asserts that the position passed is resident in the view's cache, similar to
+     * {@link #verifyCacheContainsPositions}, but additionally requires presence in
+     * PrefetchRegistry.
+     */
+    static void verifyCacheContainsPrefetchedPositions(RecyclerView view, Integer... positions) {
+        verifyCacheContainsPositions(view, positions);
+
+        for (Integer position : positions) {
+            assertTrue(view.mPrefetchRegistry.lastPrefetchIncludedPosition(position));
+        }
+        assertEquals(positions.length, view.mRecycler.mCachedViews.size());
+    }
+
+    /**
+     * Asserts that none of the positions passed are resident in the view's cache.
+     */
+    static void verifyCacheDoesNotContainPositions(RecyclerView view, Integer... positions) {
+        for (Integer position : positions) {
+            for (int i = 0; i < view.mRecycler.mCachedViews.size(); i++) {
+                assertNotEquals("Cache must not contain position " + position,
+                        (int) position, view.mRecycler.mCachedViews.get(i).mPosition);
+            }
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
index 4dcd6a7..7743520 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
@@ -24,8 +24,8 @@
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -93,8 +93,7 @@
         RecyclerView.ViewHolder vh = vh();
         vh.mPosition = 12;
         mChildHelper.addView(vh.itemView, true);
-        assertSame(vh.itemView,
-                mChildHelper.findHiddenNonRemovedView(12, RecyclerView.INVALID_TYPE));
+        assertSame(vh.itemView, mChildHelper.findHiddenNonRemovedView(12));
     }
 
     @Test
@@ -103,7 +102,7 @@
         vh.mPosition = 12;
         vh.addFlags(RecyclerView.ViewHolder.FLAG_REMOVED);
         mChildHelper.addView(vh.itemView, true);
-        assertNull(mChildHelper.findHiddenNonRemovedView(12, RecyclerView.INVALID_TYPE));
+        assertNull(mChildHelper.findHiddenNonRemovedView(12));
     }
 
     private static class LoggingCallback implements ChildHelper.Callback {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
index 1ebd17f..52777d3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
@@ -16,31 +16,29 @@
 
 package android.support.v7.widget;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
-import android.os.Looper;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import static org.junit.Assert.*;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -135,7 +133,7 @@
     }
 
     void runAndWait(int itemCount, int seconds, final ThrowingRunnable postRun) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAnimator.runPendingAnimations();
@@ -317,7 +315,7 @@
     }
 
     void endAnimations(final RecyclerView.ViewHolder... vhs) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 for (RecyclerView.ViewHolder vh : vhs) {
@@ -329,7 +327,7 @@
 
     boolean animateAdd(final RecyclerView.ViewHolder vh) throws Throwable {
         final boolean[] result = new boolean[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = mAnimator.animateAdd(vh);
@@ -340,7 +338,7 @@
 
     boolean animateRemove(final RecyclerView.ViewHolder vh) throws Throwable {
         final boolean[] result = new boolean[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = mAnimator.animateRemove(vh);
@@ -352,7 +350,7 @@
     boolean animateMove(final RecyclerView.ViewHolder vh, final int fromX, final int fromY,
             final int toX, final int toY) throws Throwable {
         final boolean[] result = new boolean[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = mAnimator.animateMove(vh, fromX, fromY, toX, toY);
@@ -365,7 +363,7 @@
             final RecyclerView.ViewHolder newHolder,
             final int fromX, final int fromY, final int toX, final int toY) throws Throwable {
         final boolean[] result = new boolean[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = mAnimator.animateChange(oldHolder, newHolder, fromX, fromY, toX, toY);
@@ -376,7 +374,7 @@
 
     private ViewHolder createViewHolder(final int pos) throws Throwable {
         final ViewHolder vh = mAdapter.createViewHolder(mDummyParent, 1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAdapter.bindViewHolder(vh, pos);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
index a24eb83..08da3fb 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultMeasureSpecTest.java
@@ -23,8 +23,8 @@
 
 import android.graphics.Rect;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 
 import org.hamcrest.CoreMatchers;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
index 48bd6c5..55bed7e 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
@@ -21,10 +21,16 @@
 import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
 import static android.support.v7.widget.RecyclerView.VERTICAL;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.content.Context;
-import android.os.Looper;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.test.R;
@@ -35,11 +41,6 @@
 import android.view.ViewParent;
 import android.widget.LinearLayout;
 
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.hamcrest.CoreMatchers.is;
-
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.Description;
@@ -56,11 +57,12 @@
 /**
  * This class tests RecyclerView focus search failure handling by using a real LayoutManager.
  */
+@MediumTest
 @RunWith(Parameterized.class)
 public class FocusSearchNavigationTest {
     @Rule
-    public ActivityTestRule<RecyclerViewTestActivity> mActivityRule
-            = new ActivityTestRule<>(RecyclerViewTestActivity.class);
+    public ActivityTestRule<RecyclerViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(RecyclerViewTestActivity.class);
 
     private final int mOrientation;
     private final int mLayoutDir;
@@ -70,13 +72,21 @@
         mLayoutDir = layoutDir;
     }
 
-    @Parameterized.Parameters(name = "orientation:{0} layoutDir:{1}")
+    @Parameterized.Parameters(name = "orientation:{0},layoutDir:{1}")
     public static List<Object[]> params() {
-        return Arrays.asList(
-                new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
-                new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR},
-                new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_RTL}
-        );
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return Arrays.asList(
+                    new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+                    new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+                    new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_RTL}
+            );
+        } else {
+            // Do not test RTL before API 17
+            return Arrays.asList(
+                    new Object[]{VERTICAL, ViewCompat.LAYOUT_DIRECTION_LTR},
+                    new Object[]{HORIZONTAL, ViewCompat.LAYOUT_DIRECTION_LTR}
+            );
+        }
     }
 
     private Activity mActivity;
@@ -86,15 +96,15 @@
 
     private void setup(final int itemCount) throws Throwable {
         mActivity = mActivityRule.getActivity();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mActivity.setContentView(R.layout.focus_search_activity);
-                mActivity.getWindow().getDecorView().setLayoutDirection(mLayoutDir);
+                ViewCompat.setLayoutDirection(mActivity.getWindow().getDecorView(), mLayoutDir);
                 LinearLayout linearLayout = (LinearLayout) mActivity.findViewById(R.id.root);
                 linearLayout.setOrientation(mOrientation);
                 mRecyclerView = (RecyclerView) mActivity.findViewById(R.id.recycler_view);
-                mRecyclerView.setLayoutDirection(mLayoutDir);
+                ViewCompat.setLayoutDirection(mRecyclerView, mLayoutDir);
                 LinearLayoutManager layout = new LinearLayoutManager(mActivity.getBaseContext());
                 layout.setOrientation(mOrientation);
                 mRecyclerView.setLayoutManager(layout);
@@ -114,7 +124,7 @@
         waitForIdleSync();
         assertThat("test sanity", mRecyclerView.getLayoutManager().getLayoutDirection(),
                 is(mLayoutDir));
-        assertThat("test sanity", mRecyclerView.getLayoutDirection(), is(mLayoutDir));
+        assertThat("test sanity", ViewCompat.getLayoutDirection(mRecyclerView), is(mLayoutDir));
     }
 
     @Test
@@ -176,7 +186,7 @@
 
     private View focusSearch(final View view, final int focusDir) throws Throwable {
         final View[] result = new View[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = view.focusSearch(focusDir);
@@ -192,7 +202,7 @@
     }
 
     private void requestFocus(final View view) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 view.requestFocus();
@@ -203,7 +213,7 @@
 
     public void waitForIdleScroll(final RecyclerView recyclerView) throws Throwable {
         final CountDownLatch latch = new CountDownLatch(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() {
@@ -225,14 +235,6 @@
         assertTrue("should go idle in 10 seconds", latch.await(10, TimeUnit.SECONDS));
     }
 
-    private void runTestOnUiThread(Runnable r) throws Throwable {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            r.run();
-        } else {
-            InstrumentationRegistry.getInstrumentation().runOnMainSync(r);
-        }
-    }
-
     static class FocusSearchAdapter extends RecyclerView.Adapter {
         private int mItemCount;
         private int mOrientation;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
new file mode 100644
index 0000000..d25526f
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GapWorkerTest {
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Test
+    public void taskOrderViewPresence() {
+        ArrayList<GapWorker.Task> list = new ArrayList<>();
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+
+        list.get(0).view = null;
+        list.get(1).view = new RecyclerView(getContext());
+        list.get(2).view = null;
+
+        Collections.sort(list, GapWorker.sTaskComparator);
+
+        assertNotNull(list.get(0).view);
+        assertNull(list.get(1).view);
+        assertNull(list.get(2).view);
+    }
+    @Test
+    public void taskOrderImmediate() {
+        ArrayList<GapWorker.Task> list = new ArrayList<>();
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+
+        list.get(0).immediate = true;
+        list.get(1).immediate = false;
+        list.get(2).immediate = true;
+
+        Collections.sort(list, GapWorker.sTaskComparator);
+
+        assertTrue(list.get(0).immediate);
+        assertTrue(list.get(1).immediate);
+        assertFalse(list.get(2).immediate);
+    }
+
+    @Test
+    public void taskOrderImmediateVelocity() {
+        ArrayList<GapWorker.Task> list = new ArrayList<>();
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+
+        list.get(0).immediate = true;
+        list.get(0).viewVelocity = 10;
+
+        list.get(1).immediate = false;
+        list.get(1).viewVelocity = 99;
+
+        list.get(2).immediate = true;
+        list.get(2).viewVelocity = 20;
+
+        Collections.sort(list, GapWorker.sTaskComparator);
+
+        assertEquals(20, list.get(0).viewVelocity);
+        assertEquals(10, list.get(1).viewVelocity);
+        assertEquals(99, list.get(2).viewVelocity);
+    }
+
+    @Test
+    public void taskOrderImmediateVelocityDistance() {
+        ArrayList<GapWorker.Task> list = new ArrayList<>();
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+        list.add(new GapWorker.Task());
+
+        list.get(0).immediate = true;
+        list.get(0).viewVelocity = 400;
+        list.get(0).distanceToItem = 300;
+
+        list.get(1).immediate = false;
+        list.get(1).viewVelocity = 800;
+        list.get(1).distanceToItem = 900;
+
+        list.get(2).immediate = true;
+        list.get(2).viewVelocity = 300;
+        list.get(2).distanceToItem = 200;
+
+        list.get(3).immediate = true;
+        list.get(3).viewVelocity = 300;
+        list.get(3).distanceToItem = 100;
+
+        Collections.sort(list, GapWorker.sTaskComparator);
+
+        assertEquals(300, list.get(0).distanceToItem);
+        assertEquals(100, list.get(1).distanceToItem);
+        assertEquals(200, list.get(2).distanceToItem);
+        assertEquals(900, list.get(3).distanceToItem);
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerBaseConfigSetTest.java
index 99ec4ed..9f877fc 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerBaseConfigSetTest.java
@@ -16,21 +16,23 @@
 
 package android.support.v7.widget;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.MediumTest;
+import android.util.Log;
+import android.view.View;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.util.Log;
-import android.view.View;
-
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
+@MediumTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerBaseConfigSetTest extends BaseGridLayoutManagerTest {
     @Parameterized.Parameters(name = "{0}")
@@ -63,7 +65,7 @@
                 * (config.mReverseLayout ? -1 : 1);
         final String logPrefix = config.toString();
         final int[] globalPos = new int[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertSame("test sanity", mRecyclerView, rv);
@@ -107,7 +109,7 @@
         });
         checkForMainThreadException();
         // test sanity, ensure scroll happened
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final int childCount = mGlm.getChildCount();
@@ -123,7 +125,7 @@
             }
         });
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int globalScrollPosition = globalPos[0];
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCacheTest.java
index 6819a6a..2c33b85 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCacheTest.java
@@ -16,9 +16,15 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -28,11 +34,6 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 @RunWith(Parameterized.class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 public class GridLayoutManagerCacheTest extends BaseGridLayoutManagerTest {
@@ -47,7 +48,7 @@
         mDy = dy;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, dx:{1}, dy:{2}")
+    @Parameterized.Parameters(name = "config:{0},dx:{1},dy:{2}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -81,7 +82,7 @@
         waitForFirstLayout(recyclerView);
 
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // pretend to have an extra 5s before next frame so prefetch won't abort early
@@ -95,20 +96,20 @@
         mRecyclerView.setItemViewCacheSize(0);
         {
             mGlm.expectPrefetch(1);
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mRecyclerView.mRecycler.recycleAndClearCachedViews();
-                    mRecyclerView.mViewPrefetcher.postFromTraversal(mDx, mDy);
+                    mRecyclerView.mGapWorker.postFromTraversal(mRecyclerView, mDx, mDy);
 
                     // Lie about post time, so prefetch executes even if it is delayed
-                    mRecyclerView.mViewPrefetcher.mPostTimeNanos += TimeUnit.SECONDS.toNanos(5);
+                    mRecyclerView.mGapWorker.mPostTimeNs += TimeUnit.SECONDS.toNanos(5);
                 }
             });
             mGlm.waitForPrefetch(1);
         }
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // validate cache state on UI thread
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCachedBordersTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCachedBordersTest.java
index 2fc5015..71f93e6 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCachedBordersTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCachedBordersTest.java
@@ -16,19 +16,22 @@
 package android.support.v7.widget;
 
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.MediumTest;
+import android.view.View;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.view.View;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static org.junit.Assert.assertEquals;
-
+@MediumTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerCachedBordersTest extends BaseGridLayoutManagerTest {
 
@@ -49,7 +52,9 @@
     @Test
     public void gridCachedBorderstTest() throws Throwable {
         RecyclerView recyclerView = setupBasic(mConfig);
-        waitForFirstLayout(recyclerView);
+        mGlm.expectLayout(1);
+        setRecyclerView(recyclerView);
+        mGlm.waitForLayout(10);
         final boolean vertical = mConfig.mOrientation == GridLayoutManager.VERTICAL;
         final int expectedSizeSum = vertical ? recyclerView.getWidth() : recyclerView.getHeight();
         final int lastVisible = mGlm.findLastVisibleItemPosition();
@@ -66,7 +71,7 @@
     }
 
     private static List<Config> cachedBordersTestConfigs() {
-        ArrayList<Config> configs = new ArrayList<Config>();
+        ArrayList<Config> configs = new ArrayList<>();
         final int[] spanCounts = new int[]{88, 279, 741};
         final int[] spanPerItem = new int[]{11, 9, 13};
         for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCustomSizeInScrollDirectionTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCustomSizeInScrollDirectionTest.java
index 77a0723..dec0d72 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCustomSizeInScrollDirectionTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerCustomSizeInScrollDirectionTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -32,9 +33,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
+@MediumTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerCustomSizeInScrollDirectionTest extends BaseGridLayoutManagerTest {
-    @Parameterized.Parameters(name = "addDecorOffsets:{1} addMargins:{2} config:{0}")
+    @Parameterized.Parameters(name = "addDecorOffsets:{1},addMargins:{2},config:{0}")
     public static List<Object[]> getParams() {
         List<Object[]> params = new ArrayList<>();
         Boolean[] options = new Boolean[]{true, false};
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerNoOpUpdateTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerNoOpUpdateTest.java
index cd0fac4..a77e2c4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerNoOpUpdateTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerNoOpUpdateTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -34,9 +35,10 @@
 /**
  * Tests dispatching no-op updates to the GLM and ensures it re-lays out items in the same location
  */
+@MediumTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerNoOpUpdateTest extends BaseGridLayoutManagerTest {
-    @Parameterized.Parameters(name = "conf:{0} rtl={1}")
+    @Parameterized.Parameters(name = "conf:{0},rtl={1}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         for (BaseGridLayoutManagerTest.Config config : createBaseVariations()) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerRtlTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerRtlTest.java
index 78f1576..3343ff5 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerRtlTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerRtlTest.java
@@ -16,23 +16,25 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.view.View;
-import android.view.ViewGroup;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
+@MediumTest
 @RunWith(Parameterized.class)
 public class GridLayoutManagerRtlTest extends BaseGridLayoutManagerTest {
 
@@ -44,7 +46,7 @@
         mItemsWrapContent = itemsWrapContent;
     }
 
-    @Parameterized.Parameters(name = "conf: {0} changeRl:{1} oneLine: {2} itemsWrap: {3}")
+    @Parameterized.Parameters(name = "conf:{0},changeRl:{1},oneLine:{2},itemsWrap:{3}")
     public static List<Object[]> params() {
         List<Object[]> result = new ArrayList<>();
         for (boolean changeRtlAfter : new boolean[]{false, true}) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
index 6dc80bd..bc107b2 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerSnappingTest.java
@@ -16,8 +16,13 @@
 
 package android.support.v7.widget;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+
 import android.support.annotation.Nullable;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -28,11 +33,6 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertTrue;
-
 @RunWith(Parameterized.class)
 public class GridLayoutManagerSnappingTest extends BaseGridLayoutManagerTest {
 
@@ -44,7 +44,7 @@
         mReverseScroll = reverseScroll;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, reverseScroll:{1}")
+    @Parameterized.Parameters(name = "config:{0},reverseScroll:{1}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -79,6 +79,7 @@
         assertCenterAligned(viewAfterFling);
     }
 
+    @MediumTest
     @Test
     public void snapOnScrollNextItem() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -223,7 +224,7 @@
     private boolean fling(final int velocityX, final int velocityY)
             throws Throwable {
         final AtomicBoolean didStart = new AtomicBoolean(false);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean result = mRecyclerView.fling(velocityX, velocityY);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index 8211fe6..db42498 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -30,12 +30,12 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.StateListDrawable;
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.SparseIntArray;
 import android.util.StateSet;
 import android.view.View;
@@ -181,7 +181,7 @@
     public void scrollWithoutLayoutAfterInvalidate() throws Throwable {
         final RecyclerView recyclerView = setupBasic(new Config(3, 100));
         waitForFirstLayout(recyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGlm.setSpanCount(5);
@@ -306,7 +306,7 @@
         final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
         final View chosen = recyclerView.getChildAt(recyclerView.getChildCount() - 2);
         final int position = recyclerView.getChildLayoutPosition(chosen);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityNodeInfo(chosen, info);
@@ -773,7 +773,7 @@
         waitForFirstLayout(rv);
         assertTrue(mGlm.supportsPredictiveItemAnimations());
         mGlm.expectLayout(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mGlm.setSpanCount(5);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
index 1703dcd..e4b6c39 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
@@ -15,7 +15,8 @@
  */
 package android.support.v7.widget;
 
-import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.AspectRatioMeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest
+        .AspectRatioMeasureBehavior;
 import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.MeasureBehavior;
 import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.WrapContentAdapter;
 import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
@@ -26,8 +27,8 @@
 import android.app.Activity;
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 import android.view.View;
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentWithAspectRatioTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentWithAspectRatioTest.java
index 6c311e4..aa847be 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentWithAspectRatioTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentWithAspectRatioTest.java
@@ -16,36 +16,37 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.BaseWrapContentTest.WrapContentConfig;
+import static android.support.v7.widget.GridLayoutManagerTest.Config;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.graphics.Color;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.graphics.Color;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static android.support.v7.widget.BaseWrapContentTest.WrapContentConfig;
-import static android.support.v7.widget.GridLayoutManagerTest.Config;
-import static org.hamcrest.CoreMatchers.*;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-
 @RunWith(Parameterized.class)
 @MediumTest
 public class GridLayoutManagerWrapContentWithAspectRatioTest
         extends BaseWrapContentWithAspectRatioTest {
 
-    @Parameterized.Parameters(name = "{0} {1} {2}")
+    @Parameterized.Parameters(name = "{0},{1},{2}")
     public static List<Object[]> params() {
         List<Object[]> params = new ArrayList<>();
         for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
index 15bbf8c..4c7161b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
@@ -31,9 +31,8 @@
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
 
 import org.hamcrest.CoreMatchers;
 import org.junit.Test;
@@ -481,12 +480,12 @@
         notifyDataSetChangedWithAppearing(true);
     }
 
-    public void notifyDataSetChangedWithAppearing(final boolean notifyBoth) throws Throwable {
+    private void notifyDataSetChangedWithAppearing(final boolean notifyBoth) throws Throwable {
         final TestAdapter adapter = new TestAdapter(10);
         adapter.setHasStableIds(true);
         setupBasic(10, 0, 10, adapter);
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -520,12 +519,12 @@
         notifyDataSetChangedWithDispappearing(true);
     }
 
-    public void notifyDataSetChangedWithDispappearing(final boolean notifyBoth) throws Throwable {
+    private void notifyDataSetChangedWithDispappearing(final boolean notifyBoth) throws Throwable {
         final TestAdapter adapter = new TestAdapter(10);
         adapter.setHasStableIds(true);
         setupBasic(10, 0, 10, adapter);
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
index 34600e9..0a9095c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
@@ -29,8 +29,8 @@
 import static org.junit.Assert.assertThat;
 
 import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
@@ -180,11 +180,11 @@
                 );
             }
         };
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
         // case
         final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollToPosition(scrollPosition);
@@ -192,7 +192,7 @@
         });
         while (mLayoutManager.isSmoothScrolling() ||
                 mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            runTestOnUiThread(viewInBoundsTest);
+            mActivityRule.runOnUiThread(viewInBoundsTest);
             Thread.sleep(400);
         }
         // delete all items
@@ -200,7 +200,7 @@
         mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
         mLayoutManager.waitForLayout(2);
         // test empty case
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         // set a new adapter with huge items to test full bounds check
         mLayoutManager.expectLayouts(1);
         final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
@@ -216,14 +216,14 @@
                 }
             }
         };
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setAdapter(newAdapter);
             }
         });
         mLayoutManager.waitForLayout(2);
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
     }
 
     @Test
@@ -242,7 +242,7 @@
 
         final int size = helper.getDecoratedMeasurement(vh.itemView);
         AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
@@ -276,7 +276,7 @@
 
         final int size = helper.getDecoratedMeasurement(vh.itemView);
         AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerCacheTest.java
index cacc9f4..d936539 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerCacheTest.java
@@ -16,10 +16,14 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static org.junit.Assert.assertEquals;
+
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.FrameLayout;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,10 +33,6 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static org.junit.Assert.assertEquals;
-
 @RunWith(Parameterized.class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 public class LinearLayoutManagerCacheTest extends BaseLinearLayoutManagerTest {
@@ -47,7 +47,7 @@
         mDy = dy;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, dx:{1}, dy:{2}")
+    @Parameterized.Parameters(name = "config:{0},dx:{1},dy:{2}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -72,7 +72,7 @@
 
         setupByConfig(config, true);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // pretend to have an extra 5s before next frame so prefetch won't abort early
@@ -85,21 +85,21 @@
         mRecyclerView.setItemViewCacheSize(0);
         {
             mLayoutManager.expectPrefetch(1);
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mRecyclerView.mRecycler.recycleAndClearCachedViews();
-                    mRecyclerView.mViewPrefetcher.postFromTraversal(mDx, mDy);
+                    mRecyclerView.mGapWorker.postFromTraversal(mRecyclerView, mDx, mDy);
 
                     // Lie about post time, so prefetch executes even if it is delayed
-                    mRecyclerView.mViewPrefetcher.mPostTimeNanos += TimeUnit.SECONDS.toNanos(5);
+                    mRecyclerView.mGapWorker.mPostTimeNs += TimeUnit.SECONDS.toNanos(5);
                 }
             });
             mLayoutManager.waitForPrefetch(1);
         }
 
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // validate cache state on UI thread
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java
index 27c93fc..2cdb2e7 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java
@@ -16,22 +16,21 @@
 
 package android.support.v7.widget;
 
-import org.junit.After;
-import org.junit.Before;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.support.test.filters.MediumTest;
+import android.view.View;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.support.test.InstrumentationRegistry;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static org.junit.Assert.*;
-
 @RunWith(Parameterized.class)
 public class LinearLayoutManagerPrepareForDropTest extends BaseLinearLayoutManagerTest {
 
@@ -44,7 +43,7 @@
         mSelectTargetChildren = selectTargetChildren;
     }
 
-    @Parameterized.Parameters(name = "{0}_{1}")
+    @Parameterized.Parameters(name = "{0},selectTargetChildren:{1}")
     public static Iterable<Object[]> params() {
         SelectTargetChildren[] selectors
                 = new SelectTargetChildren[]{
@@ -53,36 +52,60 @@
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{1, 0};
                     }
+                    @Override
+                    public String toString() {
+                        return "{1,0}";
+                    }
                 },
                 new SelectTargetChildren() {
                     @Override
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{0, 1};
                     }
+                    @Override
+                    public String toString() {
+                        return "{0,1}";
+                    }
                 },
                 new SelectTargetChildren() {
                     @Override
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{childCount - 1, childCount - 2};
                     }
+                    @Override
+                    public String toString() {
+                        return "{childCount-1,childCount-2}";
+                    }
                 },
                 new SelectTargetChildren() {
                     @Override
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{childCount - 2, childCount - 1};
                     }
+                    @Override
+                    public String toString() {
+                        return "{childCount-2,childCount-1}";
+                    }
                 },
                 new SelectTargetChildren() {
                     @Override
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{childCount / 2, childCount / 2 + 1};
                     }
+                    @Override
+                    public String toString() {
+                        return "{childCount/2,childCount/2+1}";
+                    }
                 },
                 new SelectTargetChildren() {
                     @Override
                     public int[] selectTargetChildren(int childCount) {
                         return new int[]{childCount / 2 + 1, childCount / 2};
                     }
+                    @Override
+                    public String toString() {
+                        return "{childCount/2+1,childCount/2}";
+                    }
                 }
         };
         List<Object[]> variations = new ArrayList<>();
@@ -146,7 +169,7 @@
             y = dragCoordinate;
             x = fromChild.getLeft();
         }
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mTestAdapter.moveInUIThread(fromPos, toPos);
@@ -166,7 +189,6 @@
     }
 
     protected interface SelectTargetChildren {
-
         int[] selectTargetChildren(int childCount);
     }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java
index ef371a8..e455b95 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java
@@ -16,22 +16,20 @@
 
 package android.support.v7.widget;
 
-import org.junit.After;
-import org.junit.Before;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.MediumTest;
+import android.widget.FrameLayout;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.support.test.InstrumentationRegistry;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.FrameLayout;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static org.junit.Assert.*;
-
 @RunWith(Parameterized.class)
 public class LinearLayoutManagerResizeTest extends BaseLinearLayoutManagerTest {
 
@@ -56,7 +54,7 @@
     public void resize() throws Throwable {
         final Config config = (Config) mConfig.clone();
         final FrameLayout container = getRecyclerViewContainer();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 container.setPadding(0, 0, 0, 0);
@@ -72,7 +70,7 @@
                 .findFirstCompletelyVisibleItemPosition();
         mLayoutManager.expectLayouts(1);
         // resize the recycler view to half
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (config.mOrientation == HORIZONTAL) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java
index 5fcd33c..b109a78 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java
@@ -16,22 +16,23 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
 
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.support.test.filters.LargeTest;
 import android.util.Log;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
-import static org.junit.Assert.*;
 
 @RunWith(Parameterized.class)
 @LargeTest
@@ -76,8 +77,8 @@
         };
     }
 
-    @Parameterized.Parameters(name = "{0}_waitForLayout:{1}_loadDataAfterRestore:{2}"
-            + "_postLayout:{3}_postRestore:{4}")
+    @Parameterized.Parameters(name = "{0},waitForLayout:{1},loadDataAfterRestore:{2}"
+            + ",postLayout:{3},postRestore:{4}")
     public static Iterable<Object[]> params()
             throws IllegalAccessException, CloneNotSupportedException, NoSuchFieldException {
         PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
index 3d978da..f211920 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSnappingTest.java
@@ -16,24 +16,23 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import android.support.annotation.Nullable;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertSame;
 import static junit.framework.Assert.assertTrue;
 
+import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 @RunWith(Parameterized.class)
 public class LinearLayoutManagerSnappingTest extends BaseLinearLayoutManagerTest {
 
@@ -45,7 +44,7 @@
         mReverseScroll = reverseScroll;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, reverseScroll:{1}")
+    @Parameterized.Parameters(name = "config:{0},reverseScroll:{1}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -243,7 +242,7 @@
 
     private boolean fling(final int velocityX, final int velocityY) throws Throwable {
         final AtomicBoolean didStart = new AtomicBoolean(false);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean result = mRecyclerView.fling(velocityX, velocityY);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index 5441b68..5f61ea7 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -24,10 +24,10 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.support.test.filters.MediumTest;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -301,7 +301,7 @@
         }
 
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
@@ -428,7 +428,7 @@
     @Test
     public void dontRecycleChildrenOnDetach() throws Throwable {
         setupByConfig(new Config().recycleChildrenOnDetach(false), true);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
@@ -443,7 +443,7 @@
     public void recycleChildrenOnDetach() throws Throwable {
         setupByConfig(new Config().recycleChildrenOnDetach(true), true);
         final int childCount = mLayoutManager.getChildCount();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int recyclerSize = mRecyclerView.mRecycler.getRecycledViewPool().size();
@@ -463,7 +463,7 @@
         assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0);
 
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.scrollToPositionWithOffset(1, 0);
@@ -482,7 +482,7 @@
         final AccessibilityDelegateCompat delegateCompat = mRecyclerView
                 .getCompatAccessibilityDelegate();
         final AccessibilityEvent event = AccessibilityEvent.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
index 9a8d42e..bda7297 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
@@ -22,9 +22,9 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 
 import org.junit.Test;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentWithAspectRatioTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentWithAspectRatioTest.java
index 185aa4b..9e9f7bc 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentWithAspectRatioTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentWithAspectRatioTest.java
@@ -16,25 +16,26 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.graphics.Color;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+
 import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.graphics.Color;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.ViewGroup;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static org.hamcrest.MatcherAssert.assertThat;
-
 @RunWith(Parameterized.class)
 @MediumTest
 public class LinearLayoutManagerWrapContentWithAspectRatioTest
@@ -52,7 +53,7 @@
         mRatio = ratio;
     }
 
-    @Parameterized.Parameters(name = "{0} {1} ratio:{2}")
+    @Parameterized.Parameters(name = "{0},{1},ratio:{2}")
     public static Iterable<Object[]> data() {
         List<Object[]> params = new ArrayList<>();
         for (float ratio : new float[]{.5f, 1f, 2f}) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java
new file mode 100644
index 0000000..2a615f2
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/MultiRecyclerViewPrefetchTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+@RunWith(AndroidJUnit4.class)
+public class MultiRecyclerViewPrefetchTest {
+    private RecyclerView.RecycledViewPool mRecycledViewPool;
+    private ArrayList<RecyclerView> mViews = new ArrayList<>();
+
+    private long mMockNanoTime = 0;
+
+    @Before
+    public void setup() throws Exception {
+        GapWorker gapWorker = GapWorker.sGapWorker.get();
+        if (gapWorker != null) {
+            assertTrue(gapWorker.mRecyclerViews.isEmpty());
+        }
+        mMockNanoTime = 0;
+        mRecycledViewPool = new RecyclerView.RecycledViewPool();
+    }
+
+    @After
+    public void teardown() {
+        for (RecyclerView rv : mViews) {
+            if (rv.isAttachedToWindow()) {
+                // ensure we detach views, so ThreadLocal GapWorker's list is cleared
+                rv.onDetachedFromWindow();
+            }
+        }
+        GapWorker gapWorker = GapWorker.sGapWorker.get();
+        if (gapWorker != null) {
+            assertTrue(gapWorker.mRecyclerViews.isEmpty());
+        }
+        mViews.clear();
+    }
+
+    private RecyclerView createRecyclerView() {
+        RecyclerView rv = new RecyclerView(getContext()) {
+            @Override
+            long getNanoTime() {
+                return mMockNanoTime;
+            }
+        };
+
+        // shared stats + enable clearing of pool
+        rv.setRecycledViewPool(mRecycledViewPool);
+
+        // enable GapWorker
+        rv.onAttachedToWindow();
+        mViews.add(rv);
+
+        return rv;
+    }
+
+    public void registerTimePassingMs(long ms) {
+        mMockNanoTime += TimeUnit.MILLISECONDS.toNanos(ms);
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    private void clearCachesAndPool() {
+        for (RecyclerView rv : mViews) {
+            rv.mRecycler.recycleAndClearCachedViews();
+        }
+        mRecycledViewPool.clear();
+    }
+
+    @Test
+    public void prefetchOrdering() throws Throwable {
+        for (int i = 0; i < 3; i++) {
+            RecyclerView rv = createRecyclerView();
+
+            // first view 50x100 pixels, rest are 100x100 so second column is offset
+            rv.setAdapter(new RecyclerView.Adapter() {
+                @Override
+                public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
+                        int viewType) {
+                    registerTimePassingMs(5);
+                    return new RecyclerView.ViewHolder(new View(parent.getContext())) {};
+                }
+
+                @Override
+                public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+                    registerTimePassingMs(5);
+                    holder.itemView.setMinimumWidth(100);
+                    holder.itemView.setMinimumHeight(position == 0 ? 50 : 100);
+                }
+
+                @Override
+                public int getItemCount() {
+                    return 100;
+                }
+            });
+            rv.setLayoutManager(
+                    new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
+
+            // Attach, position 200x200 view at 100 scroll offset, with an empty cache.
+            rv.measure(View.MeasureSpec.AT_MOST | 200, View.MeasureSpec.AT_MOST | 200);
+            rv.layout(0, 0, 200, 200);
+            rv.scrollBy(0, 100);
+
+            ViewCompat.setTranslationX(rv, 100 * i);
+        }
+
+        GapWorker worker = GapWorker.sGapWorker.get();
+        assertNotNull(worker);
+
+        /* Each row is 50 pixels:
+         * ------------- *
+         *   0   |   1   *
+         *___2___|___1___*
+         *   2   |   3   *
+         *   4   |   3   *
+         *   4   |   5   *
+         *___6___|___5___*
+         *   6   |   7   *
+         *   8   |   7   *
+         *      ...      *
+         */
+
+        mViews.get(0).mPrefetchRegistry.setPrefetchVector(0, 10);
+        mViews.get(1).mPrefetchRegistry.setPrefetchVector(0, -11);
+        mViews.get(2).mPrefetchRegistry.setPrefetchVector(0, 60);
+
+        // prefetch with deadline that has passed - only demand-loaded views
+        clearCachesAndPool();
+        worker.prefetch(0);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(0), 7);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(1), 1);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(2), 7, 8);
+
+
+        // prefetch with 54ms - should load demand-loaded views (taking 40ms) + one more
+        clearCachesAndPool();
+        worker.prefetch(mMockNanoTime + TimeUnit.MILLISECONDS.toNanos(54));
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(0), 7);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(1), 0, 1);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mViews.get(2), 7, 8);
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java
new file mode 100644
index 0000000..0799d3d
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/PagerSnapHelperTest.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(Parameterized.class)
+public class PagerSnapHelperTest extends BaseLinearLayoutManagerTest {
+
+    final Config mConfig;
+    final boolean mReverseScroll;
+
+    public PagerSnapHelperTest(Config config, boolean reverseScroll) {
+        mConfig = config;
+        mReverseScroll = reverseScroll;
+    }
+
+    @Parameterized.Parameters(name = "config:{0},reverseScroll:{1}")
+    public static List<Object[]> getParams() {
+        List<Object[]> result = new ArrayList<>();
+        List<Config> configs = createBaseVariations();
+        for (Config config : configs) {
+            for (boolean reverseScroll : new boolean[] {false, true}) {
+                result.add(new Object[]{config, reverseScroll});
+            }
+        }
+        return result;
+    }
+
+    @MediumTest
+    @Test
+    public void snapOnScrollSameView() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        setupByConfig(config, true,
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT),
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+        setupSnapHelper();
+
+        // Record the current center view.
+        TextView view = (TextView) findCenterView(mLayoutManager);
+        assertCenterAligned(view);
+
+        int scrollDistance = (getViewDimension(view) / 2) - 1;
+        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
+        mLayoutManager.expectIdleState(3);
+        smoothScrollBy(scrollDist);
+        mLayoutManager.waitForSnap(10);
+
+        // Views have not changed
+        View viewAfterFling = findCenterView(mLayoutManager);
+        assertSame("The view should NOT have scrolled", view, viewAfterFling);
+        assertCenterAligned(viewAfterFling);
+    }
+
+    @MediumTest
+    @Test
+    public void snapOnScrollNextView() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        setupByConfig(config, true,
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT),
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+        setupSnapHelper();
+
+        // Record the current center view.
+        View view = findCenterView(mLayoutManager);
+        assertCenterAligned(view);
+
+        int scrollDistance = (getViewDimension(view) / 2) + 1;
+        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
+        mLayoutManager.expectIdleState(3);
+        smoothScrollBy(scrollDist);
+        mLayoutManager.waitForSnap(10);
+
+        // Views have not changed
+        View viewAfterFling = findCenterView(mLayoutManager);
+        assertNotSame("The view should have scrolled", view, viewAfterFling);
+        int expectedPosition = mConfig.mItemCount / 2 + (mConfig.mReverseLayout
+                ? (mReverseScroll ? 1 : -1)
+                : (mReverseScroll ? -1 : 1));
+        assertEquals(expectedPosition, mLayoutManager.getPosition(viewAfterFling));
+        assertCenterAligned(viewAfterFling);
+    }
+
+    @MediumTest
+    @Test
+    public void snapOnFlingSameView() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        setupByConfig(config, true,
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT),
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+        setupSnapHelper();
+
+        // Record the current center view.
+        View view = findCenterView(mLayoutManager);
+        assertCenterAligned(view);
+
+        // Velocity small enough to not scroll to the next view.
+        int velocity = (int) (1.000001 * mRecyclerView.getMinFlingVelocity());
+        int velocityDir = mReverseScroll ? -velocity : velocity;
+        mLayoutManager.expectIdleState(2);
+        // Scroll at one pixel in the correct direction to allow fling snapping to the next view.
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.scrollBy(mReverseScroll ? -1 : 1, mReverseScroll ? -1 : 1);
+            }
+        });
+        waitForIdleScroll(mRecyclerView);
+        assertTrue(fling(velocityDir, velocityDir));
+        // Wait for two settling scrolls: the initial one and the corrective one.
+        waitForIdleScroll(mRecyclerView);
+        mLayoutManager.waitForSnap(100);
+
+        View viewAfterFling = findCenterView(mLayoutManager);
+
+        assertSame("The view should NOT have scrolled", view, viewAfterFling);
+        assertCenterAligned(viewAfterFling);
+    }
+
+    @MediumTest
+    @Test
+    public void snapOnFlingNextView() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        setupByConfig(config, true,
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT),
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+        setupSnapHelper();
+        runSnapOnMaxFlingNextView((int) (0.2 * mRecyclerView.getMaxFlingVelocity()));
+    }
+
+    @MediumTest
+    @Test
+    public void snapOnMaxFlingNextView() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        setupByConfig(config, true,
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT),
+                new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT));
+        setupSnapHelper();
+        runSnapOnMaxFlingNextView(mRecyclerView.getMaxFlingVelocity());
+    }
+
+    private void runSnapOnMaxFlingNextView(int velocity) throws Throwable {
+        // Record the current center view.
+        View view = findCenterView(mLayoutManager);
+        assertCenterAligned(view);
+
+        int velocityDir = mReverseScroll ? -velocity : velocity;
+        mLayoutManager.expectIdleState(1);
+
+        // Scroll at one pixel in the correct direction to allow fling snapping to the next view.
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.scrollBy(mReverseScroll ? -1 : 1, mReverseScroll ? -1 : 1);
+            }
+        });
+        waitForIdleScroll(mRecyclerView);
+        assertTrue(fling(velocityDir, velocityDir));
+        mLayoutManager.waitForSnap(100);
+        getInstrumentation().waitForIdleSync();
+
+        View viewAfterFling = findCenterView(mLayoutManager);
+
+        assertNotSame("The view should have scrolled", view, viewAfterFling);
+        int expectedPosition = mConfig.mItemCount / 2 + (mConfig.mReverseLayout
+                ? (mReverseScroll ? 1 : -1)
+                : (mReverseScroll ? -1 : 1));
+        assertEquals(expectedPosition, mLayoutManager.getPosition(viewAfterFling));
+        assertCenterAligned(viewAfterFling);
+    }
+
+    private void setupSnapHelper() throws Throwable {
+        SnapHelper snapHelper = new PagerSnapHelper();
+        mLayoutManager.expectIdleState(1);
+        snapHelper.attachToRecyclerView(mRecyclerView);
+
+        mLayoutManager.expectLayouts(1);
+        scrollToPosition(mConfig.mItemCount / 2);
+        mLayoutManager.waitForLayout(2);
+
+        View view = findCenterView(mLayoutManager);
+        int scrollDistance = distFromCenter(view) / 2;
+        if (scrollDistance == 0) {
+            return;
+        }
+
+        int scrollDist = mReverseScroll ? -scrollDistance : scrollDistance;
+
+        mLayoutManager.expectIdleState(2);
+        smoothScrollBy(scrollDist);
+        mLayoutManager.waitForSnap(10);
+    }
+
+    @Nullable
+    private View findCenterView(RecyclerView.LayoutManager layoutManager) {
+        if (layoutManager.canScrollHorizontally()) {
+            return mRecyclerView.findChildViewUnder(mRecyclerView.getWidth() / 2, 0);
+        } else {
+            return mRecyclerView.findChildViewUnder(0, mRecyclerView.getHeight() / 2);
+        }
+    }
+
+    private int getViewDimension(View view) {
+        OrientationHelper helper;
+        if (mLayoutManager.canScrollHorizontally()) {
+            helper = OrientationHelper.createHorizontalHelper(mLayoutManager);
+        } else {
+            helper = OrientationHelper.createVerticalHelper(mLayoutManager);
+        }
+        return helper.getDecoratedMeasurement(view);
+    }
+
+    private void assertCenterAligned(View view) {
+        if (mLayoutManager.canScrollHorizontally()) {
+            assertEquals(mRecyclerView.getWidth() / 2,
+                    mLayoutManager.getViewBounds(view).centerX());
+        } else {
+            assertEquals(mRecyclerView.getHeight() / 2,
+                    mLayoutManager.getViewBounds(view).centerY());
+        }
+    }
+
+    private int distFromCenter(View view) {
+        if (mLayoutManager.canScrollHorizontally()) {
+            return Math.abs(mRecyclerView.getWidth() / 2
+                    - mLayoutManager.getViewBounds(view).centerX());
+        } else {
+            return Math.abs(mRecyclerView.getHeight() / 2
+                    - mLayoutManager.getViewBounds(view).centerY());
+        }
+    }
+
+    private boolean fling(final int velocityX, final int velocityY) throws Throwable {
+        final AtomicBoolean didStart = new AtomicBoolean(false);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                boolean result = mRecyclerView.fling(velocityX, velocityY);
+                didStart.set(result);
+            }
+        });
+        if (!didStart.get()) {
+            return false;
+        }
+        waitForIdleScroll(mRecyclerView);
+        return true;
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecycledViewPoolTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecycledViewPoolTest.java
new file mode 100644
index 0000000..9a6d02c
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecycledViewPoolTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecycledViewPoolTest {
+    static class MockViewHolder extends RecyclerView.ViewHolder {
+        public MockViewHolder(Context context) {
+            super(new View(context));
+        }
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Test
+    public void construct() {
+        RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
+        assertEquals(0, pool.getRecycledViewCount(0));
+        assertEquals(0, pool.size());
+    }
+
+    private RecyclerView.ViewHolder makeHolder(int viewType) {
+        RecyclerView.ViewHolder holder = new MockViewHolder(getContext());
+        holder.mItemViewType = viewType;
+        return holder;
+    }
+
+    @Test
+    public void put() {
+        RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
+        pool.putRecycledView(makeHolder(0));
+        pool.putRecycledView(makeHolder(1));
+        pool.putRecycledView(makeHolder(2));
+        pool.putRecycledView(makeHolder(2));
+
+        assertEquals(1, pool.getRecycledViewCount(0));
+        assertEquals(1, pool.getRecycledViewCount(1));
+        assertEquals(2, pool.getRecycledViewCount(2));
+        assertEquals(0, pool.getRecycledViewCount(3));
+        assertEquals(4, pool.size());
+    }
+
+    @Test
+    public void putAndGet() {
+        RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
+        pool.putRecycledView(makeHolder(3));
+        pool.putRecycledView(makeHolder(3));
+
+        assertEquals(2, pool.size());
+        assertEquals(2, pool.getRecycledViewCount(3));
+
+        RecyclerView.ViewHolder a = pool.getRecycledView(3);
+
+        assertNotNull(a);
+        assertEquals(1, pool.size());
+        assertEquals(1, pool.getRecycledViewCount(3));
+
+        RecyclerView.ViewHolder b = pool.getRecycledView(3);
+
+        assertNotNull(b);
+        assertNotEquals(a, b);
+        assertEquals(0, pool.size());
+        assertEquals(0, pool.getRecycledViewCount(3));
+    }
+}
\ No newline at end of file
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
index 7ba99a5..22adefd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -16,23 +16,28 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
 import android.view.ViewGroup;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
+@MediumTest
 @RunWith(AndroidJUnit4.class)
 public class RecyclerViewAccessibilityLifecycleTest extends BaseRecyclerViewInstrumentationTest {
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
     @Test
     public void dontDispatchChangeDuringLayout() throws Throwable {
         LayoutAllLayoutManager lm = new LayoutAllLayoutManager();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
index be0fc10..e952370 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
@@ -16,45 +16,48 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 @MediumTest
 @RunWith(Parameterized.class)
 public class RecyclerViewAccessibilityTest extends BaseRecyclerViewInstrumentationTest {
-
-    final boolean verticalScrollBefore, horizontalScrollBefore, verticalScrollAfter,
-            horizontalScrollAfter;
+    private static final boolean SUPPORTS_COLLECTION_INFO =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+    private final boolean mVerticalScrollBefore;
+    private final boolean mHorizontalScrollBefore;
+    private final boolean mVerticalScrollAfter;
+    private final boolean mHorizontalScrollAfter;
 
     public RecyclerViewAccessibilityTest(boolean verticalScrollBefore,
             boolean horizontalScrollBefore, boolean verticalScrollAfter,
             boolean horizontalScrollAfter) {
-        this.verticalScrollBefore = verticalScrollBefore;
-        this.horizontalScrollBefore = horizontalScrollBefore;
-        this.verticalScrollAfter = verticalScrollAfter;
-        this.horizontalScrollAfter = horizontalScrollAfter;
+        mVerticalScrollBefore = verticalScrollBefore;
+        mHorizontalScrollBefore = horizontalScrollBefore;
+        mVerticalScrollAfter = verticalScrollAfter;
+        mHorizontalScrollAfter = horizontalScrollAfter;
     }
 
-    @Parameterized.Parameters(name = "vBefore={0} vAfter={1} hBefore={2} hAfter={3}")
+    @Parameterized.Parameters(name = "vBefore={0},vAfter={1},hBefore={2},hAfter={3}")
     public static List<Object[]> getParams() {
         List<Object[]> params = new ArrayList<>();
         for (boolean vBefore : new boolean[]{true, false}) {
@@ -74,14 +77,14 @@
         final RecyclerView recyclerView = new RecyclerView(getActivity()) {
             //@Override
             public boolean canScrollHorizontally(int direction) {
-                return direction < 0 && horizontalScrollBefore ||
-                        direction > 0 && horizontalScrollAfter;
+                return direction < 0 && mHorizontalScrollBefore ||
+                        direction > 0 && mHorizontalScrollAfter;
             }
 
             //@Override
             public boolean canScrollVertically(int direction) {
-                return direction < 0 && verticalScrollBefore ||
-                        direction > 0 && verticalScrollAfter;
+                return direction < 0 && mVerticalScrollBefore ||
+                        direction > 0 && mVerticalScrollAfter;
             }
         };
         final TestAdapter adapter = new TestAdapter(10);
@@ -104,7 +107,7 @@
 
             @Override
             public boolean canScrollVertically() {
-                return verticalScrollAfter || verticalScrollBefore;
+                return mVerticalScrollAfter || mVerticalScrollBefore;
             }
 
             @Override
@@ -131,37 +134,39 @@
 
             @Override
             public boolean canScrollHorizontally() {
-                return horizontalScrollAfter || horizontalScrollBefore;
+                return mHorizontalScrollAfter || mHorizontalScrollBefore;
             }
         });
         setRecyclerView(recyclerView);
         final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
                 .getCompatAccessibilityDelegate();
         final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info);
             }
         });
-        assertEquals(horizontalScrollAfter || horizontalScrollBefore
-                || verticalScrollAfter || verticalScrollBefore, info.isScrollable());
-        assertEquals(horizontalScrollBefore || verticalScrollBefore,
+        assertEquals(mHorizontalScrollAfter || mHorizontalScrollBefore
+                || mVerticalScrollAfter || mVerticalScrollBefore, info.isScrollable());
+        assertEquals(mHorizontalScrollBefore || mVerticalScrollBefore,
                 (info.getActions() & AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD) != 0);
-        assertEquals(horizontalScrollAfter || verticalScrollAfter,
+        assertEquals(mHorizontalScrollAfter || mVerticalScrollAfter,
                 (info.getActions() & AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD) != 0);
-        final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo = info
-                .getCollectionInfo();
-        assertNotNull(collectionInfo);
-        if (recyclerView.getLayoutManager().canScrollVertically()) {
-            assertEquals(adapter.getItemCount(), collectionInfo.getRowCount());
-        }
-        if (recyclerView.getLayoutManager().canScrollHorizontally()) {
-            assertEquals(adapter.getItemCount(), collectionInfo.getColumnCount());
+        if (SUPPORTS_COLLECTION_INFO) {
+            final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo = info
+                    .getCollectionInfo();
+            assertNotNull(collectionInfo);
+            if (recyclerView.getLayoutManager().canScrollVertically()) {
+                assertEquals(adapter.getItemCount(), collectionInfo.getRowCount());
+            }
+            if (recyclerView.getLayoutManager().canScrollHorizontally()) {
+                assertEquals(adapter.getItemCount(), collectionInfo.getColumnCount());
+            }
         }
 
         final AccessibilityEvent event = AccessibilityEvent.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityEvent(recyclerView, event);
@@ -169,38 +174,40 @@
         });
         final AccessibilityRecordCompat record = AccessibilityEventCompat
                 .asRecord(event);
-        assertEquals(record.isScrollable(), verticalScrollAfter || horizontalScrollAfter ||
-                verticalScrollBefore || horizontalScrollBefore);
+        assertEquals(record.isScrollable(), mVerticalScrollAfter || mHorizontalScrollAfter ||
+                mVerticalScrollBefore || mHorizontalScrollBefore);
         assertEquals(record.getItemCount(), adapter.getItemCount());
 
         getInstrumentation().waitForIdleSync();
-        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
-            final View view = mRecyclerView.getChildAt(i);
-            final AccessibilityNodeInfoCompat childInfo = AccessibilityNodeInfoCompat.obtain();
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    delegateCompat.getItemDelegate().
-                            onInitializeAccessibilityNodeInfo(view, childInfo);
+        if (SUPPORTS_COLLECTION_INFO) {
+            for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
+                final View view = mRecyclerView.getChildAt(i);
+                final AccessibilityNodeInfoCompat childInfo = AccessibilityNodeInfoCompat.obtain();
+                mActivityRule.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        delegateCompat.getItemDelegate().
+                                onInitializeAccessibilityNodeInfo(view, childInfo);
+                    }
+                });
+                final AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfo
+                        = childInfo.getCollectionItemInfo();
+                assertNotNull(collectionItemInfo);
+                if (recyclerView.getLayoutManager().canScrollHorizontally()) {
+                    assertEquals(i, collectionItemInfo.getColumnIndex());
+                } else {
+                    assertEquals(0, collectionItemInfo.getColumnIndex());
                 }
-            });
-            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfo
-                    = childInfo.getCollectionItemInfo();
-            assertNotNull(collectionItemInfo);
-            if (recyclerView.getLayoutManager().canScrollHorizontally()) {
-                assertEquals(i, collectionItemInfo.getColumnIndex());
-            } else {
-                assertEquals(0, collectionItemInfo.getColumnIndex());
-            }
 
-            if (recyclerView.getLayoutManager().canScrollVertically()) {
-                assertEquals(i, collectionItemInfo.getRowIndex());
-            } else {
-                assertEquals(0, collectionItemInfo.getRowIndex());
+                if (recyclerView.getLayoutManager().canScrollVertically()) {
+                    assertEquals(i, collectionItemInfo.getRowIndex());
+                } else {
+                    assertEquals(0, collectionItemInfo.getRowIndex());
+                }
             }
         }
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
 
@@ -212,8 +219,8 @@
         vScrolledBack.set(false);
         performAccessibilityAction(delegateCompat, recyclerView,
                 AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-        assertEquals(horizontalScrollBefore, hScrolledBack.get());
-        assertEquals(verticalScrollBefore, vScrolledBack.get());
+        assertEquals(mHorizontalScrollBefore, hScrolledBack.get());
+        assertEquals(mVerticalScrollBefore, vScrolledBack.get());
         assertEquals(false, hScrolledFwd.get());
         assertEquals(false, vScrolledFwd.get());
 
@@ -225,8 +232,8 @@
                 AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
         assertEquals(false, hScrolledBack.get());
         assertEquals(false, vScrolledBack.get());
-        assertEquals(horizontalScrollAfter, hScrolledFwd.get());
-        assertEquals(verticalScrollAfter, vScrolledFwd.get());
+        assertEquals(mHorizontalScrollAfter, hScrolledFwd.get());
+        assertEquals(mVerticalScrollAfter, vScrolledFwd.get());
     }
 
     @Test
@@ -253,7 +260,7 @@
         final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
                 .getCompatAccessibilityDelegate();
         final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info);
@@ -261,7 +268,7 @@
         });
         assertTrue("test sanity", info.isScrollable());
         final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -280,7 +287,7 @@
     boolean performAccessibilityAction(final AccessibilityDelegateCompat delegate,
             final RecyclerView recyclerView, final int action) throws Throwable {
         final boolean[] result = new boolean[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = delegate.performAccessibilityAction(recyclerView, action, null);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 2ddbabf..5379354 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -16,20 +16,31 @@
 
 package android.support.v7.widget;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Rect;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.view.ViewCompat;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -38,7 +49,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import static org.junit.Assert.*;
 
 /**
  * Tests for {@link SimpleItemAnimator} API.
@@ -63,7 +73,7 @@
 
         final RecyclerView.ViewHolder oldVh = mRecyclerView.findViewHolderForAdapterPosition(3);
         assertNotNull("test sanity", oldVh);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 oldVh.itemView.requestFocus();
@@ -118,7 +128,7 @@
         mRecyclerView.setItemAnimator(animator);
         mLayoutManager.expectLayouts(2);
         final RecyclerView.ViewHolder[] updatedVH = new RecyclerView.ViewHolder[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 adapter.notifyItemChanged(0);
@@ -422,6 +432,8 @@
         });
     }
 
+    // Disable this test on ICS because it causes testing devices to freeze.
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
     @Test
     public void dontReuseHiddenViewOnInvalidate() throws Throwable {
         reuseHiddenViewTest(new ReuseTestCallback() {
@@ -540,7 +552,7 @@
         waitForAnimations(2);
         final View[] targetChild = new View[1];
         final LoggingItemAnimator animator = new LoggingItemAnimator();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setItemAnimator(animator);
@@ -550,7 +562,7 @@
 
         assertNotNull("test sanity", targetChild);
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@@ -601,7 +613,7 @@
         final View[] targetChild = new View[1];
         final LoggingItemAnimator animator = new LoggingItemAnimator();
         animator.setRemoveDuration(500);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setItemAnimator(animator);
@@ -620,7 +632,7 @@
 
         mLayoutManager.waitForLayout(2);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // The view is still a child of mRecyclerView, and is invisible for accessibility.
@@ -634,7 +646,7 @@
         waitForAnimations(2);
 
         // Delete animation is now complete.
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // The view is in recycled state, and back to the expected accessibility.
@@ -650,7 +662,7 @@
         mTestAdapter.addAndNotify(1);
         mLayoutManager.waitForLayout(2);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // The view should be reused, and have the expected accessibility.
@@ -664,6 +676,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     public void importantForAccessibilityWhileDetelingAuto() throws Throwable {
         runTestImportantForAccessibilityWhileDeteling(
                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO,
@@ -671,6 +684,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     public void importantForAccessibilityWhileDetelingNo() throws Throwable {
         runTestImportantForAccessibilityWhileDeteling(
                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO,
@@ -678,6 +692,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     public void importantForAccessibilityWhileDetelingNoHideDescandants() throws Throwable {
         runTestImportantForAccessibilityWhileDeteling(
                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
@@ -685,6 +700,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     public void importantForAccessibilityWhileDetelingYes() throws Throwable {
         runTestImportantForAccessibilityWhileDeteling(
                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES,
@@ -899,7 +915,7 @@
             changedIndexNewType.set(defaultType + 1);
         }
         if (deleteSomeItems) {
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     try {
@@ -969,7 +985,7 @@
                 expectedPayloads.add(expectedPayloadsInOnBind[i][j]);
             }
             final Object[] payloadsToSend = notifyPayloads[i];
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     for (int j = 0; j < payloadsToSend.length; j++) {
@@ -1091,7 +1107,7 @@
         setupBasic(10, 3, 4);
         int layoutCount = mLayoutManager.mTotalLayoutCount;
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -1301,7 +1317,7 @@
                 super.onScroll(dx, recycler, state);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mTestAdapter.mItems.remove(5);
@@ -1336,7 +1352,7 @@
                 super.onScroll(dx, recycler, state);
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mTestAdapter.mItems.remove(5);
@@ -1378,6 +1394,8 @@
         mLayoutManager.waitForLayout(2);
     }
 
+    // Run this test on Jelly Bean and newer because hasTransientState was introduced in API 16.
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
     @Test
     public void appCancelAnimationInDetach() throws Throwable {
         final View[] addedView = new View[2];
@@ -1404,7 +1422,7 @@
         int limit = 200;
         while (addedView[0] == null || addedView[1] == null) {
             Thread.sleep(100);
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     if (mRecyclerView.getChildCount() == 3) {
@@ -1463,7 +1481,7 @@
         TestRecyclerView testRecyclerView = getTestRecyclerView();
         mLayoutManager.expectLayouts(1);
         testRecyclerView.expectDraw(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mTestAdapter.mItems.clear();
@@ -1498,7 +1516,7 @@
                 assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4));
             }
         };
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 97f9b92..fff41f5 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
@@ -30,14 +31,15 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 import android.widget.TextView;
 
 import org.junit.Before;
@@ -349,19 +351,67 @@
                 loggingView.getOnSavedInstanceCnt());
     }
 
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void smoothScrollWithCustomInterpolator() {
+        mRecyclerView.setLayoutManager(new MockLayoutManager());
+        mRecyclerView.setAdapter(new MockAdapter(20));
+        Interpolator interpolator = new LinearInterpolator();
+        mRecyclerView.smoothScrollBy(0, 100, interpolator);
+        assertSame(interpolator, mRecyclerView.mViewFlinger.mInterpolator);
+
+        mRecyclerView.smoothScrollBy(0, -100);
+        assertSame(RecyclerView.sQuinticInterpolator, mRecyclerView.mViewFlinger.mInterpolator);
+    }
+
     @Test
     public void prefetchChangesCacheSize() {
+        mRecyclerView.setAdapter(new MockAdapter(20));
         MockLayoutManager mlm = new MockLayoutManager() {
             @Override
-            int getItemPrefetchCount() {
-                return 3;
+            public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                    RecyclerView.PrefetchRegistry prefetchManager) {
+                prefetchManager.addPosition(0, 0);
+                prefetchManager.addPosition(1, 0);
+                prefetchManager.addPosition(2, 0);
             }
         };
+
         RecyclerView.Recycler recycler = mRecyclerView.mRecycler;
         assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
         mRecyclerView.setLayoutManager(mlm);
-        assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
+        assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            // layout, so prefetches can occur
+            mRecyclerView.measure(View.MeasureSpec.EXACTLY | 100, View.MeasureSpec.EXACTLY | 100);
+            mRecyclerView.layout(0, 0, 100, 100);
+
+            // prefetch gets 3 items, so expands cache by 3
+            mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView);
+            assertEquals(3, mRecyclerView.mPrefetchRegistry.mCount);
+            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
+
+            // Reset to default by removing layout
+            mRecyclerView.setLayoutManager(null);
+            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE, recycler.mViewCacheMax);
+
+            // And restore by restoring layout
+            mRecyclerView.setLayoutManager(mlm);
+            assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
+        }
+    }
+
+    @Test
+    public void getNanoTime() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            // check that it looks vaguely time-ish
+            long time = mRecyclerView.getNanoTime();
+            assertNotEquals(0, time);
+            assertNotEquals(time, mRecyclerView.getNanoTime());
+        } else {
+            // expect to avoid cost of system.nanoTime on older platforms that don't do prefetch
+            assertEquals(0, mRecyclerView.getNanoTime());
+        }
     }
 
     static class MockLayoutManager extends RecyclerView.LayoutManager {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
index 0d538ac..755173d 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
@@ -17,10 +17,8 @@
 package android.support.v7.widget;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
@@ -33,11 +31,12 @@
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,20 +44,48 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 @RunWith(AndroidJUnit4.class)
 public class RecyclerViewCacheTest {
-    RecyclerView mRecyclerView;
+    TimeMockingRecyclerView mRecyclerView;
     RecyclerView.Recycler mRecycler;
-    RecyclerView.ViewPrefetcher mViewPrefetcher;
+
+    private class TimeMockingRecyclerView extends RecyclerView {
+        private long mMockNanoTime = 0;
+
+        TimeMockingRecyclerView(Context context) {
+            super(context);
+        }
+
+        public void registerTimePassingMs(long ms) {
+            mMockNanoTime += TimeUnit.MILLISECONDS.toNanos(ms);
+        }
+
+        @Override
+        long getNanoTime() {
+            return mMockNanoTime;
+        }
+    }
 
     @Before
-    public void setUp() throws Exception {
-        mRecyclerView = new RecyclerView(getContext());
+    public void setup() throws Exception {
+        mRecyclerView = new TimeMockingRecyclerView(getContext());
+        mRecyclerView.onAttachedToWindow();
         mRecycler = mRecyclerView.mRecycler;
-        mViewPrefetcher = mRecyclerView.mViewPrefetcher;
+    }
+
+    @After
+    public void teardown() throws Exception {
+        if (mRecyclerView.isAttachedToWindow()) {
+            mRecyclerView.onDetachedFromWindow();
+        }
+        GapWorker gapWorker = GapWorker.sGapWorker.get();
+        if (gapWorker != null) {
+            assertTrue(gapWorker.mRecyclerViews.isEmpty());
+        }
     }
 
     private Context getContext() {
@@ -82,16 +109,11 @@
             }
 
             @Override
-            int getItemPrefetchCount() {
-                return 3;
-            }
-
-            @Override
-            int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
-                outIndices[0] = 0;
-                outIndices[1] = 1;
-                outIndices[2] = 2;
-                return 3;
+            public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                    RecyclerView.PrefetchRegistry prefetchManager) {
+                prefetchManager.addPosition(0, 0);
+                prefetchManager.addPosition(1, 0);
+                prefetchManager.addPosition(2, 0);
             }
 
             @Override
@@ -121,10 +143,7 @@
 
         // Prefetch multiple times...
         for (int i = 0; i < 4; i++) {
-            int[] itemPrefetchArray = new int[] {-1, -1, -1};
-            int viewCount = prefetchingLayoutManager.gatherPrefetchIndices(1, 1,
-                    mRecyclerView.mState, itemPrefetchArray);
-            mRecycler.prefetch(itemPrefetchArray, viewCount);
+            mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
 
             // ...but should only see the same three items fetched/bound once each
             verify(mockAdapter, times(3)).onCreateViewHolder(any(ViewGroup.class), anyInt());
@@ -132,27 +151,7 @@
                     any(RecyclerView.ViewHolder.class), anyInt(), any(List.class));
 
             assertTrue(mRecycler.mCachedViews.size() == 3);
-            verifyCacheContainsPositions(0, 1, 2);
-        }
-    }
-
-    private void verifyCacheDoesNotContainPosition(int position) {
-        for (int i = 0; i < mRecycler.mCachedViews.size(); i++) {
-            assertNotEquals("Cache must not contain position " + position,
-                    position, mRecycler.mCachedViews.get(i).mPosition);
-        }
-    }
-
-    private void verifyCacheContainsPosition(int position) {
-        for (int i = 0; i < mRecycler.mCachedViews.size(); i++) {
-            if (mRecycler.mCachedViews.get(i).mPosition == position) return;
-        }
-        fail("Cache does not contain position " + position);
-    }
-
-    private void verifyCacheContainsPositions(Integer... positions) {
-        for (int i = 0; i < positions.length; i++) {
-            verifyCacheContainsPosition(positions[i]);
+            CacheUtils.verifyCacheContainsPositions(mRecyclerView, 0, 1, 2);
         }
     }
 
@@ -177,23 +176,25 @@
 
         layout(300, 100);
 
-        mViewPrefetcher.mItemPrefetchArray = new int[] { 3, 4, 5 };
-        mRecycler.prefetch(mViewPrefetcher.mItemPrefetchArray, 3);
-        verifyCacheContainsPositions(3, 4, 5);
+        assertEquals(2, mRecyclerView.mRecycler.mViewCacheMax);
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        assertEquals(5, mRecyclerView.mRecycler.mViewCacheMax);
+
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5);
 
         // further views recycled, as though from scrolling, shouldn't evict prefetched views:
         mRecycler.recycleView(mRecycler.getViewForPosition(10));
-        verifyCacheContainsPositions(3, 4, 5, 10);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 10);
 
         mRecycler.recycleView(mRecycler.getViewForPosition(20));
-        verifyCacheContainsPositions(3, 4, 5, 10, 20);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 10, 20);
 
         mRecycler.recycleView(mRecycler.getViewForPosition(30));
-        verifyCacheContainsPositions(3, 4, 5, 20, 30);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 20, 30);
 
         mRecycler.recycleView(mRecycler.getViewForPosition(40));
-        verifyCacheContainsPositions(3, 4, 5, 30, 40);
-
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5, 30, 40);
 
         // After clearing the cache, the prefetch priorities should be cleared as well:
         mRecyclerView.mRecycler.recycleAndClearCachedViews();
@@ -202,7 +203,7 @@
         }
 
         // cache only contains most recent positions, no priority for previous prefetches:
-        verifyCacheContainsPositions(50, 60, 70, 80, 90);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 50, 60, 70, 80, 90);
     }
 
     @Test
@@ -233,24 +234,156 @@
         assertTrue(mRecycler.mCachedViews.isEmpty());
 
         // rows 0, 1, and 2 are all attached and visible. Prefetch row 3:
-        mViewPrefetcher.mItemPrefetchArray = new int[] {-1, -1, -1};
-        int viewCount = mRecyclerView.getLayoutManager().gatherPrefetchIndices(0, 1,
-                mRecyclerView.mState, mViewPrefetcher.mItemPrefetchArray);
-        mRecycler.prefetch(mViewPrefetcher.mItemPrefetchArray, viewCount);
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
 
         // row 3 is cached:
-        verifyCacheContainsPositions(9, 10, 11);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 9, 10, 11);
         assertTrue(mRecycler.mCachedViews.size() == 3);
 
         // Scroll so 1 falls off (though 3 is still not on screen)
         mRecyclerView.scrollBy(0, 50);
 
         // row 3 is still cached, with a couple other recycled views:
-        verifyCacheContainsPositions(9, 10, 11);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 9, 10, 11);
         assertTrue(mRecycler.mCachedViews.size() == 5);
     }
 
     @Test
+    public void prefetchItemsRespectDeadline() {
+        mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3));
+
+        // 100x100 pixel views
+        RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
+            @Override
+            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+                mRecyclerView.registerTimePassingMs(5);
+                View view = new View(getContext());
+                view.setMinimumWidth(100);
+                view.setMinimumHeight(100);
+                return new RecyclerView.ViewHolder(view) {};
+            }
+
+            @Override
+            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+                mRecyclerView.registerTimePassingMs(5);
+            }
+
+            @Override
+            public int getItemCount() {
+                return 100;
+            }
+        };
+        mRecyclerView.setAdapter(adapter);
+
+        layout(300, 300);
+
+        // offset scroll so that no prefetch-able views are directly adjacent to viewport
+        mRecyclerView.scrollBy(0, 50);
+
+        assertTrue(mRecycler.mCachedViews.size() == 0);
+        assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0);
+
+        // Should take 15 ms to inflate, bind, inflate, so give 19 to be safe
+        final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(19);
+
+        // Timed prefetch
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+        mRecyclerView.mGapWorker.prefetch(deadlineNs);
+
+        // will have enough time to inflate/bind one view, and inflate another
+        assertTrue(mRecycler.mCachedViews.size() == 1);
+        assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 1);
+        // Note: order/view below is an implementation detail
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 12);
+
+
+        // Unbounded prefetch this time
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+
+        // Should finish all work
+        assertTrue(mRecycler.mCachedViews.size() == 3);
+        assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 0);
+        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 12, 13, 14);
+    }
+
+    @Test
+    public void prefetchStaggeredItemsPriority() {
+        StaggeredGridLayoutManager sglm =
+                new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
+        mRecyclerView.setLayoutManager(sglm);
+
+        // first view 50x100 pixels, rest are 100x100 so second column is offset
+        mRecyclerView.setAdapter(new RecyclerView.Adapter() {
+            @Override
+            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+                return new RecyclerView.ViewHolder(new View(getContext())) {};
+            }
+
+            @Override
+            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+                holder.itemView.setMinimumWidth(100);
+                holder.itemView.setMinimumHeight(position == 0 ? 50 : 100);
+            }
+
+            @Override
+            public int getItemCount() {
+                return 100;
+            }
+        });
+
+        layout(200, 200);
+
+        /* Each row is 50 pixels:
+         * ------------- *
+         *   0   |   1   *
+         *   2   |   1   *
+         *   2   |   3   *
+         *___4___|___3___*
+         *   4   |   5   *
+         *   6   |   5   *
+         *      ...      *
+         */
+        assertEquals(5, mRecyclerView.getChildCount());
+        assertEquals(0, sglm.getFirstChildPosition());
+        assertEquals(4, sglm.getLastChildPosition());
+
+        // prefetching down shows 5 at 0 pixels away, 6 at 50 pixels away
+        CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, 10,
+                new Integer[] {5, 0}, new Integer[] {6, 50});
+
+        // Prefetch upward shows nothing
+        CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, -10);
+
+        mRecyclerView.scrollBy(0, 100);
+
+        /* Each row is 50 pixels:
+         * ------------- *
+         *   0   |   1   *
+         *___2___|___1___*
+         *   2   |   3   *
+         *   4   |   3   *
+         *   4   |   5   *
+         *___6___|___5___*
+         *   6   |   7   *
+         *   8   |   7   *
+         *      ...      *
+         */
+
+        assertEquals(5, mRecyclerView.getChildCount());
+        assertEquals(2, sglm.getFirstChildPosition());
+        assertEquals(6, sglm.getLastChildPosition());
+
+        // prefetching down shows 7 at 0 pixels away, 8 at 50 pixels away
+        CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, 10,
+                new Integer[] {7, 0}, new Integer[] {8, 50});
+
+        // prefetching up shows 1 is 0 pixels away, 0 at 50 pixels away
+        CacheUtils.verifyPositionsPrefetched(mRecyclerView, 0, -10,
+                new Integer[] {1, 0}, new Integer[] {0, 50});
+    }
+
+    @Test
     public void prefetchItemsSkipAnimations() {
         LinearLayoutManager llm = new LinearLayoutManager(getContext());
         mRecyclerView.setLayoutManager(llm);
@@ -259,7 +392,8 @@
         final RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
             @Override
             public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-                return new RecyclerView.ViewHolder(new View(parent.getContext())) {};
+                return new RecyclerView.ViewHolder(new View(parent.getContext())) {
+                };
             }
 
             @Override
@@ -269,7 +403,9 @@
             }
 
             @Override
-            public int getItemCount() { return 10; }
+            public int getItemCount() {
+                return 10;
+            }
         };
 
         // make move duration long enough to be able to see the effects
@@ -291,25 +427,27 @@
         assertEquals(4, mRecyclerView.getChildCount());
 
         // animating view should be observable as hidden, uncached...
-        verifyCacheDoesNotContainPosition(2);
+        CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 2);
         assertNotNull("Animating view should be found, hidden",
-                mRecyclerView.mChildHelper.findHiddenNonRemovedView(2, RecyclerView.INVALID_TYPE));
+                mRecyclerView.mChildHelper.findHiddenNonRemovedView(2));
+        assertTrue(GapWorker.isPrefetchPositionAttached(mRecyclerView, 2));
 
         // ...but must not be removed for prefetch
-        mViewPrefetcher.mItemPrefetchArray = new int[] {-1};
-        int viewCount = mRecyclerView.getLayoutManager().gatherPrefetchIndices(0, 1,
-                mRecyclerView.mState, mViewPrefetcher.mItemPrefetchArray);
-        mRecycler.prefetch(mViewPrefetcher.mItemPrefetchArray, viewCount);
-        int prefetchTarget = mViewPrefetcher.mItemPrefetchArray[0];
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+
+        assertEquals("Prefetch must target one view", 1, mRecyclerView.mPrefetchRegistry.mCount);
+        int prefetchTarget = mRecyclerView.mPrefetchRegistry.mPrefetchArray[0];
         assertEquals("Prefetch must target view 2", 2, prefetchTarget);
 
         // animating view still observable as hidden, uncached
-        verifyCacheDoesNotContainPosition(2);
+        CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 2);
         assertNotNull("Animating view should be found, hidden",
-                mRecyclerView.mChildHelper.findHiddenNonRemovedView(2, RecyclerView.INVALID_TYPE));
+                mRecyclerView.mChildHelper.findHiddenNonRemovedView(2));
+        assertTrue(GapWorker.isPrefetchPositionAttached(mRecyclerView, 2));
 
         assertTrue(itemAnimator.isRunning());
         assertEquals(2, llm.getChildCount());
         assertEquals(4, mRecyclerView.getChildCount());
     }
-}
\ No newline at end of file
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
index 768f2f1..7319d41 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
@@ -24,8 +24,8 @@
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
 import android.support.v7.recyclerview.test.R;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,7 +53,7 @@
     private final boolean mFocusOnChild;
     private final boolean mDisableRecovery;
 
-    @Parameterized.Parameters(name = "focusSubChild:{0}, disable:{1}")
+    @Parameterized.Parameters(name = "focusSubChild:{0},disable:{1}")
     public static List<Object[]> getParams() {
         return Arrays.asList(
                 new Object[]{false, false},
@@ -118,7 +118,7 @@
         mAdapter.changeAndNotify(3, 1);
         mLayoutManager.waitForLayout(2);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 RecyclerView.ViewHolder newVh = mRecyclerView.findViewHolderForAdapterPosition(3);
@@ -157,7 +157,7 @@
         mLayoutManager.setSupportsPredictive(withAnimation);
         final RecyclerView.ViewHolder oldVh = focusVh(3);
         mLayoutManager.expectLayouts(withAnimation ? 2 : 1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Item item = mAdapter.mItems.get(3);
@@ -200,7 +200,7 @@
         long itemId = oldVh.getItemId();
 
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Item item = mAdapter.mItems.get(4);
@@ -267,7 +267,7 @@
         assertThat("test sanity", mAdapter.hasStableIds(), is(false));
         focusVh(4);
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -304,7 +304,7 @@
         RecyclerView.ViewHolder oldVh = focusVh(3);
         final long itemId = oldVh.getItemId();
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAdapter.mItems.get(3).mType = TYPE_NO_FOCUS;
@@ -352,7 +352,7 @@
         assertFocus(oldVh, true);
         toFocusId.set(mAdapter.mItems.get(5).mId);
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mAdapter.mItems.get(3).mType += 2;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index 770ab71..e861dbb 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -51,11 +51,11 @@
 import android.os.Build;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.util.TouchUtils;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -144,14 +144,14 @@
         rv.setLayoutManager(layoutManager);
         rv.setAdapter(new TestAdapter(10));
         layoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getActivity().getContainer().addView(ll1);
             }
         });
         layoutManager.waitForLayout(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ll1.removeView(ll2);
@@ -168,7 +168,7 @@
         }
         final boolean requireLayout = requestLayoutOnDetach || removeAdapter || removeLayoutManager;
         layoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ll1.addView(ll2);
@@ -242,7 +242,7 @@
 
         final TestLayoutManager replacement = new LayoutAllLayoutManager(true);
         replacement.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 rv.setLayoutManager(replacement);
@@ -307,7 +307,7 @@
         viewBelow.setFocusableInTouchMode(true);
         container.addView(viewBelow);
         tlm.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getActivity().getContainer().addView(container);
@@ -501,7 +501,7 @@
         tlm.waitForLayout(1);
         final View child = recyclerView.getChildAt(0);
         assertThat(child.getScrollY(), CoreMatchers.is(scrollY));
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.requestChildRectangleOnScreen(child, new Rect(3, 4, 5, 6), true);
@@ -544,7 +544,7 @@
 
         setRecyclerView(recyclerView);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getActivity().getContainer().removeView(recyclerView);
@@ -675,7 +675,7 @@
         tlm.expectLayouts(1);
         setRecyclerView(recyclerView);
         tlm.waitForLayout(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 adapter.mItems.remove(9);
@@ -707,7 +707,7 @@
         final RecyclerView.ViewHolder toFocus = recyclerView.findViewHolderForAdapterPosition(9);
         requestFocus(toFocus.itemView, true);
         assertThat("test sanity", toFocus.itemView.hasFocus(), is(true));
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 adapter.mItems.remove(9);
@@ -771,7 +771,7 @@
     public View focusSearch(final ViewGroup parent, final View focused, final int direction)
             throws Throwable {
         final View[] result = new View[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 result[0] = parent.focusSearch(focused, direction);
@@ -896,7 +896,7 @@
         tlm.waitForLayout(1);
         // ready
         tlm.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -1306,7 +1306,7 @@
 
     private boolean fling(final int velocityX, final int velocityY) throws Throwable {
         final AtomicBoolean didStart = new AtomicBoolean(false);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean result = mRecyclerView.fling(velocityX, velocityY);
@@ -1326,7 +1326,7 @@
         final TestAdapter testAdapter = new TestAdapter(10);
         setupBasic(recyclerView, layoutManager, testAdapter, false);
         layoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -1443,8 +1443,7 @@
         final List<View> recycled = new ArrayList<>();
         TestAdapter testAdapter = new TestAdapter(10) {
             @Override
-            public boolean onFailedToRecycleView(
-                    TestViewHolder holder) {
+            public boolean onFailedToRecycleView(TestViewHolder holder) {
                 failedToRecycle.add(holder.itemView);
                 if (unsetTransientState) {
                     setHasTransientState(holder.itemView, false);
@@ -1506,7 +1505,7 @@
         tlm.expectLayouts(1);
         setRecyclerView(recyclerView);
         tlm.waitForLayout(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 for (int i = 0; i < tlm.getChildCount(); i++) {
@@ -1627,7 +1626,7 @@
         setRecyclerView(rv);
         tlm.waitForLayout(1);
         final RecyclerView.ViewHolder vh = rv.getChildViewHolder(rv.getChildAt(0));
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 ViewCompat.setHasTransientState(vh.itemView, true);
@@ -1726,7 +1725,7 @@
         testAdapter.changeAndNotify(2, 1);
         tlm.waitForLayout(2);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 assertThat("test sanity", recyclerView.getChildCount(), CoreMatchers.is(11));
@@ -1846,7 +1845,7 @@
         tlm.expectLayouts(1);
         setRecyclerView(recyclerView);
         tlm.waitForLayout(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -1911,7 +1910,7 @@
                 latch.countDown();
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollBy(0, 500);
@@ -1942,14 +1941,14 @@
                 latch.countDown();
             }
         });
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollBy(0, 500);
             }
         });
         latch.await(5, TimeUnit.SECONDS);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.stopScroll();
@@ -1983,7 +1982,7 @@
         final ViewConfiguration vc = ViewConfiguration.get(getActivity());
         final float fling = vc.getScaledMinimumFlingVelocity()
                 + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .1f;
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.fling(0, Math.round(fling));
@@ -2017,14 +2016,14 @@
         final ViewConfiguration vc = ViewConfiguration.get(getActivity());
         final float fling = vc.getScaledMinimumFlingVelocity()
                 + (vc.getScaledMaximumFlingVelocity() - vc.getScaledMinimumFlingVelocity()) * .8f;
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.fling(0, Math.round(fling));
             }
         });
         latch.await(5, TimeUnit.SECONDS);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.stopScroll();
@@ -2088,7 +2087,7 @@
     }
 
     private void sendTouch(final ViewGroup view, final MotionEvent event) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (view.onInterceptTouchEvent(event)) {
@@ -2258,7 +2257,7 @@
         lm.waitForLayout(1);
         // regular scroll
         final int targetPosition = visibleChildCount * (removeItem ? 30 : 4);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 rv.smoothScrollToPosition(targetPosition);
@@ -2272,7 +2271,7 @@
             final int newTarget = targetPosition - 10;
             testAdapter.deleteAndNotify(newTarget + 1, testAdapter.getItemCount() - newTarget - 1);
             final CountDownLatch targetCheck = new CountDownLatch(1);
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     ViewCompat.postOnAnimationDelayed(rv, new Runnable() {
@@ -2341,7 +2340,7 @@
         lm.expectLayouts(1);
         setRecyclerView(rv);
         lm.waitForLayout(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 rv.smoothScrollBy(0, 2000);
@@ -2349,7 +2348,7 @@
         });
         Thread.sleep(250);
         final AtomicInteger scrollAmt = new AtomicInteger();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final int soFar = totalScrolled.get();
@@ -2530,7 +2529,7 @@
         lm.expectLayouts(1);
         setRecyclerView(recyclerView);
         lm.waitForLayout(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 View child1 = lm.findViewByPosition(0);
@@ -2576,7 +2575,7 @@
         Thread.sleep(5000);
         final int pos = 1;
         final View[] ignored = new View[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 View child = lm.findViewByPosition(pos);
@@ -2643,7 +2642,7 @@
         preLayoutData.clear();
         postLayoutData.clear();
         lm.expectLayouts(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 adapter.notifyItemChanged(3, changePayload);
@@ -2772,7 +2771,7 @@
 
     public void addItemDecoration(final RecyclerView recyclerView, final
     RecyclerView.ItemDecoration itemDecoration) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.addItemDecoration(itemDecoration);
@@ -2782,7 +2781,7 @@
 
     public void removeItemDecoration(final RecyclerView recyclerView, final
     RecyclerView.ItemDecoration itemDecoration) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.removeItemDecoration(itemDecoration);
@@ -2791,7 +2790,7 @@
     }
 
     public void invalidateDecorOffsets(final RecyclerView recyclerView) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.invalidateItemDecorations();
@@ -2944,7 +2943,7 @@
         movedView[0] = recyclerView.getChildAt(movedViewFromIndex);
         test.set(true);
         lm.expectLayouts(supportsPredictive ? 2 : 1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 Item item = testAdapter.mItems.remove(movedViewFromIndex);
@@ -3197,7 +3196,7 @@
         lm.waitForLayout(2);
         lm.expectLayouts(1);
         final int prevLayoutCount = layoutCount.get();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -3295,7 +3294,7 @@
         lm.waitForLayout(2);
         validate.set(true);
         lm.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -3533,7 +3532,7 @@
         adapter.mItems.remove(1);
         adapter.dispatchDataSetChanged();
         lm.waitForLayout(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 for (int i = 2; i < 4; i++) {
@@ -3622,7 +3621,7 @@
         };
         recyclerView.setLayoutManager(testLayoutManager);
         testLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 getActivity().getContainer().addView(recyclerView);
@@ -3636,7 +3635,7 @@
                 structureChanged.get());
         Thread.sleep(1000); //wait for other layouts.
         testLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.requestLayout();
@@ -3667,7 +3666,7 @@
     @Test
     public void detachWithoutLayoutManager() throws Throwable {
         final RecyclerView recyclerView = new RecyclerView(getActivity());
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -3743,7 +3742,7 @@
         disappearingPositions.clear();
         // now that item should be moving, invalidate it and delete it.
         enableGetViewTest.set(true);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -3933,7 +3932,7 @@
         tlm.expectLayouts(1);
         tlm.assertNoLayout("test sanity, layout should not run", 1);
         getInstrumentation().waitForIdleSync();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -4128,7 +4127,7 @@
         });
         recyclerView.setAdapter(adapter);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -4208,7 +4207,7 @@
         setRecyclerView(rv);
         tlm.waitForLayout(2);
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 rv.smoothScrollToPosition(150);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
index e397b6e..b3f44a8 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
@@ -21,8 +21,8 @@
 
 import android.os.Build;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,15 +57,10 @@
         }
 
         @Override
-        int getItemPrefetchCount() {
-            return 1;
-        }
-
-        @Override
-        int gatherPrefetchIndices(int dx, int dy, RecyclerView.State state, int[] outIndices) {
+        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+                RecyclerView.PrefetchRegistry prefetchRegistry) {
             prefetchLatch.countDown();
-            outIndices[0] = 6;
-            return 1;
+            prefetchRegistry.addPosition(6, 0);
         }
 
         void waitForPrefetch(int time) throws InterruptedException {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/ScrollToPositionWithAutoMeasure.java b/v7/recyclerview/tests/src/android/support/v7/widget/ScrollToPositionWithAutoMeasure.java
index 4b670fa..f83ede1 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/ScrollToPositionWithAutoMeasure.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/ScrollToPositionWithAutoMeasure.java
@@ -25,8 +25,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -68,7 +68,7 @@
         setRecyclerView(recyclerView);
         getInstrumentation().waitForIdleSync();
         assertThat("Test sanity", recyclerView.getChildCount() > 0, is(true));
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 View lastChild = llm.getChildAt(llm.getChildCount() - 1);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
index 71484dd..b9bfe40 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerBaseConfigSetTest.java
@@ -17,26 +17,6 @@
 package android.support.v7.widget;
 
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import android.graphics.Rect;
-import android.os.Looper;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.v4.view.ViewCompat;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
 import static android.support.v7.widget.LayoutState.LAYOUT_START;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
@@ -50,6 +30,26 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Rect;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.filters.MediumTest;
+import android.support.v4.view.ViewCompat;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
 @RunWith(Parameterized.class)
 @MediumTest
 public class StaggeredGridLayoutManagerBaseConfigSetTest
@@ -170,7 +170,7 @@
                 * (mConfig.mReverseLayout ? -1 : 1);
 
         final int[] globalPos = new int[1];
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int globalScrollPosition = 0;
@@ -205,7 +205,7 @@
         }
 
         checkForMainThreadException();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 int globalScrollPosition = globalPos[0];
@@ -245,7 +245,7 @@
     }
 
     private void saveRestore(final Config config) throws Throwable {
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -326,11 +326,11 @@
                 );
             }
         };
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
         // case
         final int scrollPosition = mAdapter.getItemCount();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.smoothScrollToPosition(scrollPosition);
@@ -338,7 +338,7 @@
         });
         while (mLayoutManager.isSmoothScrolling() ||
                 mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            runTestOnUiThread(viewInBoundsTest);
+            mActivityRule.runOnUiThread(viewInBoundsTest);
             checkForMainThreadException();
             Thread.sleep(400);
         }
@@ -347,7 +347,7 @@
         mAdapter.deleteAndNotify(0, mAdapter.getItemCount());
         mLayoutManager.waitForLayout(2);
         // test empty case
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         // set a new adapter with huge items to test full bounds check
         mLayoutManager.expectLayouts(1);
         final int totalSpace = mLayoutManager.mPrimaryOrientation.getTotalSpace();
@@ -363,19 +363,19 @@
                 }
             }
         };
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mRecyclerView.setAdapter(newAdapter);
             }
         });
         mLayoutManager.waitForLayout(2);
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         checkForMainThreadException();
 
         // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
         // case
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 final int diff;
@@ -392,7 +392,7 @@
                 }
             }
         });
-        runTestOnUiThread(viewInBoundsTest);
+        mActivityRule.runOnUiThread(viewInBoundsTest);
         checkForMainThreadException();
     }
 
@@ -598,7 +598,7 @@
                     );
                 }
                 mLayoutManager.expectLayouts(1);
-                runTestOnUiThread(new Runnable() {
+                mActivityRule.runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mLayoutManager.scrollToPosition(position);
@@ -756,7 +756,7 @@
 
         final int size = helper.getDecoratedMeasurement(vh.itemView);
         AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
@@ -793,7 +793,7 @@
 
         final int size = helper.getDecoratedMeasurement(vh.itemView);
         AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 if (mConfig.mOrientation == HORIZONTAL) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerCacheTest.java
index e11a576..c15686c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerCacheTest.java
@@ -16,9 +16,15 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,11 +35,6 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
 @RunWith(Parameterized.class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 public class StaggeredGridLayoutManagerCacheTest extends BaseStaggeredGridLayoutManagerTest {
@@ -48,7 +49,7 @@
         mDy = dy;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, dx:{1}, dy: {2}")
+    @Parameterized.Parameters(name = "config:{0},dx:{1},dy:{2}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -95,7 +96,7 @@
         setupByConfig(config);
         waitFirstLayout();
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // pretend to have an extra 5s before next frame so prefetch won't abort early
@@ -109,20 +110,20 @@
         mRecyclerView.setItemViewCacheSize(0);
         {
             mLayoutManager.expectPrefetch(1);
-            runTestOnUiThread(new Runnable() {
+            mActivityRule.runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     mRecyclerView.mRecycler.recycleAndClearCachedViews();
-                    mRecyclerView.mViewPrefetcher.postFromTraversal(mDx, mDy);
+                    mRecyclerView.mGapWorker.postFromTraversal(mRecyclerView, mDx, mDy);
 
                     // Lie about post time, so prefetch executes even if it is delayed
-                    mRecyclerView.mViewPrefetcher.mPostTimeNanos += TimeUnit.SECONDS.toNanos(5);
+                    mRecyclerView.mGapWorker.mPostTimeNs += TimeUnit.SECONDS.toNanos(5);
                 }
             });
             mLayoutManager.waitForPrefetch(1);
         }
 
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 // validate cache state on UI thread
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
index 1a4d225..17299a0 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerGapTest.java
@@ -16,20 +16,21 @@
 
 package android.support.v7.widget;
 
+import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
+
+import static org.junit.Assert.assertNull;
+
+import android.graphics.Rect;
+import android.support.test.filters.MediumTest;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import android.graphics.Rect;
-import android.test.suitebuilder.annotation.MediumTest;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
-import static org.junit.Assert.assertNull;
-
 @RunWith(Parameterized.class)
 @MediumTest
 public class StaggeredGridLayoutManagerGapTest extends BaseStaggeredGridLayoutManagerTest {
@@ -43,7 +44,7 @@
         mDeleteCount = deleteCount;
     }
 
-    @Parameterized.Parameters(name = "config={0} deletePos={1} deleteCount={2}")
+    @Parameterized.Parameters(name = "config={0},deletePos={1},deleteCount={2}")
     public static List<Object[]> getParams() throws CloneNotSupportedException {
         List<Config> variations = createBaseVariations();
         List<Object[]> params = new ArrayList<>();
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
index 3c9ab73..4757a54 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSavedStateTest.java
@@ -16,23 +16,23 @@
 
 package android.support.v7.widget;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import static org.junit.Assert.assertEquals;
 
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.support.test.filters.LargeTest;
 import android.util.Log;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import static org.junit.Assert.assertEquals;
-
 @RunWith(Parameterized.class)
 @LargeTest
 public class StaggeredGridLayoutManagerSavedStateTest extends BaseStaggeredGridLayoutManagerTest {
@@ -53,8 +53,8 @@
         }
     }
 
-    @Parameterized.Parameters(name = "config={0} waitForLayout={1} loadDataAfterRestore={2}"
-            + " postLayoutRunnable={3}")
+    @Parameterized.Parameters(name = "config={0},waitForLayout={1},loadDataAfterRestore={2}"
+            + ",postLayoutRunnable={3}")
     public static List<Object[]> getParams() throws CloneNotSupportedException {
         List<Config> variations = createBaseVariations();
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
index 51b469d..828ffab 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
@@ -16,11 +16,13 @@
 
 package android.support.v7.widget;
 
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+
 import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 
 import org.junit.Test;
@@ -29,14 +31,9 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertTrue;
-
+@MediumTest
 @RunWith(Parameterized.class)
 public class StaggeredGridLayoutManagerSnappingTest extends BaseStaggeredGridLayoutManagerTest {
 
@@ -48,7 +45,7 @@
         mReverseScroll = reverseScroll;
     }
 
-    @Parameterized.Parameters(name = "config:{0}, reverseScroll:{1}")
+    @Parameterized.Parameters(name = "config:{0},reverseScroll:{1}")
     public static List<Object[]> getParams() {
         List<Object[]> result = new ArrayList<>();
         List<Config> configs = createBaseVariations();
@@ -60,7 +57,6 @@
         return result;
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameViewFixedSize() throws Throwable {
         // This test is a special case for fixed sized children.
@@ -100,7 +96,6 @@
         assertCenterAligned(viewAfterScroll);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -148,7 +143,6 @@
         assertCenterAligned(viewAfterScroll);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -175,8 +169,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-
-    @MediumTest
     @Test
     public void snapOnFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -286,7 +278,7 @@
     private boolean fling(final int velocityX, final int velocityY)
             throws Throwable {
         final AtomicBoolean didStart = new AtomicBoolean(false);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 boolean result = mRecyclerView.fling(velocityX, velocityY);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index 18cae23..64111c5 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-
 package android.support.v7.widget;
 
-
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static android.support.v7.widget.StaggeredGridLayoutManager
         .GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
@@ -39,10 +37,10 @@
 import android.graphics.drawable.StateListDrawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.test.filters.MediumTest;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StateSet;
@@ -60,7 +58,6 @@
 import java.util.Map;
 import java.util.UUID;
 
-
 @MediumTest
 public class StaggeredGridLayoutManagerTest extends BaseStaggeredGridLayoutManagerTest {
     @Test
@@ -68,7 +65,7 @@
         setupByConfig(new Config(VERTICAL, false, 3, GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS));
         waitFirstLayout();
         assertFalse("test sanity", mRecyclerView.isLayoutRequested());
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.onDetachedFromWindow(mRecyclerView, mRecyclerView.mRecycler);
@@ -438,7 +435,7 @@
             }
         };
         mLayoutManager.expectLayouts(2);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 try {
@@ -778,7 +775,7 @@
         assertTrue("Children not laid out", mLayoutManager.collectChildCoordinates().size() > 0);
 
         mLayoutManager.expectLayouts(1);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mLayoutManager.scrollToPositionWithOffset(1, 0);
@@ -797,7 +794,7 @@
         final AccessibilityDelegateCompat delegateCompat = mRecyclerView
                 .getCompatAccessibilityDelegate();
         final AccessibilityEvent event = AccessibilityEvent.obtain();
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 delegateCompat.onInitializeAccessibilityEvent(mRecyclerView, event);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
index 5021ecb..54be644 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
@@ -24,8 +24,8 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 import android.view.View;
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/TestResizingRelayoutWithAutoMeasure.java b/v7/recyclerview/tests/src/android/support/v7/widget/TestResizingRelayoutWithAutoMeasure.java
index b0f90d6..9881653 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/TestResizingRelayoutWithAutoMeasure.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/TestResizingRelayoutWithAutoMeasure.java
@@ -26,11 +26,10 @@
 
 import android.graphics.Rect;
 import android.support.annotation.NonNull;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.filters.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.Toast;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -111,7 +110,7 @@
         recyclerView.waitUntilLayout();
         recyclerView.waitUntilAnimations();
         final Map<Integer, Rect> startPositions = capturePositions(recyclerView);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.measure(
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/WrapContentBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/WrapContentBasicTest.java
index 73362dc..c733dfd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/WrapContentBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/WrapContentBasicTest.java
@@ -22,15 +22,12 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
index 7bce2d5..abd9129 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
@@ -16,21 +16,26 @@
 
 package android.support.v7.widget.helper;
 
-import static android.support.v7.widget.helper.ItemTouchHelper.*;
+import static android.support.v7.widget.helper.ItemTouchHelper.END;
+import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
+import static android.support.v7.widget.helper.ItemTouchHelper.RIGHT;
+import static android.support.v7.widget.helper.ItemTouchHelper.START;
+import static android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
+import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.view.ViewCompat;
+import android.support.v7.util.PollingCheck;
 import android.support.v7.util.TouchUtils;
 import android.support.v7.widget.BaseRecyclerViewInstrumentationTest;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.WrappedRecyclerView;
-import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
-import android.view.View;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -84,7 +89,7 @@
         mWrappedRecyclerView.setLayoutManager(mLayoutManager);
         mCalback = new LoggingCalback(dragDirs, swipeDirs);
         mItemTouchHelper = new LoggingItemTouchHelper(mCalback);
-        runTestOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 mItemTouchHelper.attachToRecyclerView(mWrappedRecyclerView);
@@ -128,15 +133,6 @@
         basicSwipeTest(END, START | END, -getActivity().getWindow().getDecorView().getWidth());
     }
 
-    private void setLayoutDirection(final View view, final int layoutDir) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                ViewCompat.setLayoutDirection(view, layoutDir);
-            }
-        });
-    }
-
     public void basicSwipeTest(int dir, int swipeDirs, int targetX) throws Throwable {
         final RecyclerView recyclerView = setup(0, swipeDirs);
         mLayoutManager.expectLayouts(1);
@@ -146,7 +142,13 @@
         final RecyclerView.ViewHolder target = mRecyclerView
                 .findViewHolderForAdapterPosition(1);
         TouchUtils.dragViewToX(getInstrumentation(), target.itemView, Gravity.CENTER, targetX);
-        Thread.sleep(100); //wait for animation end
+
+        PollingCheck.waitFor(1000, new PollingCheck.PollingCheckCondition() {
+            @Override
+            public boolean canProceed() {
+                return mCalback.getSwipe(target) != null;
+            }
+        });
         final SwipeRecord swipe = mCalback.getSwipe(target);
         assertNotNull(swipe);
         assertEquals(dir, swipe.dir);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
index 934936b..92207dd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
@@ -16,14 +16,16 @@
 
 package android.support.v7.widget.test;
 
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.recyclerview.test.CustomLayoutManager;
@@ -32,26 +34,24 @@
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.StaggeredGridLayoutManager;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class RecyclerViewTest {
 
     @Rule
-    public ActivityTestRule<RecyclerViewTestActivity> mActivityRule
-            = new ActivityTestRule<>(RecyclerViewTestActivity.class);
+    public ActivityTestRule<RecyclerViewTestActivity> mActivityRule =
+            new ActivityTestRule<>(RecyclerViewTestActivity.class);
 
     private void setContentView(final int layoutId) throws Throwable {
         final Activity activity = mActivityRule.getActivity();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 activity.setContentView(layoutId);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTestActivity.java b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTestActivity.java
index a3b20ac..c969867 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTestActivity.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTestActivity.java
@@ -16,11 +16,7 @@
 
 package android.support.v7.widget.test;
 
-
 import android.app.Activity;
-import android.content.Context;
-import android.support.v7.widget.LinearLayoutManager;
-import android.util.AttributeSet;
 
 public class RecyclerViewTestActivity extends Activity {
 
diff --git a/v8/renderscript/Android.mk b/v8/renderscript/Android.mk
deleted file mode 100644
index 8815a0a..0000000
--- a/v8/renderscript/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Don't build the library in unbundled branches.
-ifeq (,$(TARGET_BUILD_APPS))
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += -std=c++11
-
-LOCAL_MODULE := android-support-v8-renderscript
-LOCAL_SDK_VERSION := 23
-LOCAL_SRC_FILES := $(call all-java-files-under, java/src)
-LOCAL_SHARED_ANDROID_LIBRARIES := android-support-annotations
-
-LOCAL_JAVA_LANGUAGE_VERSION := 1.7
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# TODO: Build the tests as an APK here
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
-
-endif
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java b/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
deleted file mode 100644
index 2384518..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
+++ /dev/null
@@ -1,3032 +0,0 @@
-/*
- * Copyright (C) 2008-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.util.Log;
-import android.view.Surface;
-
-/**
- * <p> This class provides the primary method through which data is passed to
- * and from RenderScript kernels.  An Allocation provides the backing store for
- * a given {@link android.support.v8.renderscript.Type}.  </p>
- *
- * <p>An Allocation also contains a set of usage flags that denote how the
- * Allocation could be used. For example, an Allocation may have usage flags
- * specifying that it can be used from a script as well as input to a {@link
- * android.support.v8.renderscript.Sampler}. A developer must synchronize
- * across these different usages using
- * {@link android.support.v8.renderscript.Allocation#syncAll} in
- * order to ensure that different users of the Allocation have a consistent view
- * of memory. For example, in the case where an Allocation is used as the output
- * of one kernel and as Sampler input in a later kernel, a developer must call
- * {@link #syncAll syncAll(Allocation.USAGE_SCRIPT)} prior to launching the
- * second kernel to ensure correctness.
- *
- * <p>An Allocation can be populated with the {@link #copyFrom} routines. For
- * more complex Element types, the {@link #copyFromUnchecked} methods can be
- * used to copy from byte arrays or similar constructs.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about creating an application that uses
- * RenderScript, read the
- * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a>
- * developer guide.</p>
- * </div>
- **/
-public class Allocation extends BaseObj {
-    Type mType;
-    Bitmap mBitmap;
-    int mUsage;
-    int mSize;
-    Allocation mAdaptedAllocation;
-    ByteBuffer mByteBuffer = null;
-    long mByteBufferStride = 0;
-
-    boolean mConstrainedLOD;
-    boolean mConstrainedFace;
-    boolean mConstrainedY;
-    boolean mConstrainedZ;
-    boolean mReadAllowed = true;
-    boolean mWriteAllowed = true;
-    boolean mAutoPadding = false;
-    int mSelectedY;
-    int mSelectedZ;
-    int mSelectedLOD;
-    Type.CubemapFace mSelectedFace = Type.CubemapFace.POSITIVE_X;
-
-    int mCurrentDimX;
-    int mCurrentDimY;
-    int mCurrentDimZ;
-    int mCurrentCount;
-
-    private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) {
-        final Class c = d.getClass();
-        if (!c.isArray()) {
-            throw new RSIllegalArgumentException("Object passed is not an array of primitives.");
-        }
-        final Class cmp = c.getComponentType();
-        if (!cmp.isPrimitive()) {
-            throw new RSIllegalArgumentException("Object passed is not an Array of primitives.");
-        }
-
-        if (cmp == Long.TYPE) {
-            if (checkType) {
-                validateIsInt64();
-                return mType.mElement.mType;
-            }
-            return Element.DataType.SIGNED_64;
-        }
-
-        if (cmp == Integer.TYPE) {
-            if (checkType) {
-                validateIsInt32();
-                return mType.mElement.mType;
-            }
-            return Element.DataType.SIGNED_32;
-        }
-
-        if (cmp == Short.TYPE) {
-            if (checkType) {
-                validateIsInt16();
-                return mType.mElement.mType;
-            }
-            return Element.DataType.SIGNED_16;
-        }
-
-        if (cmp == Byte.TYPE) {
-            if (checkType) {
-                validateIsInt8();
-                return mType.mElement.mType;
-            }
-            return Element.DataType.SIGNED_8;
-        }
-
-        if (cmp == Float.TYPE) {
-            if (checkType) {
-                validateIsFloat32();
-            }
-            return Element.DataType.FLOAT_32;
-        }
-
-        if (cmp == Double.TYPE) {
-            if (checkType) {
-                validateIsFloat64();
-            }
-            return Element.DataType.FLOAT_64;
-        }
-        return null;
-    }
-
-    /*
-     * Hold reference to the shared allocation in compat context
-     * for Incremental Support Lib.
-     */
-    long mIncCompatAllocation;
-    boolean mIncAllocDestroyed;
-    /**
-     * The usage of the Allocation.  These signal to RenderScript where to place
-     * the Allocation in memory.
-     *
-     */
-
-    /**
-     * The Allocation will be bound to and accessed by scripts.
-     */
-    public static final int USAGE_SCRIPT = 0x0001;
-
-    /**
-     * The Allocation will be used as a texture source by one or more graphics
-     * programs.
-     *
-     */
-    public static final int USAGE_GRAPHICS_TEXTURE = 0x0002;
-
-    /**
-     * The Allocation will be used as a {@link android.graphics.SurfaceTexture}
-     * consumer.  This usage will cause the Allocation to be created as
-     * read-only.
-     *
-     */
-    public static final int USAGE_IO_INPUT = 0x0020;
-
-    /**
-     * The Allocation will be used as a {@link android.graphics.SurfaceTexture}
-     * producer.  The dimensions and format of the {@link
-     * android.graphics.SurfaceTexture} will be forced to those of the
-     * Allocation.
-     *
-     */
-    public static final int USAGE_IO_OUTPUT = 0x0040;
-
-    /**
-     * The Allocation's backing store will be inherited from another object
-     * (usually a {@link android.graphics.Bitmap}); copying to or from the
-     * original source Bitmap will cause a synchronization rather than a full
-     * copy.  {@link #syncAll} may also be used to synchronize the Allocation
-     * and the source Bitmap.
-     *
-     * <p>This is set by default for allocations created with {@link
-     * #createFromBitmap} in API version 18 and higher.</p>
-     *
-     */
-    public static final int USAGE_SHARED = 0x0080;
-
-    /**
-     * Controls mipmap behavior when using the bitmap creation and update
-     * functions.
-     */
-    public enum MipmapControl {
-        /**
-         * No mipmaps will be generated and the type generated from the incoming
-         * bitmap will not contain additional LODs.
-         */
-        MIPMAP_NONE(0),
-
-        /**
-         * A full mipmap chain will be created in script memory.  The Type of
-         * the Allocation will contain a full mipmap chain.  On upload, the full
-         * chain will be transferred.
-         */
-        MIPMAP_FULL(1),
-
-        /**
-         * The Type of the Allocation will be the same as MIPMAP_NONE.  It will
-         * not contain mipmaps.  On upload, the allocation data will contain a
-         * full mipmap chain generated from the top level in script memory.
-         */
-        MIPMAP_ON_SYNC_TO_TEXTURE(2);
-
-        int mID;
-        MipmapControl(int id) {
-            mID = id;
-        }
-    }
-
-    /**
-     * Getter & Setter for the dummy allocation for Inc Support Lib.
-     *
-     */
-    public long getIncAllocID() {
-        return mIncCompatAllocation;
-    }
-    public void setIncAllocID(long id) {
-        mIncCompatAllocation = id;
-    }
-
-    private long getIDSafe() {
-        if (mAdaptedAllocation != null) {
-            return mAdaptedAllocation.getID(mRS);
-        }
-        return getID(mRS);
-    }
-
-
-   /**
-     * Get the {@link android.support.v8.renderscript.Element} of the {@link
-     * android.support.v8.renderscript.Type} of the Allocation.
-     *
-     * @return Element
-     *
-     */
-    public Element getElement() {
-        return mType.getElement();
-    }
-
-    /**
-     * Get the usage flags of the Allocation.
-     *
-     * @return usage this Allocation's set of the USAGE_* flags OR'd together
-     *
-     */
-    public int getUsage() {
-        return mUsage;
-    }
-
-    /**
-     * Specifies the mapping between the Allocation's cells and an array's elements
-     * when data is copied from the Allocation to the array, or vice-versa.
-     *
-     * Only applies to an Allocation whose Element is a vector of length 3 (such as
-     * {@link Element#U8_3} or {@link Element#RGB_888}). Enabling this feature may make
-     * copying data from the Allocation to an array or vice-versa less efficient.
-     *
-     * <p> Vec3 Element cells are stored in an Allocation as Vec4 Element cells with
-     * the same {@link android.support.v8.renderscript.Element.DataType}, with the fourth vector
-     * component treated as padding. When this feature is enabled, only the data components,
-     * i.e. the first 3 vector components of each cell, will be mapped between the array
-     * and the Allocation. When disabled, explicit mapping of the padding components
-     * is required, as described in the following example.
-     *
-     * <p> For example, when copying an integer array to an Allocation of two {@link
-     * Element#I32_3} cells using {@link #copyFrom(int[])}:
-     * <p> When disabled:
-     *     The array must have at least 8 integers, with the first 4 integers copied
-     *     to the first cell of the Allocation, and the next 4 integers copied to
-     *     the second cell. The 4th and 8th integers are mapped as the padding components.
-     *
-     * <p> When enabled:
-     *     The array just needs to have at least 6 integers, with the first 3 integers
-     *     copied to the the first cell as data components, and the next 3 copied to
-     *     the second cell. There is no mapping for the padding components.
-     *
-     * <p> Similarly, when copying a byte array to an Allocation of two {@link
-     * Element#I32_3} cells, using {@link #copyFromUnchecked(int[])}:
-     * <p> When disabled:
-     *     The array must have at least 32 bytes, with the first 16 bytes copied
-     *     to the first cell of the Allocation, and the next 16 bytes copied to
-     *     the second cell. The 13th-16th and 29th-32nd bytes are mapped as padding
-     *     components.
-     *
-     * <p> When enabled:
-     *     The array just needs to have at least 24 bytes, with the first 12 bytes copied
-     *     to the first cell of the Allocation, and the next 12 bytes copied to
-     *     the second cell. There is no mapping for the padding components.
-     *
-     * <p> Similar to copying data to an Allocation from an array, when copying data from an
-     * Allocation to an array, the padding components for Vec3 Element cells will not be
-     * copied/mapped to the array if AutoPadding is enabled.
-     *
-     * <p> Default: Disabled.
-     *
-     * @param useAutoPadding True: enable AutoPadding; False: disable AutoPadding
-     *
-     */
-    public void setAutoPadding(boolean useAutoPadding) {
-        mAutoPadding = useAutoPadding;
-    }
-
-    /**
-     * Get the size of the Allocation in bytes.
-     *
-     * @return size of the Allocation in bytes.
-     *
-     */
-    public int getBytesSize() {
-        if (mType.mDimYuv != 0) {
-            return (int)Math.ceil(mType.getCount() * mType.getElement().getBytesSize() * 1.5);
-        }
-        return mType.getCount() * mType.getElement().getBytesSize();
-    }
-
-    private void updateCacheInfo(Type t) {
-        mCurrentDimX = t.getX();
-        mCurrentDimY = t.getY();
-        mCurrentDimZ = t.getZ();
-        mCurrentCount = mCurrentDimX;
-        if (mCurrentDimY > 1) {
-            mCurrentCount *= mCurrentDimY;
-        }
-        if (mCurrentDimZ > 1) {
-            mCurrentCount *= mCurrentDimZ;
-        }
-    }
-
-    private void setBitmap(Bitmap b) {
-        mBitmap = b;
-    }
-
-    Allocation(long id, RenderScript rs, Type t, int usage) {
-        super(id, rs);
-        if ((usage & ~(USAGE_SCRIPT |
-                       USAGE_GRAPHICS_TEXTURE |
-                       USAGE_IO_INPUT |
-                       USAGE_IO_OUTPUT |
-                       USAGE_SHARED)) != 0) {
-            throw new RSIllegalArgumentException("Unknown usage specified.");
-        }
-
-        if ((usage & USAGE_IO_INPUT) != 0) {
-            mWriteAllowed = false;
-
-            if ((usage & ~(USAGE_IO_INPUT |
-                           USAGE_GRAPHICS_TEXTURE |
-                           USAGE_SCRIPT)) != 0) {
-                throw new RSIllegalArgumentException("Invalid usage combination.");
-            }
-        }
-
-        mType = t;
-        mUsage = usage;
-        mIncCompatAllocation = 0;
-        mIncAllocDestroyed = false;
-
-        if (t != null) {
-            // TODO: A3D doesn't have Type info during creation, so we can't
-            // calculate the size ahead of time. We can possibly add a method
-            // to update the size in the future if it seems reasonable.
-            mSize = mType.getCount() * mType.getElement().getBytesSize();
-            updateCacheInfo(t);
-        }
-        if (RenderScript.sUseGCHooks == true) {
-            try {
-                RenderScript.registerNativeAllocation.invoke(RenderScript.sRuntime, mSize);
-            } catch (Exception e) {
-                Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e);
-                throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e);
-            }
-        }
-    }
-
-    protected void finalize() throws Throwable {
-        if (RenderScript.sUseGCHooks == true) {
-            RenderScript.registerNativeFree.invoke(RenderScript.sRuntime, mSize);
-        }
-        super.finalize();
-    }
-
-    private void validateIsInt64() {
-        if ((mType.mElement.mType == Element.DataType.SIGNED_64) ||
-            (mType.mElement.mType == Element.DataType.UNSIGNED_64)) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "64 bit integer source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsInt32() {
-        if ((mType.mElement.mType == Element.DataType.SIGNED_32) ||
-            (mType.mElement.mType == Element.DataType.UNSIGNED_32)) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "32 bit integer source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsInt16() {
-        if ((mType.mElement.mType == Element.DataType.SIGNED_16) ||
-            (mType.mElement.mType == Element.DataType.UNSIGNED_16)) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "16 bit integer source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsInt8() {
-        if ((mType.mElement.mType == Element.DataType.SIGNED_8) ||
-            (mType.mElement.mType == Element.DataType.UNSIGNED_8)) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "8 bit integer source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsFloat32() {
-        if (mType.mElement.mType == Element.DataType.FLOAT_32) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "32 bit float source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsFloat64() {
-        if (mType.mElement.mType == Element.DataType.FLOAT_64) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "64 bit float source does not match allocation type " + mType.mElement.mType);
-    }
-
-    private void validateIsObject() {
-        if ((mType.mElement.mType == Element.DataType.RS_ELEMENT) ||
-            (mType.mElement.mType == Element.DataType.RS_TYPE) ||
-            (mType.mElement.mType == Element.DataType.RS_ALLOCATION) ||
-            (mType.mElement.mType == Element.DataType.RS_SAMPLER) ||
-            (mType.mElement.mType == Element.DataType.RS_SCRIPT)) {
-            return;
-        }
-        throw new RSIllegalArgumentException(
-            "Object source does not match allocation type " + mType.mElement.mType);
-    }
-
-    /**
-     * Get the {@link android.support.v8.renderscript.Type} of the Allocation.
-     *
-     * @return Type
-     *
-     */
-    public Type getType() {
-        return mType;
-    }
-
-    /**
-     * Propagate changes from one usage of the Allocation to the
-     * other usages of the Allocation.
-     *
-     */
-    public void syncAll(int srcLocation) {
-        switch (srcLocation) {
-        case USAGE_SCRIPT:
-        case USAGE_GRAPHICS_TEXTURE:
-            break;
-        default:
-            throw new RSIllegalArgumentException("Source must be exactly one usage type.");
-        }
-        mRS.validate();
-        mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
-    }
-
-    /**
-     * Send a buffer to the output stream.  The contents of the Allocation will
-     * be undefined after this operation. This operation is only valid if {@link
-     * #USAGE_IO_OUTPUT} is set on the Allocation.
-     *
-     *
-     */
-    public void ioSend() {
-        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
-            throw new RSIllegalArgumentException(
-                "Can only send buffer if IO_OUTPUT usage specified.");
-        }
-        mRS.validate();
-        mRS.nAllocationIoSend(getID(mRS));
-    }
-
-    /**
-     * Delete once code is updated.
-     */
-    public void ioSendOutput() {
-        ioSend();
-    }
-    /**
-     * Gets or creates a ByteBuffer that contains the raw data of the current Allocation.
-     * <p> If the Allocation is created with USAGE_IO_INPUT, the returned ByteBuffer
-     * would contain the up-to-date data as READ ONLY.
-     * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
-     * the Allocation has certain alignment. The size of each row including padding,
-     * called stride, can be queried using the {@link #getStride()} method.
-     *
-     * Note: Operating on the ByteBuffer of a destroyed Allocation will triger errors.
-     *       The ByteBuffer will be Read-Only for devices before Lollopop (API 21).
-     *
-     * @return ByteBuffer The ByteBuffer associated with raw data pointer of the Allocation.
-     */
-    public ByteBuffer getByteBuffer() {
-        int xBytesSize = mType.getX() * mType.getElement().getBytesSize();
-        // When running on devices before L, we need to construct the ByteBuffer
-        // and explicitly copy the data from the allocation to it.
-        if (mRS.getDispatchAPILevel() < 21) {
-            byte[] data = null;
-            if (mType.getZ() > 0) {
-                // TODO: add support for 3D allocations.
-                return null;
-            } else if (mType.getY() > 0) {
-                // 2D Allocation
-                data = new byte[xBytesSize * mType.getY()];
-                copy2DRangeToUnchecked(0, 0, mType.getX(), mType.getY(), data,
-                                       Element.DataType.SIGNED_8, xBytesSize * mType.getY());
-            } else {
-                // 1D Allocation
-                data = new byte[xBytesSize];
-                copy1DRangeToUnchecked(0, mType.getX(), data);
-            }
-            ByteBuffer bBuffer = ByteBuffer.wrap(data).asReadOnlyBuffer();
-            mByteBufferStride = xBytesSize;
-            return bBuffer;
-        }
-        // Create a new ByteBuffer if it is not initialized or using IO_INPUT.
-        if (mByteBuffer == null || (mUsage & USAGE_IO_INPUT) != 0) {
-            mByteBuffer = mRS.nAllocationGetByteBuffer(getID(mRS), xBytesSize, mType.getY(), mType.getZ());
-        }
-        return mByteBuffer;
-    }
-
-    /**
-     * Gets the stride of the Allocation.
-     * For a 2D or 3D Allocation, the raw data maybe padded so that each row of
-     * the Allocation has certain alignment. The size of each row including such
-     * padding is called stride.
-     *
-     * @return the stride. For 1D Allocation, the stride will be the number of
-     *         bytes of this Allocation. For 2D and 3D Allocations, the stride
-     *         will be the stride in X dimension measuring in bytes.
-     */
-    public long getStride() {
-        if (mByteBufferStride ==0) {
-            if (mRS.getDispatchAPILevel() > 21) {
-                mByteBufferStride = mRS.nAllocationGetStride(getID(mRS));
-            } else {
-                mByteBufferStride = mType.getX() * mType.getElement().getBytesSize();
-            }
-        }
-        return mByteBufferStride;
-    }
-
-    /**
-     * Receive the latest input into the Allocation. This operation
-     * is only valid if {@link #USAGE_IO_INPUT} is set on the Allocation.
-     *
-     */
-    public void ioReceive() {
-        if ((mUsage & USAGE_IO_INPUT) == 0) {
-            throw new RSIllegalArgumentException(
-                "Can only receive if IO_INPUT usage specified.");
-        }
-        mRS.validate();
-        mRS.nAllocationIoReceive(getID(mRS));
-    }
-
-    /**
-     * Copy an array of RS objects to the Allocation.
-     *
-     * @param d Source array.
-     */
-    public void copyFrom(BaseObj[] d) {
-        mRS.validate();
-        validateIsObject();
-        if (d.length != mCurrentCount) {
-            throw new RSIllegalArgumentException("Array size mismatch, allocation sizeX = " +
-                                                 mCurrentCount + ", array length = " + d.length);
-        }
-
-        if (RenderScript.sPointerSize == 8) {
-            long i[] = new long[d.length * 4];
-            for (int ct=0; ct < d.length; ct++) {
-                i[ct * 4] = d[ct].getID(mRS);
-            }
-            copy1DRangeFromUnchecked(0, mCurrentCount, i);
-        } else {
-            int i[] = new int[d.length];
-            for (int ct=0; ct < d.length; ct++) {
-                i[ct] = (int)d[ct].getID(mRS);
-            }
-            copy1DRangeFromUnchecked(0, mCurrentCount, i);
-        }
-    }
-
-    private void validateBitmapFormat(Bitmap b) {
-        Bitmap.Config bc = b.getConfig();
-        if (bc == null) {
-            throw new RSIllegalArgumentException("Bitmap has an unsupported format for this operation");
-        }
-        switch (bc) {
-        case ALPHA_8:
-            if (mType.getElement().mKind != Element.DataKind.PIXEL_A) {
-                throw new RSIllegalArgumentException("Allocation kind is " +
-                                                     mType.getElement().mKind + ", type " +
-                                                     mType.getElement().mType +
-                                                     " of " + mType.getElement().getBytesSize() +
-                                                     " bytes, passed bitmap was " + bc);
-            }
-            break;
-        case ARGB_8888:
-            if ((mType.getElement().mKind != Element.DataKind.PIXEL_RGBA) ||
-                (mType.getElement().getBytesSize() != 4)) {
-                throw new RSIllegalArgumentException("Allocation kind is " +
-                                                     mType.getElement().mKind + ", type " +
-                                                     mType.getElement().mType +
-                                                     " of " + mType.getElement().getBytesSize() +
-                                                     " bytes, passed bitmap was " + bc);
-            }
-            break;
-        case RGB_565:
-            if ((mType.getElement().mKind != Element.DataKind.PIXEL_RGB) ||
-                (mType.getElement().getBytesSize() != 2)) {
-                throw new RSIllegalArgumentException("Allocation kind is " +
-                                                     mType.getElement().mKind + ", type " +
-                                                     mType.getElement().mType +
-                                                     " of " + mType.getElement().getBytesSize() +
-                                                     " bytes, passed bitmap was " + bc);
-            }
-            break;
-        case ARGB_4444:
-            if ((mType.getElement().mKind != Element.DataKind.PIXEL_RGBA) ||
-                (mType.getElement().getBytesSize() != 2)) {
-                throw new RSIllegalArgumentException("Allocation kind is " +
-                                                     mType.getElement().mKind + ", type " +
-                                                     mType.getElement().mType +
-                                                     " of " + mType.getElement().getBytesSize() +
-                                                     " bytes, passed bitmap was " + bc);
-            }
-            break;
-
-        }
-    }
-
-    private void validateBitmapSize(Bitmap b) {
-        if((mCurrentDimX != b.getWidth()) || (mCurrentDimY != b.getHeight())) {
-            throw new RSIllegalArgumentException("Cannot update allocation from bitmap, sizes mismatch");
-        }
-    }
-
-    private void copyFromUnchecked(Object array, Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        if (mCurrentDimZ > 0) {
-            copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
-        } else if (mCurrentDimY > 0) {
-            copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
-        } else {
-            copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
-        }
-    }
-
-    /**
-     * Copy into this Allocation from an array. This method does not guarantee
-     * that the Allocation is compatible with the input buffer; it copies memory
-     * without reinterpretation.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param array The source array
-     */
-    public void copyFromUnchecked(Object array) {
-        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
-                          java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy into this Allocation from an array. This method does not guarantee
-     * that the Allocation is compatible with the input buffer; it copies memory
-     * without reinterpretation.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFromUnchecked(int[] d) {
-        copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array. This method does not guarantee
-     * that the Allocation is compatible with the input buffer; it copies memory
-     * without reinterpretation.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFromUnchecked(short[] d) {
-        copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array. This method does not guarantee
-     * that the Allocation is compatible with the input buffer; it copies memory
-     * without reinterpretation.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFromUnchecked(byte[] d) {
-        copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array. This method does not guarantee
-     * that the Allocation is compatible with the input buffer; it copies memory
-     * without reinterpretation.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFromUnchecked(float[] d) {
-        copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
-    }
-
-
-    /**
-     * Copy into this Allocation from an array.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the array's
-     * primitive type.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param array The source array
-     */
-    public void copyFrom(Object array) {
-        copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
-                          java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy into this Allocation from an array.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 32 bit integer nor a vector of 32 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFrom(int[] d) {
-        validateIsInt32();
-        copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 16 bit integer nor a vector of 16 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFrom(short[] d) {
-        validateIsInt16();
-        copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not an 8 bit integer nor a vector of 8 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFrom(byte[] d) {
-        validateIsInt8();
-        copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy into this Allocation from an array.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector of
-     * 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d the source array
-     */
-    public void copyFrom(float[] d) {
-        validateIsFloat32();
-        copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
-    }
-
-    /**
-     * Copy into an Allocation from a {@link android.graphics.Bitmap}.  The
-     * height, width, and format of the bitmap must match the existing
-     * allocation.
-     *
-     * <p>If the {@link android.graphics.Bitmap} is the same as the {@link
-     * android.graphics.Bitmap} used to create the Allocation with {@link
-     * #createFromBitmap} and {@link #USAGE_SHARED} is set on the Allocation,
-     * this will synchronize the Allocation with the latest data from the {@link
-     * android.graphics.Bitmap}, potentially avoiding the actual copy.</p>
-     *
-     * @param b the source bitmap
-     */
-    public void copyFrom(Bitmap b) {
-        mRS.validate();
-        if (b.getConfig() == null) {
-            Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(newBitmap);
-            c.drawBitmap(b, 0, 0, null);
-            copyFrom(newBitmap);
-            return;
-        }
-        validateBitmapSize(b);
-        validateBitmapFormat(b);
-        mRS.nAllocationCopyFromBitmap(getID(mRS), b);
-    }
-
-    /**
-     * Copy an Allocation from an Allocation.  The types of both allocations
-     * must be identical.
-     *
-     * @param a the source allocation
-     */
-    public void copyFrom(Allocation a) {
-        mRS.validate();
-        if (!mType.equals(a.getType())) {
-            throw new RSIllegalArgumentException("Types of allocations must match.");
-        }
-        copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, a, 0, 0);
-    }
-
-
-    /**
-     * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files and should not be used by developers.
-     *
-     * @param xoff
-     * @param fp
-     */
-    public void setFromFieldPacker(int xoff, FieldPacker fp) {
-        mRS.validate();
-        int eSize = mType.mElement.getBytesSize();
-        final byte[] data = fp.getData();
-        int data_length = fp.getPos();
-
-        int count = data_length / eSize;
-        if ((eSize * count) != data_length) {
-            throw new RSIllegalArgumentException("Field packer length " + data_length +
-                                               " not divisible by element size " + eSize + ".");
-        }
-        copy1DRangeFromUnchecked(xoff, count, data);
-    }
-
-    /**
-     * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files.
-     *
-     * @param xoff
-     * @param component_number
-     * @param fp
-     */
-    public void setFromFieldPacker(int xoff, int component_number, FieldPacker fp) {
-        mRS.validate();
-        if (component_number >= mType.mElement.mElements.length) {
-            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
-        }
-        if(xoff < 0) {
-            throw new RSIllegalArgumentException("Offset must be >= 0.");
-        }
-
-        final byte[] data = fp.getData();
-        int data_length = fp.getPos();
-        int eSize = mType.mElement.mElements[component_number].getBytesSize();
-        eSize *= mType.mElement.mArraySizes[component_number];
-
-        if (data_length != eSize) {
-            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
-                                               " does not match component size " + eSize + ".");
-        }
-
-        mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD,
-                                     component_number, data, data_length);
-    }
-
-    /**
-     * @hide
-     * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files.
-     *
-     * @param xoff
-     * @param yoff
-     * @param zoff
-     * @param component_number
-     * @param fp
-     */
-    /*
-    public void setFromFieldPacker(int xoff, int yoff, int zoff, int component_number, FieldPacker fp) {
-        mRS.validate();
-        if (component_number >= mType.mElement.mElements.length) {
-            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
-        }
-        if(xoff < 0) {
-            throw new RSIllegalArgumentException("Offset x must be >= 0.");
-        }
-        if(yoff < 0) {
-            throw new RSIllegalArgumentException("Offset y must be >= 0.");
-        }
-        if(zoff < 0) {
-            throw new RSIllegalArgumentException("Offset z must be >= 0.");
-        }
-
-        final byte[] data = fp.getData();
-        int data_length = fp.getPos();
-        int eSize = mType.mElement.mElements[component_number].getBytesSize();
-        eSize *= mType.mElement.mArraySizes[component_number];
-
-        if (data_length != eSize) {
-            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
-                                               " does not match component size " + eSize + ".");
-        }
-
-        mRS.nAllocationElementData(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                                   component_number, data, data_length);
-    }
-    */
-
-    private void data1DChecks(int off, int count, int len, int dataSize, boolean usePadding) {
-        mRS.validate();
-        if(off < 0) {
-            throw new RSIllegalArgumentException("Offset must be >= 0.");
-        }
-        if(count < 1) {
-            throw new RSIllegalArgumentException("Count must be >= 1.");
-        }
-        if((off + count) > mCurrentCount) {
-            throw new RSIllegalArgumentException("Overflow, Available count " + mCurrentCount +
-                                               ", got " + count + " at offset " + off + ".");
-        }
-        if(usePadding) {
-            if(len < dataSize / 4 * 3) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        } else {
-            if(len < dataSize) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        }
-    }
-
-    /**
-     * Generate a mipmap chain. This is only valid if the Type of the Allocation
-     * includes mipmaps.
-     *
-     * <p>This function will generate a complete set of mipmaps from the top
-     * level LOD and place them into the script memory space.</p>
-     *
-     * <p>If the Allocation is also using other memory spaces, a call to {@link
-     * #syncAll syncAll(Allocation.USAGE_SCRIPT)} is required.</p>
-     */
-    public void generateMipmaps() {
-        mRS.nAllocationGenerateMipmaps(getID(mRS));
-    }
-
-    private void copy1DRangeFromUnchecked(int off, int count, Object array,
-                                          Element.DataType dt, int arrayLen) {
-        final int dataSize = mType.mElement.getBytesSize() * count;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
-        }
-        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
-        mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
-                              mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param array The source array
-     */
-    public void copy1DRangeFromUnchecked(int off, int count, Object array) {
-        copy1DRangeFromUnchecked(off, count, array,
-                                 validateObjectIsPrimitiveArray(array, false),
-                                 java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
-        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
-        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
-        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
-        copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.FLOAT_32, d.length);
-    }
-
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the component type
-     * of the array passed in.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param array The source array.
-     */
-    public void copy1DRangeFrom(int off, int count, Object array) {
-        copy1DRangeFromUnchecked(off, count, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not an 32 bit integer nor a vector of 32 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFrom(int off, int count, int[] d) {
-        validateIsInt32();
-        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not an 16 bit integer nor a vector of 16 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFrom(int off, int count, short[] d) {
-        validateIsInt16();
-        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not an 8 bit integer nor a vector of 8 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeFrom(int off, int count, byte[] d) {
-        validateIsInt8();
-        copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy an array into a 1D region of this Allocation.  This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector of
-     * 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array.
-     */
-    public void copy1DRangeFrom(int off, int count, float[] d) {
-        validateIsFloat32();
-        copy1DRangeFromUnchecked(off, count, d, Element.DataType.FLOAT_32, d.length);
-    }
-
-     /**
-     * Copy part of an Allocation into this Allocation.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param data the source data allocation.
-     * @param dataOff off The offset of the first element in data to
-     *          be copied.
-     */
-    public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
-        mRS.nAllocationData2D(getIDSafe(), off, 0,
-                              mSelectedLOD, mSelectedFace.mID,
-                              count, 1, data.getID(mRS), dataOff, 0,
-                              data.mSelectedLOD, data.mSelectedFace.mID);
-    }
-
-    private void validate2DRange(int xoff, int yoff, int w, int h) {
-        if (mAdaptedAllocation != null) {
-
-        } else {
-
-            if (xoff < 0 || yoff < 0) {
-                throw new RSIllegalArgumentException("Offset cannot be negative.");
-            }
-            if (h < 0 || w < 0) {
-                throw new RSIllegalArgumentException("Height or width cannot be negative.");
-            }
-            if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY)) {
-                throw new RSIllegalArgumentException("Updated region larger than allocation.");
-            }
-        }
-    }
-
-    void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, Object array,
-                                  Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        final int dataSize = mType.mElement.getBytesSize() * w * h;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        }
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
-                              array, sizeBytes, dt,
-                              mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy from an array into a rectangular region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the input data type.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param array Data to be placed into the Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, Object array) {
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy from an array into a rectangular region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not an 8 bit integer nor a vector of 8 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param data to be placed into the Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
-        validateIsInt8();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
-                                 Element.DataType.SIGNED_8, data.length);
-    }
-
-    /**
-     * Copy from an array into a rectangular region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 16 bit integer nor a vector of 16 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param data to be placed into the Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
-        validateIsInt16();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
-                                 Element.DataType.SIGNED_16, data.length);
-    }
-
-    /**
-     * Copy from an array into a rectangular region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 32 bit integer nor a vector of 32 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param data to be placed into the Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
-        validateIsInt32();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
-                                 Element.DataType.SIGNED_32, data.length);
-    }
-
-    /**
-     * Copy from an array into a rectangular region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector of
-     * 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param data to be placed into the Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
-        validateIsFloat32();
-        copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
-                                 Element.DataType.FLOAT_32, data.length);
-    }
-
-    /**
-     * Copy a rectangular region from an Allocation into a rectangular region in
-     * this Allocation.
-     *
-     * @param xoff X offset of the region in this Allocation
-     * @param yoff Y offset of the region in this Allocation
-     * @param w Width of the region to update.
-     * @param h Height of the region to update.
-     * @param data source Allocation.
-     * @param dataXoff X offset in source Allocation
-     * @param dataYoff Y offset in source Allocation
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, int w, int h,
-                                Allocation data, int dataXoff, int dataYoff) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
-                              mSelectedLOD, mSelectedFace.mID,
-                              w, h, data.getID(mRS), dataXoff, dataYoff,
-                              data.mSelectedLOD, data.mSelectedFace.mID);
-    }
-
-    /**
-     * Copy a {@link android.graphics.Bitmap} into an Allocation.  The height
-     * and width of the update will use the height and width of the {@link
-     * android.graphics.Bitmap}.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param data the Bitmap to be copied
-     */
-    public void copy2DRangeFrom(int xoff, int yoff, Bitmap data) {
-        mRS.validate();
-        if (data.getConfig() == null) {
-            Bitmap newBitmap = Bitmap.createBitmap(data.getWidth(), data.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(newBitmap);
-            c.drawBitmap(data, 0, 0, null);
-            copy2DRangeFrom(xoff, yoff, newBitmap);
-            return;
-        }
-        validateBitmapFormat(data);
-        validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
-        mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
-    }
-
-    private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
-        if (mAdaptedAllocation != null) {
-
-        } else {
-
-            if (xoff < 0 || yoff < 0 || zoff < 0) {
-                throw new RSIllegalArgumentException("Offset cannot be negative.");
-            }
-            if (h < 0 || w < 0 || d < 0) {
-                throw new RSIllegalArgumentException("Height or width cannot be negative.");
-            }
-            if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY) || ((zoff + d) > mCurrentDimZ)) {
-                throw new RSIllegalArgumentException("Updated region larger than allocation.");
-            }
-        }
-    }
-
-    /**
-     * Copy a rectangular region from the array into the allocation.
-     * The array is assumed to be tightly packed.
-     *
-     * The data type of the array is not required to be the same as
-     * the element data type.
-     */
-    private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
-                                          Object array, Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        }
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
-                              array, sizeBytes, dt,
-                              mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy from an array into a 3D region in this Allocation.  The
-     * array is assumed to be tightly packed. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the input data type.
-     *
-     * <p> The size of the region is: w * h * d * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param zoff Z offset of the region to update in this Allocation
-     * @param w Width of the region to update
-     * @param h Height of the region to update
-     * @param d Depth of the region to update
-     * @param array to be placed into the allocation
-     */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
-        copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy a rectangular region into the allocation from another
-     * allocation.
-     *
-     * @param xoff X offset of the region to update in this Allocation
-     * @param yoff Y offset of the region to update in this Allocation
-     * @param zoff Z offset of the region to update in this Allocation
-     * @param w Width of the region to update.
-     * @param h Height of the region to update.
-     * @param d Depth of the region to update.
-     * @param data source allocation.
-     * @param dataXoff X offset of the region in the source Allocation
-     * @param dataYoff Y offset of the region in the source Allocation
-     * @param dataZoff Z offset of the region in the source Allocation
-     */
-    public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d,
-                                Allocation data, int dataXoff, int dataYoff, int dataZoff) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                              w, h, d, data.getID(mRS), dataXoff, dataYoff, dataZoff,
-                              data.mSelectedLOD);
-    }
-
-
-    /**
-     * Copy from the Allocation into a {@link android.graphics.Bitmap}.  The
-     * bitmap must match the dimensions of the Allocation.
-     *
-     * @param b The bitmap to be set from the Allocation.
-     */
-    public void copyTo(Bitmap b) {
-        mRS.validate();
-        validateBitmapFormat(b);
-        validateBitmapSize(b);
-        mRS.nAllocationCopyToBitmap(getID(mRS), b);
-    }
-
-    private void copyTo(Object array, Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
-        }
-        if (usePadding) {
-            if (dt.mSize * arrayLen < mSize / 4 * 3) {
-                throw new RSIllegalArgumentException(
-                    "Size of output array cannot be smaller than size of allocation.");
-            }
-        } else {
-            if (dt.mSize * arrayLen < mSize) {
-                throw new RSIllegalArgumentException(
-                    "Size of output array cannot be smaller than size of allocation.");
-            }
-        }
-        mRS.nAllocationRead(getID(mRS), array, dt, mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy from the Allocation into an array. The method is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the input data type.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells will be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param array The array to be set from the Allocation.
-     */
-    public void copyTo(Object array) {
-        copyTo(array, validateObjectIsPrimitiveArray(array, true),
-               java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy from the Allocation into a byte array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither an 8 bit integer nor a vector of 8 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells will be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d The array to be set from the Allocation.
-     */
-    public void copyTo(byte[] d) {
-        validateIsInt8();
-        copyTo(d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy from the Allocation into a short array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 16 bit integer nor a vector of 16 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells will be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d The array to be set from the Allocation.
-     */
-    public void copyTo(short[] d) {
-        validateIsInt16();
-        copyTo(d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy from the Allocation into a int array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is not a 32 bit integer nor a vector of 32 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells will be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d The array to be set from the Allocation.
-     */
-    public void copyTo(int[] d) {
-        validateIsInt32();
-        copyTo(d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy from the Allocation into a float array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector of
-     * 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the Allocation {@link
-     * #getBytesSize getBytesSize()}.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells will be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the Allocation {@link #getBytesSize getBytesSize()}. The padding bytes for
-     * the cells must not be part of the array.
-     *
-     * @param d The array to be set from the Allocation.
-     */
-    public void copyTo(float[] d) {
-        validateIsFloat32();
-        copyTo(d, Element.DataType.FLOAT_32, d.length);
-    }
-
-    /**
-     * @hide
-     * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files and should not be used by developers.
-     *
-     * @param xoff
-     * @param yoff
-     * @param zoff
-     * @param component_number
-     * @param fp
-     */
-    /*
-    public void copyToFieldPacker(int xoff, int yoff, int zoff, int component_number, FieldPacker fp) {
-        mRS.validate();
-        if (component_number >= mType.mElement.mElements.length) {
-            throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
-        }
-        if(xoff < 0) {
-            throw new RSIllegalArgumentException("Offset x must be >= 0.");
-        }
-        if(yoff < 0) {
-            throw new RSIllegalArgumentException("Offset y must be >= 0.");
-        }
-        if(zoff < 0) {
-            throw new RSIllegalArgumentException("Offset z must be >= 0.");
-        }
-
-        final byte[] data = fp.getData();
-        int data_length = data.length;
-        int eSize = mType.mElement.mElements[component_number].getBytesSize();
-        eSize *= mType.mElement.mArraySizes[component_number];
-
-        if (data_length != eSize) {
-            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
-                                               " does not match component size " + eSize + ".");
-        }
-
-        mRS.nAllocationElementRead(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
-                                   component_number, data, data_length);
-    }
-    */
-
-    private void copy1DRangeToUnchecked(int off, int count, Object array,
-                                        Element.DataType dt, int arrayLen) {
-        final int dataSize = mType.mElement.getBytesSize() * count;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            usePadding = true;
-        }
-        data1DChecks(off, count, arrayLen * dt.mSize, dataSize, usePadding);
-        mRS.nAllocationRead1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt,
-                              mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param array The dest array
-     */
-    public void copy1DRangeToUnchecked(int off, int count, Object array) {
-        copy1DRangeToUnchecked(off, count, array,
-                               validateObjectIsPrimitiveArray(array, false),
-                               java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeToUnchecked(int off, int count, int[] d) {
-        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeToUnchecked(int off, int count, short[] d) {
-        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeToUnchecked(int off, int count, byte[] d) {
-        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method does not
-     * guarantee that the Allocation is compatible with the input buffer.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeToUnchecked(int off, int count, float[] d) {
-        copy1DRangeToUnchecked(off, count, (Object)d, Element.DataType.FLOAT_32, d.length);
-    }
-
-
-    /**
-     * Copy a 1D region of this Allocation into an array.  This method is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} does not match the component type
-     * of the array passed in.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param array The source array.
-     */
-    public void copy1DRangeTo(int off, int count, Object array) {
-        copy1DRangeToUnchecked(off, count, array,
-                               validateObjectIsPrimitiveArray(array, true),
-                               java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit integer nor a vector of 32 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeTo(int off, int count, int[] d) {
-        validateIsInt32();
-        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_32, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 16 bit integer nor a vector of 16 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeTo(int off, int count, short[] d) {
-        validateIsInt16();
-        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither an 8 bit integer nor a vector of 8 bit
-     * integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array
-     */
-    public void copy1DRangeTo(int off, int count, byte[] d) {
-        validateIsInt8();
-        copy1DRangeToUnchecked(off, count, d, Element.DataType.SIGNED_8, d.length);
-    }
-
-    /**
-     * Copy a 1D region of this Allocation into an array. This variant is type checked
-     * and will generate exceptions if the Allocation's {@link
-     * android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector of
-     * 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: count * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param off The offset of the first element to be copied.
-     * @param count The number of elements to be copied.
-     * @param d the source array.
-     */
-    public void copy1DRangeTo(int off, int count, float[] d) {
-        validateIsFloat32();
-        copy1DRangeToUnchecked(off, count, d, Element.DataType.FLOAT_32, d.length);
-    }
-
-
-    void copy2DRangeToUnchecked(int xoff, int yoff, int w, int h, Object array,
-                                Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        validate2DRange(xoff, yoff, w, h);
-        final int dataSize = mType.mElement.getBytesSize() * w * h;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        }
-        mRS.nAllocationRead2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
-                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
-    }
-
-    /**
-     * Copy from a rectangular region in this Allocation into an array. This
-     * method is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} does not match the component type
-     * of the array passed in.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param array Dest Array to be copied into
-     */
-    public void copy2DRangeTo(int xoff, int yoff, int w, int h, Object array) {
-        copy2DRangeToUnchecked(xoff, yoff, w, h, array,
-                               validateObjectIsPrimitiveArray(array, true),
-                               java.lang.reflect.Array.getLength(array));
-    }
-
-    /**
-     * Copy from a rectangular region in this Allocation into an array. This
-     * variant is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} is neither an 8 bit integer nor a vector
-     * of 8 bit integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param data Dest Array to be copied into
-     */
-    public void copy2DRangeTo(int xoff, int yoff, int w, int h, byte[] data) {
-        validateIsInt8();
-        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
-                               Element.DataType.SIGNED_8, data.length);
-    }
-
-    /**
-     * Copy from a rectangular region in this Allocation into an array. This
-     * variant is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} is neither a 16 bit integer nor a vector
-     * of 16 bit integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param data Dest Array to be copied into
-     */
-    public void copy2DRangeTo(int xoff, int yoff, int w, int h, short[] data) {
-        validateIsInt16();
-        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
-                               Element.DataType.SIGNED_16, data.length);
-    }
-
-    /**
-     * Copy from a rectangular region in this Allocation into an array. This
-     * variant is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} is neither a 32 bit integer nor a vector
-     * of 32 bit integers {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param data Dest Array to be copied into
-     */
-    public void copy2DRangeTo(int xoff, int yoff, int w, int h, int[] data) {
-        validateIsInt32();
-        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
-                               Element.DataType.SIGNED_32, data.length);
-    }
-
-    /**
-     * Copy from a rectangular region in this Allocation into an array. This
-     * variant is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} is neither a 32 bit float nor a vector
-     * of 32 bit floats {@link android.support.v8.renderscript.Element.DataType}.
-     *
-     * <p> The size of the region is: w * h * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param data Dest Array to be copied into
-     */
-    public void copy2DRangeTo(int xoff, int yoff, int w, int h, float[] data) {
-        validateIsFloat32();
-        copy2DRangeToUnchecked(xoff, yoff, w, h, data,
-                               Element.DataType.FLOAT_32, data.length);
-    }
-
-
-    /**
-     * Copy from a 3D region in this Allocation into an array. This method does
-     * not guarantee that the Allocation is compatible with the input buffer.
-     * The array is assumed to be tightly packed.
-     *
-     * The data type of the array is not required to be the same as
-     * the element data type.
-     */
-    /*
-    private void copy3DRangeToUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
-                                        Object array, Element.DataType dt, int arrayLen) {
-        mRS.validate();
-        validate3DRange(xoff, yoff, zoff, w, h, d);
-        final int dataSize = mType.mElement.getBytesSize() * w * h * d;
-        // AutoPadding for Vec3 Element
-        boolean usePadding = false;
-        int sizeBytes = arrayLen * dt.mSize;
-        if (mAutoPadding && (mType.getElement().getVectorSize() == 3)) {
-            if (dataSize / 4 * 3 > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-            usePadding = true;
-            sizeBytes = dataSize;
-        } else {
-            if (dataSize > sizeBytes) {
-                throw new RSIllegalArgumentException("Array too small for allocation type.");
-            }
-        }
-        mRS.nAllocationRead3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
-                              array, sizeBytes, dt, mType.mElement.mType.mSize, usePadding);
-    }
-    */
-
-    /**
-     * @hide
-     * Copy from a 3D region in this Allocation into an array. This
-     * method is type checked and will generate exceptions if the Allocation's
-     * {@link android.support.v8.renderscript.Element} does not match the component type
-     * of the array passed in.
-     *
-     * <p> The size of the region is: w * h * d * {@link #getElement}.{@link
-     * Element#getBytesSize}.
-     *
-     * <p> If the Allocation does not have Vec3 Elements, then the size of the
-     * array in bytes must be at least the size of the region.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is disabled, then the size of the array in bytes must be at least the size
-     * of the region. The padding bytes for the cells must be part of the array.
-     *
-     * <p> If the Allocation has Vec3 Elements and {@link #setAutoPadding AutoPadding}
-     * is enabled, then the size of the array in bytes must be at least 3/4 the size
-     * of the region. The padding bytes for the cells must not be part of the array.
-     *
-     * @param xoff X offset of the region to copy in this Allocation
-     * @param yoff Y offset of the region to copy in this Allocation
-     * @param zoff Z offset of the region to copy in this Allocation
-     * @param w Width of the region to copy
-     * @param h Height of the region to copy
-     * @param d Depth of the region to copy
-     * @param array Dest Array to be copied into
-     */
-    /*
-    public void copy3DRangeTo(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
-        copy3DRangeToUnchecked(xoff, yoff, zoff, w, h, d, array,
-                                 validateObjectIsPrimitiveArray(array, true),
-                                 java.lang.reflect.Array.getLength(array));
-    }
-    */
-
-    // creation
-
-    static BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
-    static {
-        mBitmapOptions.inScaled = false;
-    }
-
-    /**
-     * Creates a new Allocation with the given {@link
-     * android.support.v8.renderscript.Type}, mipmap flag, and usage flags.
-     *
-     * @param type RenderScript type describing data layout
-     * @param mips specifies desired mipmap behaviour for the
-     *             allocation
-     * @param usage bit field specifying how the Allocation is
-     *              utilized
-     */
-    static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
-        rs.validate();
-        if (type.getID(rs) == 0) {
-            throw new RSInvalidStateException("Bad Type");
-        }
-
-        if(!rs.usingIO() && (usage & (USAGE_IO_INPUT | USAGE_IO_INPUT)) != 0) {
-            throw new RSRuntimeException("USAGE_IO not supported, Allocation creation failed.");
-        }
-
-        long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
-        if (id == 0) {
-            throw new RSRuntimeException("Allocation creation failed.");
-        }
-        return new Allocation(id, rs, type, usage);
-    }
-
-    /**
-     * Creates an Allocation with the size specified by the type and no mipmaps
-     * generated by default
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param type renderscript type describing data layout
-     * @param usage bit field specifying how the allocation is
-     *              utilized
-     *
-     * @return allocation
-     */
-    static public Allocation createTyped(RenderScript rs, Type type, int usage) {
-        return createTyped(rs, type, MipmapControl.MIPMAP_NONE, usage);
-    }
-
-    /**
-     * Creates an Allocation for use by scripts with a given {@link
-     * android.support.v8.renderscript.Type} and no mipmaps
-     *
-     * @param rs Context to which the Allocation will belong.
-     * @param type RenderScript Type describing data layout
-     *
-     * @return allocation
-     */
-    static public Allocation createTyped(RenderScript rs, Type type) {
-        return createTyped(rs, type, MipmapControl.MIPMAP_NONE, USAGE_SCRIPT);
-    }
-
-    /**
-     * Creates an Allocation with a specified number of given elements
-     *
-     * @param rs Context to which the Allocation will belong.
-     * @param e Element to use in the Allocation
-     * @param count the number of Elements in the Allocation
-     * @param usage bit field specifying how the Allocation is
-     *              utilized
-     *
-     * @return allocation
-     */
-    static public Allocation createSized(RenderScript rs, Element e,
-                                         int count, int usage) {
-        rs.validate();
-        Type.Builder b = new Type.Builder(rs, e);
-        b.setX(count);
-        Type t = b.create();
-
-        long id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
-        if (id == 0) {
-            throw new RSRuntimeException("Allocation creation failed.");
-        }
-        return new Allocation(id, rs, t, usage);
-    }
-
-    /**
-     * Creates an Allocation with a specified number of given elements
-     *
-     * @param rs Context to which the Allocation will belong.
-     * @param e Element to use in the Allocation
-     * @param count the number of Elements in the Allocation
-     *
-     * @return allocation
-     */
-    static public Allocation createSized(RenderScript rs, Element e, int count) {
-        return createSized(rs, e, count, USAGE_SCRIPT);
-    }
-
-    static Element elementFromBitmap(RenderScript rs, Bitmap b) {
-        final Bitmap.Config bc = b.getConfig();
-        if (bc == Bitmap.Config.ALPHA_8) {
-            return Element.A_8(rs);
-        }
-        if (bc == Bitmap.Config.ARGB_4444) {
-            return Element.RGBA_4444(rs);
-        }
-        if (bc == Bitmap.Config.ARGB_8888) {
-            return Element.RGBA_8888(rs);
-        }
-        if (bc == Bitmap.Config.RGB_565) {
-            return Element.RGB_565(rs);
-        }
-        throw new RSInvalidStateException("Bad bitmap type: " + bc);
-    }
-
-    static Type typeFromBitmap(RenderScript rs, Bitmap b,
-                                       MipmapControl mip) {
-        Element e = elementFromBitmap(rs, b);
-        Type.Builder tb = new Type.Builder(rs, e);
-        tb.setX(b.getWidth());
-        tb.setY(b.getHeight());
-        tb.setMipmaps(mip == MipmapControl.MIPMAP_FULL);
-        return tb.create();
-    }
-
-    /**
-     * Creates an Allocation from a {@link android.graphics.Bitmap}.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param b Bitmap source for the allocation data
-     * @param mips specifies desired mipmap behaviour for the
-     *             allocation
-     * @param usage bit field specifying how the allocation is
-     *              utilized
-     *
-     * @return Allocation containing bitmap data
-     *
-     */
-    static public Allocation createFromBitmap(RenderScript rs, Bitmap b,
-                                              MipmapControl mips,
-                                              int usage) {
-        rs.validate();
-
-        // WAR undocumented color formats
-        if (b.getConfig() == null) {
-            if ((usage & USAGE_SHARED) != 0) {
-                throw new RSIllegalArgumentException("USAGE_SHARED cannot be used with a Bitmap that has a null config.");
-            }
-            Bitmap newBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(newBitmap);
-            c.drawBitmap(b, 0, 0, null);
-            return createFromBitmap(rs, newBitmap, mips, usage);
-        }
-
-        Type t = typeFromBitmap(rs, b, mips);
-
-        // enable optimized bitmap path only with no mipmap and script-only usage
-        if (mips == MipmapControl.MIPMAP_NONE &&
-            t.getElement().isCompatible(Element.RGBA_8888(rs)) &&
-            usage == (USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE)) {
-            long id = rs.nAllocationCreateBitmapBackedAllocation(t.getID(rs), mips.mID, b, usage);
-            if (id == 0) {
-                throw new RSRuntimeException("Load failed.");
-            }
-
-            // keep a reference to the Bitmap around to prevent GC
-            Allocation alloc = new Allocation(id, rs, t, usage);
-            alloc.setBitmap(b);
-            return alloc;
-        }
-
-
-        long id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
-        if (id == 0) {
-            throw new RSRuntimeException("Load failed.");
-        }
-        return new Allocation(id, rs, t, usage);
-    }
-
-    /**
-     * Associate a {@link android.view.Surface} with this Allocation. This
-     * operation is only valid for Allocations with {@link #USAGE_IO_OUTPUT}.
-     *
-     * @param sur Surface to associate with allocation
-     */
-    public void setSurface(Surface sur) {
-        mRS.validate();
-        if ((mUsage & USAGE_IO_OUTPUT) == 0) {
-            throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
-        }
-
-        mRS.nAllocationSetSurface(getID(mRS), sur);
-    }
-
-    /**
-     * Creates an Allocation from a {@link android.graphics.Bitmap}.
-     *
-     * <p>This Allocation will be created with {@link #USAGE_SHARED}, and
-     * {@link #USAGE_SCRIPT}.</p>
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param b bitmap source for the allocation data
-     *
-     * @return Allocation containing bitmap data
-     *
-     */
-    static public Allocation createFromBitmap(RenderScript rs, Bitmap b) {
-        return createFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
-                                USAGE_SHARED | USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE);
-    }
-
-    /**
-     * Creates a cubemap Allocation from a {@link android.graphics.Bitmap}
-     * containing the horizontal list of cube faces. Each face must be a square,
-     * have the same size as all other faces, and have a width that is a power
-     * of 2.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param b Bitmap with cubemap faces layed out in the following
-     *          format: right, left, top, bottom, front, back
-     * @param mips specifies desired mipmap behaviour for the cubemap
-     * @param usage bit field specifying how the cubemap is utilized
-     *
-     * @return allocation containing cubemap data
-     *
-     */
-    static public Allocation createCubemapFromBitmap(RenderScript rs, Bitmap b,
-                                                     MipmapControl mips,
-                                                     int usage) {
-        rs.validate();
-
-        int height = b.getHeight();
-        int width = b.getWidth();
-
-        if (width % 6 != 0) {
-            throw new RSIllegalArgumentException("Cubemap height must be multiple of 6");
-        }
-        if (width / 6 != height) {
-            throw new RSIllegalArgumentException("Only square cube map faces supported");
-        }
-        boolean isPow2 = (height & (height - 1)) == 0;
-        if (!isPow2) {
-            throw new RSIllegalArgumentException("Only power of 2 cube faces supported");
-        }
-
-        Element e = elementFromBitmap(rs, b);
-        Type.Builder tb = new Type.Builder(rs, e);
-        tb.setX(height);
-        tb.setY(height);
-        tb.setFaces(true);
-        tb.setMipmaps(mips == MipmapControl.MIPMAP_FULL);
-        Type t = tb.create();
-
-        long id = rs.nAllocationCubeCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
-        if(id == 0) {
-            throw new RSRuntimeException("Load failed for bitmap " + b + " element " + e);
-        }
-        return new Allocation(id, rs, t, usage);
-    }
-
-    /**
-     * Creates a non-mipmapped cubemap Allocation for use as a graphics texture
-     * from a {@link android.graphics.Bitmap} containing the horizontal list of
-     * cube faces. Each face must be a square, have the same size as all other
-     * faces, and have a width that is a power of 2.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param b bitmap with cubemap faces layed out in the following
-     *          format: right, left, top, bottom, front, back
-     *
-     * @return allocation containing cubemap data
-     *
-     */
-    static public Allocation createCubemapFromBitmap(RenderScript rs,
-                                                     Bitmap b) {
-        return createCubemapFromBitmap(rs, b, MipmapControl.MIPMAP_NONE,
-                                       USAGE_GRAPHICS_TEXTURE);
-    }
-
-    /**
-     * Creates a cubemap Allocation from 6 {@link android.graphics.Bitmap}
-     * objects containing the cube faces. Each face must be a square, have the
-     * same size as all other faces, and have a width that is a power of 2.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param xpos cubemap face in the positive x direction
-     * @param xneg cubemap face in the negative x direction
-     * @param ypos cubemap face in the positive y direction
-     * @param yneg cubemap face in the negative y direction
-     * @param zpos cubemap face in the positive z direction
-     * @param zneg cubemap face in the negative z direction
-     * @param mips specifies desired mipmap behaviour for the cubemap
-     * @param usage bit field specifying how the cubemap is utilized
-     *
-     * @return allocation containing cubemap data
-     *
-     */
-    static public Allocation createCubemapFromCubeFaces(RenderScript rs,
-                                                        Bitmap xpos,
-                                                        Bitmap xneg,
-                                                        Bitmap ypos,
-                                                        Bitmap yneg,
-                                                        Bitmap zpos,
-                                                        Bitmap zneg,
-                                                        MipmapControl mips,
-                                                        int usage) {
-        /*
-        int height = xpos.getHeight();
-        if (xpos.getWidth() != height ||
-            xneg.getWidth() != height || xneg.getHeight() != height ||
-            ypos.getWidth() != height || ypos.getHeight() != height ||
-            yneg.getWidth() != height || yneg.getHeight() != height ||
-            zpos.getWidth() != height || zpos.getHeight() != height ||
-            zneg.getWidth() != height || zneg.getHeight() != height) {
-            throw new RSIllegalArgumentException("Only square cube map faces supported");
-        }
-        boolean isPow2 = (height & (height - 1)) == 0;
-        if (!isPow2) {
-            throw new RSIllegalArgumentException("Only power of 2 cube faces supported");
-        }
-
-        Element e = elementFromBitmap(rs, xpos);
-        Type.Builder tb = new Type.Builder(rs, e);
-        tb.setX(height);
-        tb.setY(height);
-        tb.setFaces(true);
-        tb.setMipmaps(mips == MipmapControl.MIPMAP_FULL);
-        Type t = tb.create();
-        Allocation cubemap = Allocation.createTyped(rs, t, mips, usage);
-
-        AllocationAdapter adapter = AllocationAdapter.create2D(rs, cubemap);
-        adapter.setFace(Type.CubemapFace.POSITIVE_X);
-        adapter.copyFrom(xpos);
-        adapter.setFace(Type.CubemapFace.NEGATIVE_X);
-        adapter.copyFrom(xneg);
-        adapter.setFace(Type.CubemapFace.POSITIVE_Y);
-        adapter.copyFrom(ypos);
-        adapter.setFace(Type.CubemapFace.NEGATIVE_Y);
-        adapter.copyFrom(yneg);
-        adapter.setFace(Type.CubemapFace.POSITIVE_Z);
-        adapter.copyFrom(zpos);
-        adapter.setFace(Type.CubemapFace.NEGATIVE_Z);
-        adapter.copyFrom(zneg);
-
-        return cubemap;
-        */
-        return null;
-    }
-
-    /**
-     * Creates a non-mipmapped cubemap Allocation for use as a sampler input
-     * from 6 {@link android.graphics.Bitmap} objects containing the cube
-     * faces. Each face must be a square, have the same size as all other faces,
-     * and have a width that is a power of 2.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param xpos cubemap face in the positive x direction
-     * @param xneg cubemap face in the negative x direction
-     * @param ypos cubemap face in the positive y direction
-     * @param yneg cubemap face in the negative y direction
-     * @param zpos cubemap face in the positive z direction
-     * @param zneg cubemap face in the negative z direction
-     *
-     * @return allocation containing cubemap data
-     *
-     */
-    static public Allocation createCubemapFromCubeFaces(RenderScript rs,
-                                                        Bitmap xpos,
-                                                        Bitmap xneg,
-                                                        Bitmap ypos,
-                                                        Bitmap yneg,
-                                                        Bitmap zpos,
-                                                        Bitmap zneg) {
-        return createCubemapFromCubeFaces(rs, xpos, xneg, ypos, yneg,
-                                          zpos, zneg, MipmapControl.MIPMAP_NONE,
-                                          USAGE_GRAPHICS_TEXTURE);
-    }
-
-    /**
-     * Creates an Allocation from the Bitmap referenced
-     * by resource ID.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param res application resources
-     * @param id resource id to load the data from
-     * @param mips specifies desired mipmap behaviour for the
-     *             allocation
-     * @param usage bit field specifying how the allocation is
-     *              utilized
-     *
-     * @return Allocation containing resource data
-     *
-     */
-    static public Allocation createFromBitmapResource(RenderScript rs,
-                                                      Resources res,
-                                                      int id,
-                                                      MipmapControl mips,
-                                                      int usage) {
-
-        rs.validate();
-        if ((usage & (USAGE_SHARED | USAGE_IO_INPUT | USAGE_IO_OUTPUT)) != 0) {
-            throw new RSIllegalArgumentException("Unsupported usage specified.");
-        }
-        Bitmap b = BitmapFactory.decodeResource(res, id);
-        Allocation alloc = createFromBitmap(rs, b, mips, usage);
-        b.recycle();
-        return alloc;
-    }
-
-    /**
-     * Creates a non-mipmapped Allocation to use as a graphics texture from the
-     * {@link android.graphics.Bitmap} referenced by resource ID.
-     *
-     * <p>This allocation will be created with {@link #USAGE_SCRIPT} and
-     * {@link #USAGE_GRAPHICS_TEXTURE}.</p>
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param res application resources
-     * @param id resource id to load the data from
-     *
-     * @return Allocation containing resource data
-     *
-     */
-    static public Allocation createFromBitmapResource(RenderScript rs,
-                                                      Resources res,
-                                                      int id) {
-        return createFromBitmapResource(rs, res, id,
-                                        MipmapControl.MIPMAP_NONE,
-                                        USAGE_SCRIPT | USAGE_GRAPHICS_TEXTURE);
-    }
-
-    /**
-     * Creates an Allocation containing string data encoded in UTF-8 format.
-     *
-     * @param rs Context to which the allocation will belong.
-     * @param str string to create the allocation from
-     * @param usage bit field specifying how the allocaiton is
-     *              utilized
-     *
-     */
-    static public Allocation createFromString(RenderScript rs,
-                                              String str,
-                                              int usage) {
-        rs.validate();
-        byte[] allocArray = null;
-        try {
-            allocArray = str.getBytes("UTF-8");
-            Allocation alloc = Allocation.createSized(rs, Element.U8(rs), allocArray.length, usage);
-            alloc.copyFrom(allocArray);
-            return alloc;
-        }
-        catch (Exception e) {
-            throw new RSRuntimeException("Could not convert string to utf-8.");
-        }
-    }
-
-    /**
-     * Frees any native resources associated with this object.  The
-     * primary use is to force immediate cleanup of resources when it is
-     * believed the GC will not respond quickly enough.
-     * For USAGE_IO_OUTPUT, destroy() implies setSurface(null).
-     */
-    @Override
-    public void destroy() {
-        if (mIncCompatAllocation != 0) {
-            boolean shouldDestroy = false;
-            synchronized(this) {
-                if (!mIncAllocDestroyed) {
-                    shouldDestroy = true;
-                    mIncAllocDestroyed = true;
-                }
-            }
-
-            if (shouldDestroy) {
-                // must include nObjDestroy in the critical section
-                ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
-                rlock.lock();
-                if(mRS.isAlive()) {
-                    mRS.nIncObjDestroy(mIncCompatAllocation);
-                }
-                rlock.unlock();
-                mIncCompatAllocation = 0;
-            }
-        }
-        if ((mUsage & (USAGE_IO_INPUT | USAGE_IO_OUTPUT)) != 0) {
-            setSurface(null);
-        }
-        super.destroy();
-    }
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java b/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java
deleted file mode 100644
index bb49600..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/BaseObj.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * BaseObj is the base class for all RenderScript objects owned by a RS context.
- * It is responsible for lifetime management and resource tracking. This class
- * should not be used by a user application.
- *
- **/
-public class BaseObj {
-    BaseObj(long id, RenderScript rs) {
-        rs.validate();
-        mRS = rs;
-        mID = id;
-        mDestroyed = false;
-    }
-
-    void setID(long id) {
-        if (mID != 0) {
-            throw new RSRuntimeException("Internal Error, reset of object ID.");
-        }
-        mID = id;
-    }
-
-    /**
-     * Lookup the native object ID for this object.  Primarily used by the
-     * generated reflected code.
-     *
-     * @param rs Context to verify against internal context for
-     *           match.
-     *
-     * @return long
-     */
-    long getID(RenderScript rs) {
-        mRS.validate();
-        if (mDestroyed) {
-            throw new RSInvalidStateException("using a destroyed object.");
-        }
-        if (mID == 0) {
-            throw new RSRuntimeException("Internal error: Object id 0.");
-        }
-        if ((rs != null) && (rs != mRS)) {
-            throw new RSInvalidStateException("using object with mismatched context.");
-        }
-        return mID;
-    }
-
-    android.renderscript.BaseObj getNObj() {
-        return null;
-    }
-
-    void checkValid() {
-        if ((mID == 0) && (getNObj() == null)) {
-            throw new RSIllegalArgumentException("Invalid object.");
-        }
-    }
-
-    private long mID;
-    private boolean mDestroyed;
-    RenderScript mRS;
-
-    private void helpDestroy() {
-        boolean shouldDestroy = false;
-        synchronized(this) {
-            if (!mDestroyed) {
-                shouldDestroy = true;
-                mDestroyed = true;
-            }
-        }
-
-        if (shouldDestroy) {
-            // must include nObjDestroy in the critical section
-            ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
-            rlock.lock();
-            if(mRS.isAlive()) {
-                mRS.nObjDestroy(mID);
-            }
-            rlock.unlock();
-            mRS = null;
-            mID = 0;
-        }
-    }
-
-
-    protected void finalize() throws Throwable {
-        helpDestroy();
-        super.finalize();
-    }
-
-    /**
-     * Frees any native resources associated with this object.  The
-     * primary use is to force immediate cleanup of resources when it is
-     * believed the GC will not respond quickly enough.
-     */
-    public void destroy() {
-        if(mDestroyed) {
-            throw new RSInvalidStateException("Object already destroyed.");
-        }
-        helpDestroy();
-    }
-
-    /**
-     * Calculates the hash code value for a BaseObj.
-     *
-     * @return int
-     */
-    @Override
-    public int hashCode() {
-        return (int)((mID & 0xfffffff) ^ (mID >> 32));
-    }
-
-    /**
-     * Compare the current BaseObj with another BaseObj for equality.
-     *
-     * @param obj The object to check equality with.
-     *
-     * @return boolean
-     */
-    @Override
-    public boolean equals(Object obj) {
-        // Early-out check to see if both BaseObjs are actually the same
-        if (this == obj)
-            return true;
-
-        if (obj == null) {
-            return false;
-        }
-
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-
-        BaseObj b = (BaseObj) obj;
-        return mID == b.mID;
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java
deleted file mode 100644
index 2371077..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Byte2.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript byte2 type back to the Android system.
- *
- **/
-public class Byte2 {
-    public Byte2() {
-    }
-
-    public Byte2(byte initX, byte initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public byte x;
-    public byte y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Byte3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Byte3.java
deleted file mode 100644
index 4ed6af6..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Byte3.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript byte3 type back to the Android system.
- *
- **/
-public class Byte3 {
-    public Byte3() {
-    }
-
-    public Byte3(byte initX, byte initY, byte initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public byte x;
-    public byte y;
-    public byte z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Byte4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Byte4.java
deleted file mode 100644
index 715a718..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Byte4.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript byte4 type back to the Android system.
- *
- **/
-public class Byte4 {
-    public Byte4() {
-    }
-
-    public Byte4(byte initX, byte initY, byte initZ, byte initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public byte x;
-    public byte y;
-    public byte z;
-    public byte w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Double2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Double2.java
deleted file mode 100644
index cd73363..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Double2.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript double2 type back
- * to the Android system.
- *
- **/
-public class Double2 {
-    public Double2() {
-    }
-
-    public Double2(double initX, double initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public double x;
-    public double y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Double3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Double3.java
deleted file mode 100644
index 38a46a6..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Double3.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript double3 type back
- * to the Android system.
- *
- **/
-public class Double3 {
-    public Double3() {
-    }
-
-    public Double3(double initX, double initY, double initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public double x;
-    public double y;
-    public double z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Double4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Double4.java
deleted file mode 100644
index 6de0fa8..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Double4.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript double4 type back
- * to the Android system.
- *
- **/
-public class Double4 {
-    public Double4() {
-    }
-
-    public Double4(double initX, double initY, double initZ, double initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public double x;
-    public double y;
-    public double z;
-    public double w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Element.java b/v8/renderscript/java/src/android/support/v8/renderscript/Element.java
deleted file mode 100644
index 135d854..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Element.java
+++ /dev/null
@@ -1,1020 +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.v8.renderscript;
-
-import java.lang.reflect.Field;
-
-import android.util.Log;
-
-/**
- * <p>An Element represents one item within an {@link
- * android.support.v8.renderscript.Allocation}.  An Element is roughly
- * equivalent to a C type in a RenderScript kernel. Elements may be basic or
- * complex. Some basic elements are</p> <ul> <li>A single float value
- * (equivalent to a float in a kernel)</li> <li>A four-element float vector
- * (equivalent to a float4 in a kernel)</li> <li>An unsigned 32-bit integer
- * (equivalent to an unsigned int in a kernel)</li> <li>A single signed 8-bit
- * integer (equivalent to a char in a kernel)</li> </ul> <p>A complex element is
- * roughly equivalent to a C struct and contains a number of basic or complex
- * Elements. From Java code, a complex element contains a list of sub-elements
- * and names that represents a particular data structure. Structs used in RS
- * scripts are available to Java code by using the
- * {@code ScriptField_structname} class that is reflected from a particular
- * script.</p>
- *
- * <p>Basic Elements are comprised of a {@link
- * android.support.v8.renderscript.Element.DataType} and a {@link
- * android.support.v8.renderscript.Element.DataKind}. The DataType encodes C
- * type information of an Element, while the DataKind encodes how that Element
- * should be interpreted by a {@link android.support.v8.renderscript.Sampler}.
- * Note that {@link android.support.v8.renderscript.Allocation} objects with
- * DataKind {@link android.support.v8.renderscript.Element.DataKind#USER} cannot
- * be used as input for a {@link android.support.v8.renderscript.Sampler}. In
- * general, {@link android.support.v8.renderscript.Allocation} objects that are
- * intended for use with a {@link android.support.v8.renderscript.Sampler}
- * should use bitmap-derived Elements such as
- * {@link android.support.v8.renderscript.Element#RGBA_8888} or {@link
- * android.support.v8.renderscript#Element.A_8}.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about creating an application that uses RenderScript,
- * read the
- * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a>
- * developer guide.</p>
- * </div>
- **/
-public class Element extends BaseObj {
-    int mSize;
-    Element[] mElements;
-    String[] mElementNames;
-    int[] mArraySizes;
-    int[] mOffsetInBytes;
-    int[] mVisibleElementMap;
-
-    DataType mType;
-    DataKind mKind;
-    boolean mNormalized;
-    int mVectorSize;
-
-    private void updateVisibleSubElements() {
-        if (mElements == null) {
-            return;
-        }
-
-        int noPaddingFieldCount = 0;
-        int fieldCount = mElementNames.length;
-        // Find out how many elements are not padding
-        for (int ct = 0; ct < fieldCount; ct ++) {
-            if (mElementNames[ct].charAt(0) != '#') {
-                noPaddingFieldCount ++;
-            }
-        }
-        mVisibleElementMap = new int[noPaddingFieldCount];
-
-        // Make a map that points us at non-padding elements
-        for (int ct = 0, ctNoPadding = 0; ct < fieldCount; ct ++) {
-            if (mElementNames[ct].charAt(0) != '#') {
-                mVisibleElementMap[ctNoPadding ++] = ct;
-            }
-        }
-    }
-
-    /**
-    * @return element size in bytes
-    */
-    public int getBytesSize() {
-        return mSize;
-    }
-
-    /**
-    * Returns the number of vector components. 2 for float2, 4 for
-    * float4, etc.
-    * @return element vector size
-    */
-    public int getVectorSize() {
-        return mVectorSize;
-    }
-
-
-    /**
-     * DataType represents the basic type information for a basic element.  The
-     * naming convention follows.  For numeric types it is FLOAT,
-     * SIGNED, or UNSIGNED followed by the _BITS where BITS is the
-     * size of the data.  BOOLEAN is a true / false (1,0)
-     * represented in an 8 bit container.  The UNSIGNED variants
-     * with multiple bit definitions are for packed graphical data
-     * formats and represent vectors with per vector member sizes
-     * which are treated as a single unit for packing and alignment
-     * purposes.
-     *
-     * MATRIX the three matrix types contain FLOAT_32 elements and are treated
-     * as 32 bits for alignment purposes.
-     *
-     * RS_* objects.  32 bit opaque handles.
-     */
-    public enum DataType {
-        NONE (0, 0),
-        //FLOAT_16 (1, 2),
-        FLOAT_32 (2, 4),
-        FLOAT_64 (3, 8),
-        SIGNED_8 (4, 1),
-        SIGNED_16 (5, 2),
-        SIGNED_32 (6, 4),
-        SIGNED_64 (7, 8),
-        UNSIGNED_8 (8, 1),
-        UNSIGNED_16 (9, 2),
-        UNSIGNED_32 (10, 4),
-        UNSIGNED_64 (11, 8),
-
-        BOOLEAN(12, 1),
-
-        UNSIGNED_5_6_5 (13, 2),
-        UNSIGNED_5_5_5_1 (14, 2),
-        UNSIGNED_4_4_4_4 (15, 2),
-
-        MATRIX_4X4 (16, 64),
-        MATRIX_3X3 (17, 36),
-        MATRIX_2X2 (18, 16),
-
-        RS_ELEMENT (1000),
-        RS_TYPE (1001),
-        RS_ALLOCATION (1002),
-        RS_SAMPLER (1003),
-        RS_SCRIPT (1004);
-
-        int mID;
-        int mSize;
-        DataType(int id, int size) {
-            mID = id;
-            mSize = size;
-        }
-
-        DataType(int id) {
-            mID = id;
-            mSize = 4;
-            if (RenderScript.sPointerSize == 8) {
-                mSize = 32;
-            }
-        }
-    }
-
-    /**
-     * The special interpretation of the data if required.  This is primarly
-     * useful for graphical data.  USER indicates no special interpretation is
-     * expected.  PIXEL is used in conjunction with the standard data types for
-     * representing texture formats.
-     */
-    public enum DataKind {
-        USER (0),
-
-        PIXEL_L (7),
-        PIXEL_A (8),
-        PIXEL_LA (9),
-        PIXEL_RGB (10),
-        PIXEL_RGBA (11),
-        PIXEL_DEPTH (12),
-        PIXEL_YUV(13);
-
-        int mID;
-        DataKind(int id) {
-            mID = id;
-        }
-    }
-
-    /**
-     * Return if a element is too complex for use as a data source for a Mesh or
-     * a Program.
-     *
-     * @return boolean
-     */
-    public boolean isComplex() {
-        if (mElements == null) {
-            return false;
-        }
-        for (int ct=0; ct < mElements.length; ct++) {
-            if (mElements[ct].mElements != null) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-    * Elements could be simple, such as an int or a float, or a
-    * structure with multiple sub elements, such as a collection of
-    * floats, float2, float4. This function returns zero for simple
-    * elements or the number of sub-elements otherwise.
-    * @return number of sub-elements in this element
-    */
-    public int getSubElementCount() {
-        if (mVisibleElementMap == null) {
-            return 0;
-        }
-        return mVisibleElementMap.length;
-    }
-
-    /**
-    * For complex elements, this function will return the
-    * sub-element at index
-    * @param index index of the sub-element to return
-    * @return sub-element in this element at given index
-    */
-    public Element getSubElement(int index) {
-        if (mVisibleElementMap == null) {
-            throw new RSIllegalArgumentException("Element contains no sub-elements");
-        }
-        if (index < 0 || index >= mVisibleElementMap.length) {
-            throw new RSIllegalArgumentException("Illegal sub-element index");
-        }
-        return mElements[mVisibleElementMap[index]];
-    }
-
-    /**
-    * For complex elements, this function will return the
-    * sub-element name at index
-    * @param index index of the sub-element
-    * @return sub-element in this element at given index
-    */
-    public String getSubElementName(int index) {
-        if (mVisibleElementMap == null) {
-            throw new RSIllegalArgumentException("Element contains no sub-elements");
-        }
-        if (index < 0 || index >= mVisibleElementMap.length) {
-            throw new RSIllegalArgumentException("Illegal sub-element index");
-        }
-        return mElementNames[mVisibleElementMap[index]];
-    }
-
-    /**
-    * For complex elements, some sub-elements could be statically
-    * sized arrays. This function will return the array size for
-    * sub-element at index
-    * @param index index of the sub-element
-    * @return array size of sub-element in this element at given index
-    */
-    public int getSubElementArraySize(int index) {
-        if (mVisibleElementMap == null) {
-            throw new RSIllegalArgumentException("Element contains no sub-elements");
-        }
-        if (index < 0 || index >= mVisibleElementMap.length) {
-            throw new RSIllegalArgumentException("Illegal sub-element index");
-        }
-        return mArraySizes[mVisibleElementMap[index]];
-    }
-
-    /**
-    * This function specifies the location of a sub-element within
-    * the element
-    * @param index index of the sub-element
-    * @return offset in bytes of sub-element in this element at given index
-    */
-    public int getSubElementOffsetBytes(int index) {
-        if (mVisibleElementMap == null) {
-            throw new RSIllegalArgumentException("Element contains no sub-elements");
-        }
-        if (index < 0 || index >= mVisibleElementMap.length) {
-            throw new RSIllegalArgumentException("Illegal sub-element index");
-        }
-        return mOffsetInBytes[mVisibleElementMap[index]];
-    }
-
-    /**
-    * @return element data type
-    */
-    public DataType getDataType() {
-        return mType;
-    }
-
-    /**
-    * @return element data kind
-    */
-    public DataKind getDataKind() {
-        return mKind;
-    }
-
-    /**
-     * Utility function for returning an Element containing a single Boolean.
-     *
-     * @param rs Context to which the element will belong.
-     *
-     * @return Element
-     */
-    public static Element BOOLEAN(RenderScript rs) {
-        if(rs.mElement_BOOLEAN == null) {
-            rs.mElement_BOOLEAN = createUser(rs, DataType.BOOLEAN);
-        }
-        return rs.mElement_BOOLEAN;
-    }
-
-    /**
-     * Utility function for returning an Element containing a single UNSIGNED_8.
-     *
-     * @param rs Context to which the element will belong.
-     *
-     * @return Element
-     */
-    public static Element U8(RenderScript rs) {
-        if(rs.mElement_U8 == null) {
-            rs.mElement_U8 = createUser(rs, DataType.UNSIGNED_8);
-        }
-        return rs.mElement_U8;
-    }
-
-    /**
-     * Utility function for returning an Element containing a single SIGNED_8.
-     *
-     * @param rs Context to which the element will belong.
-     *
-     * @return Element
-     */
-    public static Element I8(RenderScript rs) {
-        if(rs.mElement_I8 == null) {
-            rs.mElement_I8 = createUser(rs, DataType.SIGNED_8);
-        }
-        return rs.mElement_I8;
-    }
-
-    public static Element U16(RenderScript rs) {
-        if(rs.mElement_U16 == null) {
-            rs.mElement_U16 = createUser(rs, DataType.UNSIGNED_16);
-        }
-        return rs.mElement_U16;
-    }
-
-    public static Element I16(RenderScript rs) {
-        if(rs.mElement_I16 == null) {
-            rs.mElement_I16 = createUser(rs, DataType.SIGNED_16);
-        }
-        return rs.mElement_I16;
-    }
-
-    public static Element U32(RenderScript rs) {
-        if(rs.mElement_U32 == null) {
-            rs.mElement_U32 = createUser(rs, DataType.UNSIGNED_32);
-        }
-        return rs.mElement_U32;
-    }
-
-    public static Element I32(RenderScript rs) {
-        if(rs.mElement_I32 == null) {
-            rs.mElement_I32 = createUser(rs, DataType.SIGNED_32);
-        }
-        return rs.mElement_I32;
-    }
-
-    public static Element U64(RenderScript rs) {
-        if(rs.mElement_U64 == null) {
-            rs.mElement_U64 = createUser(rs, DataType.UNSIGNED_64);
-        }
-        return rs.mElement_U64;
-    }
-
-    public static Element I64(RenderScript rs) {
-        if(rs.mElement_I64 == null) {
-            rs.mElement_I64 = createUser(rs, DataType.SIGNED_64);
-        }
-        return rs.mElement_I64;
-    }
-
-    public static Element F32(RenderScript rs) {
-        if(rs.mElement_F32 == null) {
-            rs.mElement_F32 = createUser(rs, DataType.FLOAT_32);
-        }
-        return rs.mElement_F32;
-    }
-
-    public static Element F64(RenderScript rs) {
-        if(rs.mElement_F64 == null) {
-            rs.mElement_F64 = createUser(rs, DataType.FLOAT_64);
-        }
-        return rs.mElement_F64;
-    }
-
-    public static Element ELEMENT(RenderScript rs) {
-        if(rs.mElement_ELEMENT == null) {
-            rs.mElement_ELEMENT = createUser(rs, DataType.RS_ELEMENT);
-        }
-        return rs.mElement_ELEMENT;
-    }
-
-    public static Element TYPE(RenderScript rs) {
-        if(rs.mElement_TYPE == null) {
-            rs.mElement_TYPE = createUser(rs, DataType.RS_TYPE);
-        }
-        return rs.mElement_TYPE;
-    }
-
-    public static Element ALLOCATION(RenderScript rs) {
-        if(rs.mElement_ALLOCATION == null) {
-            rs.mElement_ALLOCATION = createUser(rs, DataType.RS_ALLOCATION);
-        }
-        return rs.mElement_ALLOCATION;
-    }
-
-    public static Element SAMPLER(RenderScript rs) {
-        if(rs.mElement_SAMPLER == null) {
-            rs.mElement_SAMPLER = createUser(rs, DataType.RS_SAMPLER);
-        }
-        return rs.mElement_SAMPLER;
-    }
-
-    public static Element SCRIPT(RenderScript rs) {
-        if(rs.mElement_SCRIPT == null) {
-            rs.mElement_SCRIPT = createUser(rs, DataType.RS_SCRIPT);
-        }
-        return rs.mElement_SCRIPT;
-    }
-
-
-    public static Element A_8(RenderScript rs) {
-        if(rs.mElement_A_8 == null) {
-            rs.mElement_A_8 = createPixel(rs, DataType.UNSIGNED_8, DataKind.PIXEL_A);
-        }
-        return rs.mElement_A_8;
-    }
-
-    public static Element RGB_565(RenderScript rs) {
-        if(rs.mElement_RGB_565 == null) {
-            rs.mElement_RGB_565 = createPixel(rs, DataType.UNSIGNED_5_6_5, DataKind.PIXEL_RGB);
-        }
-        return rs.mElement_RGB_565;
-    }
-
-    public static Element RGB_888(RenderScript rs) {
-        if(rs.mElement_RGB_888 == null) {
-            rs.mElement_RGB_888 = createPixel(rs, DataType.UNSIGNED_8, DataKind.PIXEL_RGB);
-        }
-        return rs.mElement_RGB_888;
-    }
-
-    public static Element RGBA_5551(RenderScript rs) {
-        if(rs.mElement_RGBA_5551 == null) {
-            rs.mElement_RGBA_5551 = createPixel(rs, DataType.UNSIGNED_5_5_5_1, DataKind.PIXEL_RGBA);
-        }
-        return rs.mElement_RGBA_5551;
-    }
-
-    public static Element RGBA_4444(RenderScript rs) {
-        if(rs.mElement_RGBA_4444 == null) {
-            rs.mElement_RGBA_4444 = createPixel(rs, DataType.UNSIGNED_4_4_4_4, DataKind.PIXEL_RGBA);
-        }
-        return rs.mElement_RGBA_4444;
-    }
-
-    public static Element RGBA_8888(RenderScript rs) {
-        if(rs.mElement_RGBA_8888 == null) {
-            rs.mElement_RGBA_8888 = createPixel(rs, DataType.UNSIGNED_8, DataKind.PIXEL_RGBA);
-        }
-        return rs.mElement_RGBA_8888;
-    }
-
-    public static Element F32_2(RenderScript rs) {
-        if(rs.mElement_FLOAT_2 == null) {
-            rs.mElement_FLOAT_2 = createVector(rs, DataType.FLOAT_32, 2);
-        }
-        return rs.mElement_FLOAT_2;
-    }
-
-    public static Element F32_3(RenderScript rs) {
-        if(rs.mElement_FLOAT_3 == null) {
-            rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_32, 3);
-        }
-        return rs.mElement_FLOAT_3;
-    }
-
-    public static Element F32_4(RenderScript rs) {
-        if(rs.mElement_FLOAT_4 == null) {
-            rs.mElement_FLOAT_4 = createVector(rs, DataType.FLOAT_32, 4);
-        }
-        return rs.mElement_FLOAT_4;
-    }
-
-    public static Element F64_2(RenderScript rs) {
-        if(rs.mElement_DOUBLE_2 == null) {
-            rs.mElement_DOUBLE_2 = createVector(rs, DataType.FLOAT_64, 2);
-        }
-        return rs.mElement_DOUBLE_2;
-    }
-
-    public static Element F64_3(RenderScript rs) {
-        if(rs.mElement_DOUBLE_3 == null) {
-            rs.mElement_DOUBLE_3 = createVector(rs, DataType.FLOAT_64, 3);
-        }
-        return rs.mElement_DOUBLE_3;
-    }
-
-    public static Element F64_4(RenderScript rs) {
-        if(rs.mElement_DOUBLE_4 == null) {
-            rs.mElement_DOUBLE_4 = createVector(rs, DataType.FLOAT_64, 4);
-        }
-        return rs.mElement_DOUBLE_4;
-    }
-
-    public static Element U8_2(RenderScript rs) {
-        if(rs.mElement_UCHAR_2 == null) {
-            rs.mElement_UCHAR_2 = createVector(rs, DataType.UNSIGNED_8, 2);
-        }
-        return rs.mElement_UCHAR_2;
-    }
-
-    public static Element U8_3(RenderScript rs) {
-        if(rs.mElement_UCHAR_3 == null) {
-            rs.mElement_UCHAR_3 = createVector(rs, DataType.UNSIGNED_8, 3);
-        }
-        return rs.mElement_UCHAR_3;
-    }
-
-    public static Element U8_4(RenderScript rs) {
-        if(rs.mElement_UCHAR_4 == null) {
-            rs.mElement_UCHAR_4 = createVector(rs, DataType.UNSIGNED_8, 4);
-        }
-        return rs.mElement_UCHAR_4;
-    }
-
-    public static Element I8_2(RenderScript rs) {
-        if(rs.mElement_CHAR_2 == null) {
-            rs.mElement_CHAR_2 = createVector(rs, DataType.SIGNED_8, 2);
-        }
-        return rs.mElement_CHAR_2;
-    }
-
-    public static Element I8_3(RenderScript rs) {
-        if(rs.mElement_CHAR_3 == null) {
-            rs.mElement_CHAR_3 = createVector(rs, DataType.SIGNED_8, 3);
-        }
-        return rs.mElement_CHAR_3;
-    }
-
-    public static Element I8_4(RenderScript rs) {
-        if(rs.mElement_CHAR_4 == null) {
-            rs.mElement_CHAR_4 = createVector(rs, DataType.SIGNED_8, 4);
-        }
-        return rs.mElement_CHAR_4;
-    }
-
-    public static Element U16_2(RenderScript rs) {
-        if(rs.mElement_USHORT_2 == null) {
-            rs.mElement_USHORT_2 = createVector(rs, DataType.UNSIGNED_16, 2);
-        }
-        return rs.mElement_USHORT_2;
-    }
-
-    public static Element U16_3(RenderScript rs) {
-        if(rs.mElement_USHORT_3 == null) {
-            rs.mElement_USHORT_3 = createVector(rs, DataType.UNSIGNED_16, 3);
-        }
-        return rs.mElement_USHORT_3;
-    }
-
-    public static Element U16_4(RenderScript rs) {
-        if(rs.mElement_USHORT_4 == null) {
-            rs.mElement_USHORT_4 = createVector(rs, DataType.UNSIGNED_16, 4);
-        }
-        return rs.mElement_USHORT_4;
-    }
-
-    public static Element I16_2(RenderScript rs) {
-        if(rs.mElement_SHORT_2 == null) {
-            rs.mElement_SHORT_2 = createVector(rs, DataType.SIGNED_16, 2);
-        }
-        return rs.mElement_SHORT_2;
-    }
-
-    public static Element I16_3(RenderScript rs) {
-        if(rs.mElement_SHORT_3 == null) {
-            rs.mElement_SHORT_3 = createVector(rs, DataType.SIGNED_16, 3);
-        }
-        return rs.mElement_SHORT_3;
-    }
-
-    public static Element I16_4(RenderScript rs) {
-        if(rs.mElement_SHORT_4 == null) {
-            rs.mElement_SHORT_4 = createVector(rs, DataType.SIGNED_16, 4);
-        }
-        return rs.mElement_SHORT_4;
-    }
-
-    public static Element U32_2(RenderScript rs) {
-        if(rs.mElement_UINT_2 == null) {
-            rs.mElement_UINT_2 = createVector(rs, DataType.UNSIGNED_32, 2);
-        }
-        return rs.mElement_UINT_2;
-    }
-
-    public static Element U32_3(RenderScript rs) {
-        if(rs.mElement_UINT_3 == null) {
-            rs.mElement_UINT_3 = createVector(rs, DataType.UNSIGNED_32, 3);
-        }
-        return rs.mElement_UINT_3;
-    }
-
-    public static Element U32_4(RenderScript rs) {
-        if(rs.mElement_UINT_4 == null) {
-            rs.mElement_UINT_4 = createVector(rs, DataType.UNSIGNED_32, 4);
-        }
-        return rs.mElement_UINT_4;
-    }
-
-    public static Element I32_2(RenderScript rs) {
-        if(rs.mElement_INT_2 == null) {
-            rs.mElement_INT_2 = createVector(rs, DataType.SIGNED_32, 2);
-        }
-        return rs.mElement_INT_2;
-    }
-
-    public static Element I32_3(RenderScript rs) {
-        if(rs.mElement_INT_3 == null) {
-            rs.mElement_INT_3 = createVector(rs, DataType.SIGNED_32, 3);
-        }
-        return rs.mElement_INT_3;
-    }
-
-    public static Element I32_4(RenderScript rs) {
-        if(rs.mElement_INT_4 == null) {
-            rs.mElement_INT_4 = createVector(rs, DataType.SIGNED_32, 4);
-        }
-        return rs.mElement_INT_4;
-    }
-
-    public static Element U64_2(RenderScript rs) {
-        if(rs.mElement_ULONG_2 == null) {
-            rs.mElement_ULONG_2 = createVector(rs, DataType.UNSIGNED_64, 2);
-        }
-        return rs.mElement_ULONG_2;
-    }
-
-    public static Element U64_3(RenderScript rs) {
-        if(rs.mElement_ULONG_3 == null) {
-            rs.mElement_ULONG_3 = createVector(rs, DataType.UNSIGNED_64, 3);
-        }
-        return rs.mElement_ULONG_3;
-    }
-
-    public static Element U64_4(RenderScript rs) {
-        if(rs.mElement_ULONG_4 == null) {
-            rs.mElement_ULONG_4 = createVector(rs, DataType.UNSIGNED_64, 4);
-        }
-        return rs.mElement_ULONG_4;
-    }
-
-    public static Element I64_2(RenderScript rs) {
-        if(rs.mElement_LONG_2 == null) {
-            rs.mElement_LONG_2 = createVector(rs, DataType.SIGNED_64, 2);
-        }
-        return rs.mElement_LONG_2;
-    }
-
-    public static Element I64_3(RenderScript rs) {
-        if(rs.mElement_LONG_3 == null) {
-            rs.mElement_LONG_3 = createVector(rs, DataType.SIGNED_64, 3);
-        }
-        return rs.mElement_LONG_3;
-    }
-
-    public static Element I64_4(RenderScript rs) {
-        if(rs.mElement_LONG_4 == null) {
-            rs.mElement_LONG_4 = createVector(rs, DataType.SIGNED_64, 4);
-        }
-        return rs.mElement_LONG_4;
-    }
-
-    public static Element MATRIX_4X4(RenderScript rs) {
-        if(rs.mElement_MATRIX_4X4 == null) {
-            rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
-        }
-        return rs.mElement_MATRIX_4X4;
-    }
-
-    public static Element MATRIX_3X3(RenderScript rs) {
-        if(rs.mElement_MATRIX_3X3 == null) {
-            rs.mElement_MATRIX_3X3 = createUser(rs, DataType.MATRIX_3X3);
-        }
-        return rs.mElement_MATRIX_3X3;
-    }
-
-    public static Element MATRIX_2X2(RenderScript rs) {
-        if(rs.mElement_MATRIX_2X2 == null) {
-            rs.mElement_MATRIX_2X2 = createUser(rs, DataType.MATRIX_2X2);
-        }
-        return rs.mElement_MATRIX_2X2;
-    }
-
-    Element(long id, RenderScript rs, Element[] e, String[] n, int[] as) {
-        super(id, rs);
-        mSize = 0;
-        mVectorSize = 1;
-        mElements = e;
-        mElementNames = n;
-        mArraySizes = as;
-        mType = DataType.NONE;
-        mKind = DataKind.USER;
-        mOffsetInBytes = new int[mElements.length];
-        for (int ct = 0; ct < mElements.length; ct++ ) {
-            mOffsetInBytes[ct] = mSize;
-            mSize += mElements[ct].mSize * mArraySizes[ct];
-        }
-        updateVisibleSubElements();
-    }
-
-    Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
-        super(id, rs);
-        if ((dt != DataType.UNSIGNED_5_6_5) &&
-            (dt != DataType.UNSIGNED_4_4_4_4) &&
-            (dt != DataType.UNSIGNED_5_5_5_1)) {
-            if (size == 3) {
-                mSize = dt.mSize * 4;
-            } else {
-                mSize = dt.mSize * size;
-            }
-        } else {
-            mSize = dt.mSize;
-        }
-        mType = dt;
-        mKind = dk;
-        mNormalized = norm;
-        mVectorSize = size;
-    }
-
-    Element(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /*
-     * Get an identical dummy Element for Compat Context
-     *
-     */
-    public long getDummyElement(RenderScript mRS) {
-        return mRS.nIncElementCreate(mType.mID, mKind.mID, mNormalized, mVectorSize);
-    }
-    /**
-     * Create a custom Element of the specified DataType.  The DataKind will be
-     * set to USER and the vector size to 1 indicating non-vector.
-     *
-     * @param rs The context associated with the new Element.
-     * @param dt The DataType for the new element.
-     * @return Element
-     */
-    static Element createUser(RenderScript rs, DataType dt) {
-        DataKind dk = DataKind.USER;
-        boolean norm = false;
-        int vecSize = 1;
-        long id = rs.nElementCreate(dt.mID, dk.mID, norm, vecSize);
-        return new Element(id, rs, dt, dk, norm, vecSize);
-    }
-
-    /**
-     * Create a custom vector element of the specified DataType and vector size.
-     * DataKind will be set to USER. Only primitive types (FLOAT_32, FLOAT_64,
-     * SIGNED_8, SIGNED_16, SIGNED_32, SIGNED_64, UNSIGNED_8, UNSIGNED_16,
-     * UNSIGNED_32, UNSIGNED_64, BOOLEAN) are supported.
-     *
-     * @param rs The context associated with the new Element.
-     * @param dt The DataType for the new Element.
-     * @param size Vector size for the new Element.  Range 2-4 inclusive
-     *             supported.
-     *
-     * @return Element
-     */
-    public static Element createVector(RenderScript rs, DataType dt, int size) {
-        if (size < 2 || size > 4) {
-            throw new RSIllegalArgumentException("Vector size out of range 2-4.");
-        }
-
-        switch (dt) {
-        // Support only primitive integer/float/boolean types as vectors.
-        case FLOAT_32:
-        case FLOAT_64:
-        case SIGNED_8:
-        case SIGNED_16:
-        case SIGNED_32:
-        case SIGNED_64:
-        case UNSIGNED_8:
-        case UNSIGNED_16:
-        case UNSIGNED_32:
-        case UNSIGNED_64:
-        case BOOLEAN: {
-            DataKind dk = DataKind.USER;
-            boolean norm = false;
-            long id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
-            return new Element(id, rs, dt, dk, norm, size);
-        }
-
-        default: {
-            throw new RSIllegalArgumentException("Cannot create vector of " +
-                "non-primitive type.");
-        }
-        }
-    }
-
-    /**
-     * Create a new pixel Element type.  A matching DataType and DataKind must
-     * be provided.  The DataType and DataKind must contain the same number of
-     * components.  Vector size will be set to 1.
-     *
-     * @param rs The context associated with the new Element.
-     * @param dt The DataType for the new element.
-     * @param dk The DataKind to specify the mapping of each component in the
-     *           DataType.
-     *
-     * @return Element
-     */
-    public static Element createPixel(RenderScript rs, DataType dt, DataKind dk) {
-        if (!(dk == DataKind.PIXEL_L ||
-              dk == DataKind.PIXEL_A ||
-              dk == DataKind.PIXEL_LA ||
-              dk == DataKind.PIXEL_RGB ||
-              dk == DataKind.PIXEL_RGBA ||
-              dk == DataKind.PIXEL_DEPTH ||
-              dk == DataKind.PIXEL_YUV)) {
-            throw new RSIllegalArgumentException("Unsupported DataKind");
-        }
-        if (!(dt == DataType.UNSIGNED_8 ||
-              dt == DataType.UNSIGNED_16 ||
-              dt == DataType.UNSIGNED_5_6_5 ||
-              dt == DataType.UNSIGNED_4_4_4_4 ||
-              dt == DataType.UNSIGNED_5_5_5_1)) {
-            throw new RSIllegalArgumentException("Unsupported DataType");
-        }
-        if (dt == DataType.UNSIGNED_5_6_5 && dk != DataKind.PIXEL_RGB) {
-            throw new RSIllegalArgumentException("Bad kind and type combo");
-        }
-        if (dt == DataType.UNSIGNED_5_5_5_1 && dk != DataKind.PIXEL_RGBA) {
-            throw new RSIllegalArgumentException("Bad kind and type combo");
-        }
-        if (dt == DataType.UNSIGNED_4_4_4_4 && dk != DataKind.PIXEL_RGBA) {
-            throw new RSIllegalArgumentException("Bad kind and type combo");
-        }
-        if (dt == DataType.UNSIGNED_16 &&
-            dk != DataKind.PIXEL_DEPTH) {
-            throw new RSIllegalArgumentException("Bad kind and type combo");
-        }
-
-        int size = 1;
-        switch (dk) {
-        case PIXEL_LA:
-            size = 2;
-            break;
-        case PIXEL_RGB:
-            size = 3;
-            break;
-        case PIXEL_RGBA:
-            size = 4;
-            break;
-        }
-
-        boolean norm = true;
-        long id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
-        return new Element(id, rs, dt, dk, norm, size);
-    }
-
-    /**
-     * Check if the current Element is compatible with another Element.
-     * Primitive Elements are compatible if they share the same underlying
-     * size and type (i.e. U8 is compatible with A_8). User-defined Elements
-     * must be equal in order to be compatible. This requires strict name
-     * equivalence for all sub-Elements (in addition to structural equivalence).
-     *
-     * @param e The Element to check compatibility with.
-     *
-     * @return boolean true if the Elements are compatible, otherwise false.
-     */
-    public boolean isCompatible(Element e) {
-        // Try strict BaseObj equality to start with.
-        if (this.equals(e)) {
-            return true;
-        }
-
-        // Ignore mKind because it is allowed to be different (user vs. pixel).
-        // We also ignore mNormalized because it can be different. The mType
-        // field must not be NONE since we require name equivalence for
-        // all user-created Elements.
-        return ((mSize == e.mSize) &&
-                (mType != DataType.NONE) &&
-                (mType == e.mType) &&
-                (mVectorSize == e.mVectorSize));
-    }
-
-    /**
-     * Builder class for producing complex elements with matching field and name
-     * pairs.  The builder starts empty.  The order in which elements are added
-     * is retained for the layout in memory.
-     *
-     */
-    public static class Builder {
-
-        RenderScript mRS;
-        Element[] mElements;
-        String[] mElementNames;
-        int[] mArraySizes;
-        int mCount;
-        int mSkipPadding;
-
-        /**
-         * Create a builder object.
-         *
-         * @param rs
-         */
-        public Builder(RenderScript rs) {
-            mRS = rs;
-            mCount = 0;
-            mElements = new Element[8];
-            mElementNames = new String[8];
-            mArraySizes = new int[8];
-        }
-
-        /**
-         * Add an array of elements to this element.
-         *
-         * @param element
-         * @param name
-         * @param arraySize
-         */
-        public Builder add(Element element, String name, int arraySize) {
-            if (arraySize < 1) {
-                throw new RSIllegalArgumentException("Array size cannot be less than 1.");
-            }
-
-            // Skip padding fields after a vector 3 type.
-            if (mSkipPadding != 0) {
-                if (name.startsWith("#padding_")) {
-                    mSkipPadding = 0;
-                    return this;
-                }
-            }
-
-            if (element.mVectorSize == 3) {
-                mSkipPadding = 1;
-            } else {
-                mSkipPadding = 0;
-            }
-
-            if(mCount == mElements.length) {
-                Element[] e = new Element[mCount + 8];
-                String[] s = new String[mCount + 8];
-                int[] as = new int[mCount + 8];
-                System.arraycopy(mElements, 0, e, 0, mCount);
-                System.arraycopy(mElementNames, 0, s, 0, mCount);
-                System.arraycopy(mArraySizes, 0, as, 0, mCount);
-                mElements = e;
-                mElementNames = s;
-                mArraySizes = as;
-            }
-            mElements[mCount] = element;
-            mElementNames[mCount] = name;
-            mArraySizes[mCount] = arraySize;
-            mCount++;
-
-            return this;
-        }
-
-        /**
-         * Add a single element to this Element.
-         *
-         * @param element
-         * @param name
-         */
-        public Builder add(Element element, String name) {
-            return add(element, name, 1);
-        }
-
-        /**
-         * Create the element from this builder.
-         *
-         *
-         * @return Element
-         */
-        public Element create() {
-            mRS.validate();
-            Element[] ein = new Element[mCount];
-            String[] sin = new String[mCount];
-            int[] asin = new int[mCount];
-            java.lang.System.arraycopy(mElements, 0, ein, 0, mCount);
-            java.lang.System.arraycopy(mElementNames, 0, sin, 0, mCount);
-            java.lang.System.arraycopy(mArraySizes, 0, asin, 0, mCount);
-
-            long[] ids = new long[ein.length];
-            for (int ct = 0; ct < ein.length; ct++ ) {
-                ids[ct] = ein[ct].getID(mRS);
-            }
-
-            long id = mRS.nElementCreate2(ids, sin, asin);
-            return new Element(id, mRS, ein, sin, asin);
-        }
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java b/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java
deleted file mode 100644
index 5f61920..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/FieldPacker.java
+++ /dev/null
@@ -1,946 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.support.v8.renderscript.RenderScript;
-import java.util.BitSet;
-
-/**
- * Utility class for packing arguments and structures from Android system objects to
- * RenderScript objects.
- *
- * This class is only intended to be used to support the
- * reflected code generated by the RS tool chain.  It should not
- * be called directly.
- *
- **/
-public class FieldPacker {
-    public FieldPacker(int len) {
-        mPos = 0;
-        mLen = len;
-        mData = new byte[len];
-        mAlignment = new BitSet();
-    }
-
-    public FieldPacker(byte[] data) {
-        // Advance mPos to the end of the buffer, since we are copying in the
-        // full data input.
-        mPos = data.length;
-        mLen = data.length;
-        mData = data;
-        mAlignment = new BitSet();
-        // TODO: We should either have an actual FieldPacker copy constructor
-        // or drop support for computing alignment like this. As it stands,
-        // subAlign() can never work correctly for copied FieldPacker objects.
-    }
-
-    static FieldPacker createFromArray(Object[] args) {
-        FieldPacker fp = new FieldPacker(RenderScript.sPointerSize * 8);
-        for (Object arg : args) {
-            fp.addSafely(arg);
-        }
-        fp.resize(fp.mPos);
-        return fp;
-    }
-
-    public void align(int v) {
-        if ((v <= 0) || ((v & (v - 1)) != 0)) {
-            throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
-        }
-
-        while ((mPos & (v - 1)) != 0) {
-            mAlignment.flip(mPos);
-            mData[mPos++] = 0;
-        }
-    }
-
-    public void subalign(int v) {
-        if ((v & (v - 1)) != 0) {
-            throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
-        }
-
-        while ((mPos & (v - 1)) != 0) {
-            mPos--;
-        }
-
-        if (mPos > 0) {
-            while (mAlignment.get(mPos - 1) == true) {
-                mPos--;
-                mAlignment.flip(mPos);
-            }
-        }
-
-    }
-
-    public void reset() {
-        mPos = 0;
-    }
-    public void reset(int i) {
-        if ((i < 0) || (i > mLen)) {
-            throw new RSIllegalArgumentException("out of range argument: " + i);
-        }
-        mPos = i;
-    }
-
-    public void skip(int i) {
-        int res = mPos + i;
-        if ((res < 0) || (res > mLen)) {
-            throw new RSIllegalArgumentException("out of range argument: " + i);
-        }
-        mPos = res;
-    }
-
-    public void addI8(byte v) {
-        mData[mPos++] = v;
-    }
-
-    public byte subI8() {
-        subalign(1);
-        return mData[--mPos];
-    }
-
-    public void addI16(short v) {
-        align(2);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)(v >> 8);
-    }
-
-    public short subI16() {
-        subalign(2);
-        short v = 0;
-        v = (short)((mData[--mPos] & 0xff) << 8);
-        v = (short)(v | (short)(mData[--mPos] & 0xff));
-        return v;
-    }
-
-
-    public void addI32(int v) {
-        align(4);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)((v >> 8) & 0xff);
-        mData[mPos++] = (byte)((v >> 16) & 0xff);
-        mData[mPos++] = (byte)((v >> 24) & 0xff);
-    }
-
-    public int subI32() {
-        subalign(4);
-        int v = 0;
-        v = ((mData[--mPos] & 0xff) << 24);
-        v = v | ((mData[--mPos] & 0xff) << 16);
-        v = v | ((mData[--mPos] & 0xff) << 8);
-        v = v | ((mData[--mPos] & 0xff));
-        return v;
-    }
-
-
-    public void addI64(long v) {
-        align(8);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)((v >> 8) & 0xff);
-        mData[mPos++] = (byte)((v >> 16) & 0xff);
-        mData[mPos++] = (byte)((v >> 24) & 0xff);
-        mData[mPos++] = (byte)((v >> 32) & 0xff);
-        mData[mPos++] = (byte)((v >> 40) & 0xff);
-        mData[mPos++] = (byte)((v >> 48) & 0xff);
-        mData[mPos++] = (byte)((v >> 56) & 0xff);
-    }
-
-    public long subI64() {
-        subalign(8);
-        long v = 0;
-        byte x = 0;
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 56l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 48l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 40l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 32l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 24l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 16l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff) << 8l);
-        x = ((mData[--mPos]));
-        v = (long)(v | (((long)x) & 0xff));
-        return v;
-    }
-
-    public void addU8(short v) {
-        if ((v < 0) || (v > 0xff)) {
-            android.util.Log.e("rs", "FieldPacker.addU8( " + v + " )");
-            throw new IllegalArgumentException("Saving value out of range for type");
-        }
-        mData[mPos++] = (byte)v;
-    }
-
-    public void addU16(int v) {
-        if ((v < 0) || (v > 0xffff)) {
-            android.util.Log.e("rs", "FieldPacker.addU16( " + v + " )");
-            throw new IllegalArgumentException("Saving value out of range for type");
-        }
-        align(2);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)(v >> 8);
-    }
-
-    public void addU32(long v) {
-        if ((v < 0) || (v > 0xffffffffL)) {
-            android.util.Log.e("rs", "FieldPacker.addU32( " + v + " )");
-            throw new IllegalArgumentException("Saving value out of range for type");
-        }
-        align(4);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)((v >> 8) & 0xff);
-        mData[mPos++] = (byte)((v >> 16) & 0xff);
-        mData[mPos++] = (byte)((v >> 24) & 0xff);
-    }
-
-    public void addU64(long v) {
-        if (v < 0) {
-            android.util.Log.e("rs", "FieldPacker.addU64( " + v + " )");
-            throw new IllegalArgumentException("Saving value out of range for type");
-        }
-        align(8);
-        mData[mPos++] = (byte)(v & 0xff);
-        mData[mPos++] = (byte)((v >> 8) & 0xff);
-        mData[mPos++] = (byte)((v >> 16) & 0xff);
-        mData[mPos++] = (byte)((v >> 24) & 0xff);
-        mData[mPos++] = (byte)((v >> 32) & 0xff);
-        mData[mPos++] = (byte)((v >> 40) & 0xff);
-        mData[mPos++] = (byte)((v >> 48) & 0xff);
-        mData[mPos++] = (byte)((v >> 56) & 0xff);
-    }
-
-    public void addF32(float v) {
-        addI32(Float.floatToRawIntBits(v));
-    }
-
-    public float subF32() {
-        return Float.intBitsToFloat(subI32());
-    }
-
-    public void addF64(double v) {
-        addI64(Double.doubleToRawLongBits(v));
-    }
-
-    public double subF64() {
-        return Double.longBitsToDouble(subI64());
-    }
-
-    public void addObj(BaseObj obj) {
-        if (obj != null) {
-            if (RenderScript.sPointerSize == 8) {
-                addI64(obj.getID(null));
-                addI64(0);
-                addI64(0);
-                addI64(0);
-            } else {
-                addI32((int)obj.getID(null));
-            }
-        } else {
-            if (RenderScript.sPointerSize == 8) {
-                addI64(0);
-                addI64(0);
-                addI64(0);
-                addI64(0);
-            } else {
-                addI32(0);
-            }
-        }
-    }
-
-    public void addF32(Float2 v) {
-        addF32(v.x);
-        addF32(v.y);
-    }
-    public void addF32(Float3 v) {
-        addF32(v.x);
-        addF32(v.y);
-        addF32(v.z);
-    }
-    public void addF32(Float4 v) {
-        addF32(v.x);
-        addF32(v.y);
-        addF32(v.z);
-        addF32(v.w);
-    }
-
-    public void addF64(Double2 v) {
-        addF64(v.x);
-        addF64(v.y);
-    }
-    public void addF64(Double3 v) {
-        addF64(v.x);
-        addF64(v.y);
-        addF64(v.z);
-    }
-    public void addF64(Double4 v) {
-        addF64(v.x);
-        addF64(v.y);
-        addF64(v.z);
-        addF64(v.w);
-    }
-
-    public void addI8(Byte2 v) {
-        addI8(v.x);
-        addI8(v.y);
-    }
-    public void addI8(Byte3 v) {
-        addI8(v.x);
-        addI8(v.y);
-        addI8(v.z);
-    }
-    public void addI8(Byte4 v) {
-        addI8(v.x);
-        addI8(v.y);
-        addI8(v.z);
-        addI8(v.w);
-    }
-
-    public void addU8(Short2 v) {
-        addU8(v.x);
-        addU8(v.y);
-    }
-    public void addU8(Short3 v) {
-        addU8(v.x);
-        addU8(v.y);
-        addU8(v.z);
-    }
-    public void addU8(Short4 v) {
-        addU8(v.x);
-        addU8(v.y);
-        addU8(v.z);
-        addU8(v.w);
-    }
-
-    public void addI16(Short2 v) {
-        addI16(v.x);
-        addI16(v.y);
-    }
-    public void addI16(Short3 v) {
-        addI16(v.x);
-        addI16(v.y);
-        addI16(v.z);
-    }
-    public void addI16(Short4 v) {
-        addI16(v.x);
-        addI16(v.y);
-        addI16(v.z);
-        addI16(v.w);
-    }
-
-    public void addU16(Int2 v) {
-        addU16(v.x);
-        addU16(v.y);
-    }
-    public void addU16(Int3 v) {
-        addU16(v.x);
-        addU16(v.y);
-        addU16(v.z);
-    }
-    public void addU16(Int4 v) {
-        addU16(v.x);
-        addU16(v.y);
-        addU16(v.z);
-        addU16(v.w);
-    }
-
-    public void addI32(Int2 v) {
-        addI32(v.x);
-        addI32(v.y);
-    }
-    public void addI32(Int3 v) {
-        addI32(v.x);
-        addI32(v.y);
-        addI32(v.z);
-    }
-    public void addI32(Int4 v) {
-        addI32(v.x);
-        addI32(v.y);
-        addI32(v.z);
-        addI32(v.w);
-    }
-
-    public void addU32(Long2 v) {
-        addU32(v.x);
-        addU32(v.y);
-    }
-    public void addU32(Long3 v) {
-        addU32(v.x);
-        addU32(v.y);
-        addU32(v.z);
-    }
-    public void addU32(Long4 v) {
-        addU32(v.x);
-        addU32(v.y);
-        addU32(v.z);
-        addU32(v.w);
-    }
-
-    public void addI64(Long2 v) {
-        addI64(v.x);
-        addI64(v.y);
-    }
-    public void addI64(Long3 v) {
-        addI64(v.x);
-        addI64(v.y);
-        addI64(v.z);
-    }
-    public void addI64(Long4 v) {
-        addI64(v.x);
-        addI64(v.y);
-        addI64(v.z);
-        addI64(v.w);
-    }
-
-    public void addU64(Long2 v) {
-        addU64(v.x);
-        addU64(v.y);
-    }
-    public void addU64(Long3 v) {
-        addU64(v.x);
-        addU64(v.y);
-        addU64(v.z);
-    }
-    public void addU64(Long4 v) {
-        addU64(v.x);
-        addU64(v.y);
-        addU64(v.z);
-        addU64(v.w);
-    }
-
-
-    public Float2 subFloat2() {
-        Float2 v = new Float2();
-        v.y = subF32();
-        v.x = subF32();
-        return v;
-    }
-    public Float3 subFloat3() {
-        Float3 v = new Float3();
-        v.z = subF32();
-        v.y = subF32();
-        v.x = subF32();
-        return v;
-    }
-    public Float4 subFloat4() {
-        Float4 v = new Float4();
-        v.w = subF32();
-        v.z = subF32();
-        v.y = subF32();
-        v.x = subF32();
-        return v;
-    }
-
-    public Double2 subDouble2() {
-        Double2 v = new Double2();
-        v.y = subF64();
-        v.x = subF64();
-        return v;
-    }
-    public Double3 subDouble3() {
-        Double3 v = new Double3();
-        v.z = subF64();
-        v.y = subF64();
-        v.x = subF64();
-        return v;
-    }
-    public Double4 subDouble4() {
-        Double4 v = new Double4();
-        v.w = subF64();
-        v.z = subF64();
-        v.y = subF64();
-        v.x = subF64();
-        return v;
-    }
-
-    public Byte2 subByte2() {
-        Byte2 v = new Byte2();
-        v.y = subI8();
-        v.x = subI8();
-        return v;
-    }
-    public Byte3 subByte3() {
-        Byte3 v = new Byte3();
-        v.z = subI8();
-        v.y = subI8();
-        v.x = subI8();
-        return v;
-    }
-    public Byte4 subByte4() {
-        Byte4 v = new Byte4();
-        v.w = subI8();
-        v.z = subI8();
-        v.y = subI8();
-        v.x = subI8();
-        return v;
-    }
-
-    public Short2 subShort2() {
-        Short2 v = new Short2();
-        v.y = subI16();
-        v.x = subI16();
-        return v;
-    }
-    public Short3 subShort3() {
-        Short3 v = new Short3();
-        v.z = subI16();
-        v.y = subI16();
-        v.x = subI16();
-        return v;
-    }
-    public Short4 subShort4() {
-        Short4 v = new Short4();
-        v.w = subI16();
-        v.z = subI16();
-        v.y = subI16();
-        v.x = subI16();
-        return v;
-    }
-
-    public Int2 subInt2() {
-        Int2 v = new Int2();
-        v.y = subI32();
-        v.x = subI32();
-        return v;
-    }
-    public Int3 subInt3() {
-        Int3 v = new Int3();
-        v.z = subI32();
-        v.y = subI32();
-        v.x = subI32();
-        return v;
-    }
-    public Int4 subInt4() {
-        Int4 v = new Int4();
-        v.w = subI32();
-        v.z = subI32();
-        v.y = subI32();
-        v.x = subI32();
-        return v;
-    }
-
-    public Long2 subLong2() {
-        Long2 v = new Long2();
-        v.y = subI64();
-        v.x = subI64();
-        return v;
-    }
-    public Long3 subLong3() {
-        Long3 v = new Long3();
-        v.z = subI64();
-        v.y = subI64();
-        v.x = subI64();
-        return v;
-    }
-    public Long4 subLong4() {
-        Long4 v = new Long4();
-        v.w = subI64();
-        v.z = subI64();
-        v.y = subI64();
-        v.x = subI64();
-        return v;
-    }
-
-
-
-    public void addMatrix(Matrix4f v) {
-        for (int i=0; i < v.mMat.length; i++) {
-            addF32(v.mMat[i]);
-        }
-    }
-
-    public Matrix4f subMatrix4f() {
-        Matrix4f v = new Matrix4f();
-        for (int i = v.mMat.length - 1; i >= 0; i--) {
-            v.mMat[i] = subF32();
-        }
-        return v;
-    }
-
-    public void addMatrix(Matrix3f v) {
-        for (int i=0; i < v.mMat.length; i++) {
-            addF32(v.mMat[i]);
-        }
-    }
-
-    public Matrix3f subMatrix3f() {
-        Matrix3f v = new Matrix3f();
-        for (int i = v.mMat.length - 1; i >= 0; i--) {
-            v.mMat[i] = subF32();
-        }
-        return v;
-    }
-
-    public void addMatrix(Matrix2f v) {
-        for (int i=0; i < v.mMat.length; i++) {
-            addF32(v.mMat[i]);
-        }
-    }
-
-    public Matrix2f subMatrix2f() {
-        Matrix2f v = new Matrix2f();
-        for (int i = v.mMat.length - 1; i >= 0; i--) {
-            v.mMat[i] = subF32();
-        }
-        return v;
-    }
-
-    public void addBoolean(boolean v) {
-        addI8((byte)(v ? 1 : 0));
-    }
-
-    public boolean subBoolean() {
-        byte v = subI8();
-        if (v == 1) {
-            return true;
-        }
-        return false;
-    }
-
-    public final byte[] getData() {
-        return mData;
-    }
-
-    /**
-     * Get the actual length used for the FieldPacker.
-     *
-     * @hide
-     */
-    public int getPos() {
-        return mPos;
-    }
-
-    private static void addToPack(FieldPacker fp, Object obj) {
-        if (obj instanceof Boolean) {
-            fp.addBoolean(((Boolean)obj).booleanValue());
-            return;
-        }
-
-        if (obj instanceof Byte) {
-            fp.addI8(((Byte)obj).byteValue());
-            return;
-        }
-
-        if (obj instanceof Short) {
-            fp.addI16(((Short)obj).shortValue());
-            return;
-        }
-
-        if (obj instanceof Integer) {
-            fp.addI32(((Integer)obj).intValue());
-            return;
-        }
-
-        if (obj instanceof Long) {
-            fp.addI64(((Long)obj).longValue());
-            return;
-        }
-
-        if (obj instanceof Float) {
-            fp.addF32(((Float)obj).floatValue());
-            return;
-        }
-
-        if (obj instanceof Double) {
-            fp.addF64(((Double)obj).doubleValue());
-            return;
-        }
-
-        if (obj instanceof Byte2) {
-            fp.addI8((Byte2)obj);
-            return;
-        }
-
-        if (obj instanceof Byte3) {
-            fp.addI8((Byte3)obj);
-            return;
-        }
-
-        if (obj instanceof Byte4) {
-            fp.addI8((Byte4)obj);
-            return;
-        }
-
-        if (obj instanceof Short2) {
-            fp.addI16((Short2)obj);
-            return;
-        }
-
-        if (obj instanceof Short3) {
-            fp.addI16((Short3)obj);
-            return;
-        }
-
-        if (obj instanceof Short4) {
-            fp.addI16((Short4)obj);
-            return;
-        }
-
-        if (obj instanceof Int2) {
-            fp.addI32((Int2)obj);
-            return;
-        }
-
-        if (obj instanceof Int3) {
-            fp.addI32((Int3)obj);
-            return;
-        }
-
-        if (obj instanceof Int4) {
-            fp.addI32((Int4)obj);
-            return;
-        }
-
-        if (obj instanceof Long2) {
-            fp.addI64((Long2)obj);
-            return;
-        }
-
-        if (obj instanceof Long3) {
-            fp.addI64((Long3)obj);
-            return;
-        }
-
-        if (obj instanceof Long4) {
-            fp.addI64((Long4)obj);
-            return;
-        }
-
-        if (obj instanceof Float2) {
-            fp.addF32((Float2)obj);
-            return;
-        }
-
-        if (obj instanceof Float3) {
-            fp.addF32((Float3)obj);
-            return;
-        }
-
-        if (obj instanceof Float4) {
-            fp.addF32((Float4)obj);
-            return;
-        }
-
-        if (obj instanceof Double2) {
-            fp.addF64((Double2)obj);
-            return;
-        }
-
-        if (obj instanceof Double3) {
-            fp.addF64((Double3)obj);
-            return;
-        }
-
-        if (obj instanceof Double4) {
-            fp.addF64((Double4)obj);
-            return;
-        }
-
-        if (obj instanceof Matrix2f) {
-            fp.addMatrix((Matrix2f)obj);
-            return;
-        }
-
-        if (obj instanceof Matrix3f) {
-            fp.addMatrix((Matrix3f)obj);
-            return;
-        }
-
-        if (obj instanceof Matrix4f) {
-            fp.addMatrix((Matrix4f)obj);
-            return;
-        }
-
-        if (obj instanceof BaseObj) {
-            fp.addObj((BaseObj)obj);
-            return;
-        }
-    }
-
-    private static int getPackedSize(Object obj) {
-        if (obj instanceof Boolean) {
-            return 1;
-        }
-
-        if (obj instanceof Byte) {
-            return 1;
-        }
-
-        if (obj instanceof Short) {
-            return 2;
-        }
-
-        if (obj instanceof Integer) {
-            return 4;
-        }
-
-        if (obj instanceof Long) {
-            return 8;
-        }
-
-        if (obj instanceof Float) {
-            return 4;
-        }
-
-        if (obj instanceof Double) {
-            return 8;
-        }
-
-        if (obj instanceof Byte2) {
-            return 2;
-        }
-
-        if (obj instanceof Byte3) {
-            return 3;
-        }
-
-        if (obj instanceof Byte4) {
-            return 4;
-        }
-
-        if (obj instanceof Short2) {
-            return 4;
-        }
-
-        if (obj instanceof Short3) {
-            return 6;
-        }
-
-        if (obj instanceof Short4) {
-            return 8;
-        }
-
-        if (obj instanceof Int2) {
-            return 8;
-        }
-
-        if (obj instanceof Int3) {
-            return 12;
-        }
-
-        if (obj instanceof Int4) {
-            return 16;
-        }
-
-        if (obj instanceof Long2) {
-            return 16;
-        }
-
-        if (obj instanceof Long3) {
-            return 24;
-        }
-
-        if (obj instanceof Long4) {
-            return 32;
-        }
-
-        if (obj instanceof Float2) {
-            return 8;
-        }
-
-        if (obj instanceof Float3) {
-            return 12;
-        }
-
-        if (obj instanceof Float4) {
-            return 16;
-        }
-
-        if (obj instanceof Double2) {
-            return 16;
-        }
-
-        if (obj instanceof Double3) {
-            return 24;
-        }
-
-        if (obj instanceof Double4) {
-            return 32;
-        }
-
-        if (obj instanceof Matrix2f) {
-            return 16;
-        }
-
-        if (obj instanceof Matrix3f) {
-            return 36;
-        }
-
-        if (obj instanceof Matrix4f) {
-            return 64;
-        }
-
-        if (obj instanceof BaseObj) {
-            if (RenderScript.sPointerSize == 8) {
-                return 32;
-            } else {
-                return 4;
-            }
-        }
-
-        return 0;
-    }
-
-    static FieldPacker createFieldPack(Object[] args) {
-        int len = 0;
-        for (Object arg : args) {
-            len += getPackedSize(arg);
-        }
-        FieldPacker fp = new FieldPacker(len);
-        for (Object arg : args) {
-            addToPack(fp, arg);
-        }
-        return fp;
-    }
-
-
-    private boolean resize(int newSize) {
-        if (newSize == mLen) {
-            return false;
-        }
-
-        byte[] newData = new byte[newSize];
-        System.arraycopy(mData, 0, newData, 0, mPos);
-        mData = newData;
-        mLen = newSize;
-        return true;
-    }
-
-    private void addSafely(Object obj) {
-        boolean retry;
-        final int oldPos = mPos;
-        do {
-            retry = false;
-            try {
-                addToPack(this, obj);
-            } catch (ArrayIndexOutOfBoundsException e) {
-                mPos = oldPos;
-                resize(mLen * 2);
-                retry = true;
-            }
-        } while (retry);
-    }
-
-    private byte mData[];
-    private int mPos;
-    private int mLen;
-    private BitSet mAlignment;
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Float2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Float2.java
deleted file mode 100644
index edbc5aa..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Float2.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript float2 type back to the Android system.
- *
- **/
-public class Float2 {
-    public Float2() {
-    }
-
-    public Float2(float initX, float initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public float x;
-    public float y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Float3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Float3.java
deleted file mode 100644
index 90162a1..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Float3.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript float2 type back to the Android system.
- *
- **/
-public class Float3 {
-    public Float3() {
-    }
-    public Float3(float initX, float initY, float initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public float x;
-    public float y;
-    public float z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Float4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Float4.java
deleted file mode 100644
index d0dc568..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Float4.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript float2 type back to the Android system.
- *
- **/
-public class Float4 {
-    public Float4() {
-    }
-
-    public Float4(float initX, float initY, float initZ, float initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public float x;
-    public float y;
-    public float z;
-    public float w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Int2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Int2.java
deleted file mode 100644
index e5d04b8..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Int2.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript int2 type back to the Android system.
- *
- **/
-public class Int2 {
-    public Int2() {
-    }
-
-    public Int2(int initX, int initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public int x;
-    public int y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Int3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Int3.java
deleted file mode 100644
index 12f59e8..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Int3.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript int3 type back to the Android system.
- *
- **/
-public class Int3 {
-    public Int3() {
-    }
-
-    public Int3(int initX, int initY, int initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public int x;
-    public int y;
-    public int z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Int4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Int4.java
deleted file mode 100644
index bb49fb1..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Int4.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript int4 type back to the Android system.
- *
- **/
-public class Int4 {
-    public Int4() {
-    }
-
-    public Int4(int initX, int initY, int initZ, int initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public int x;
-    public int y;
-    public int z;
-    public int w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Long2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Long2.java
deleted file mode 100644
index b3c95f2..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Long2.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript long2 type back to the Android system.
- **/
-public class Long2 {
-    public Long2() {
-    }
-
-    public Long2(long initX, long initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public long x;
-    public long y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Long3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Long3.java
deleted file mode 100644
index 6ce0f83..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Long3.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript long3 type back to the Android system.
- **/
-public class Long3 {
-    public Long3() {
-    }
-
-    public Long3(long initX, long initY, long initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public long x;
-    public long y;
-    public long z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Long4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Long4.java
deleted file mode 100644
index d44a321..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Long4.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript long4 type back to the Android system.
- **/
-public class Long4 {
-    public Long4() {
-    }
-
-    public Long4(long initX, long initY, long initZ, long initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public long x;
-    public long y;
-    public long z;
-    public long w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix2f.java b/v8/renderscript/java/src/android/support/v8/renderscript/Matrix2f.java
deleted file mode 100644
index 9a8b5bf..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix2f.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2009-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript rs_matrix2x2 type back to the Android system.
- *
- **/
-public class Matrix2f {
-
-    /**
-    * Creates a new identity 2x2 matrix
-    */
-    public Matrix2f() {
-        mMat = new float[4];
-        loadIdentity();
-    }
-
-    /**
-    * Creates a new matrix and sets its values from the given
-    * parameter
-    *
-    * @param dataArray values to set the matrix to, must be 4
-    *                  floats long
-    */
-    public Matrix2f(float[] dataArray) {
-        mMat = new float[4];
-        System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Return a reference to the internal array representing matrix
-    * values. Modifying this array will also change the matrix
-    *
-    * @return internal array representing the matrix
-    */
-    public float[] getArray() {
-        return mMat;
-    }
-
-    /**
-    * Returns the value for a given row and column
-    *
-    * @param x column of the value to return
-    * @param y row of the value to return
-    *
-    * @return value in the yth row and xth column
-    */
-    public float get(int x, int y) {
-        return mMat[x*2 + y];
-    }
-
-    /**
-    * Sets the value for a given row and column
-    *
-    * @param x column of the value to set
-    * @param y row of the value to set
-    */
-    public void set(int x, int y, float v) {
-        mMat[x*2 + y] = v;
-    }
-
-    /**
-    * Sets the matrix values to identity
-    */
-    public void loadIdentity() {
-        mMat[0] = 1;
-        mMat[1] = 0;
-
-        mMat[2] = 0;
-        mMat[3] = 1;
-    }
-
-    /**
-    * Sets the values of the matrix to those of the parameter
-    *
-    * @param src matrix to load the values from
-    */
-    public void load(Matrix2f src) {
-        System.arraycopy(src.getArray(), 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Sets current values to be a rotation matrix of given angle
-    *
-    * @param rot rotation angle
-    */
-    public void loadRotate(float rot) {
-        float c, s;
-        rot *= (float)(java.lang.Math.PI / 180.0f);
-        c = (float)java.lang.Math.cos(rot);
-        s = (float)java.lang.Math.sin(rot);
-        mMat[0] = c;
-        mMat[1] = -s;
-        mMat[2] = s;
-        mMat[3] = c;
-    }
-
-    /**
-    * Sets current values to be a scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    */
-    public void loadScale(float x, float y) {
-        loadIdentity();
-        mMat[0] = x;
-        mMat[3] = y;
-    }
-
-    /**
-    * Sets current values to be the result of multiplying two given
-    * matrices
-    *
-    * @param lhs left hand side matrix
-    * @param rhs right hand side matrix
-    */
-    public void loadMultiply(Matrix2f lhs, Matrix2f rhs) {
-        for (int i=0 ; i<2 ; i++) {
-            float ri0 = 0;
-            float ri1 = 0;
-            for (int j=0 ; j<2 ; j++) {
-                float rhs_ij = rhs.get(i,j);
-                ri0 += lhs.get(j,0) * rhs_ij;
-                ri1 += lhs.get(j,1) * rhs_ij;
-            }
-            set(i,0, ri0);
-            set(i,1, ri1);
-        }
-    }
-
-    /**
-    * Post-multiplies the current matrix by a given parameter
-    *
-    * @param rhs right hand side to multiply by
-    */
-    public void multiply(Matrix2f rhs) {
-        Matrix2f tmp = new Matrix2f();
-        tmp.loadMultiply(this, rhs);
-        load(tmp);
-    }
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * rotation matrix of given angle
-    *
-    * @param rot angle of rotation
-    */
-    public void rotate(float rot) {
-        Matrix2f tmp = new Matrix2f();
-        tmp.loadRotate(rot);
-        multiply(tmp);
-    }
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    */
-    public void scale(float x, float y) {
-        Matrix2f tmp = new Matrix2f();
-        tmp.loadScale(x, y);
-        multiply(tmp);
-    }
-    /**
-    * Sets the current matrix to its transpose
-    */
-    public void transpose() {
-        float temp = mMat[1];
-        mMat[1] = mMat[2];
-        mMat[2] = temp;
-    }
-
-    final float[] mMat;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix3f.java b/v8/renderscript/java/src/android/support/v8/renderscript/Matrix3f.java
deleted file mode 100644
index 4528543..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix3f.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2009-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript rs_matrix3x3 type back to the Android system.
- *
- **/
-public class Matrix3f {
-
-    /**
-    * Creates a new identity 3x3 matrix
-    */
-    public Matrix3f() {
-        mMat = new float[9];
-        loadIdentity();
-    }
-
-    /**
-    * Creates a new matrix and sets its values from the given
-    * parameter
-    *
-    * @param dataArray values to set the matrix to, must be 9
-    *                  floats long
-    */
-    public Matrix3f(float[] dataArray) {
-        mMat = new float[9];
-        System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Return a reference to the internal array representing matrix
-    * values. Modifying this array will also change the matrix
-    *
-    * @return internal array representing the matrix
-    */
-    public float[] getArray() {
-        return mMat;
-    }
-
-    /**
-    * Returns the value for a given row and column
-    *
-    * @param x column of the value to return
-    * @param y row of the value to return
-    *
-    * @return value in the yth row and xth column
-    */
-    public float get(int x, int y) {
-        return mMat[x*3 + y];
-    }
-
-    /**
-    * Sets the value for a given row and column
-    *
-    * @param x column of the value to set
-    * @param y row of the value to set
-    */
-    public void set(int x, int y, float v) {
-        mMat[x*3 + y] = v;
-    }
-
-    /**
-    * Sets the matrix values to identity
-    */
-    public void loadIdentity() {
-        mMat[0] = 1;
-        mMat[1] = 0;
-        mMat[2] = 0;
-
-        mMat[3] = 0;
-        mMat[4] = 1;
-        mMat[5] = 0;
-
-        mMat[6] = 0;
-        mMat[7] = 0;
-        mMat[8] = 1;
-    }
-
-    /**
-    * Sets the values of the matrix to those of the parameter
-    *
-    * @param src matrix to load the values from
-    */
-    public void load(Matrix3f src) {
-        System.arraycopy(src.getArray(), 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Sets current values to be a rotation matrix of certain angle
-    * about a given axis
-    *
-    * @param rot angle of rotation
-    * @param x rotation axis x
-    * @param y rotation axis y
-    * @param z rotation axis z
-    */
-    public void loadRotate(float rot, float x, float y, float z) {
-        float c, s;
-        rot *= (float)(java.lang.Math.PI / 180.0f);
-        c = (float)java.lang.Math.cos(rot);
-        s = (float)java.lang.Math.sin(rot);
-
-        float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
-        if (!(len != 1)) {
-            float recipLen = 1.f / len;
-            x *= recipLen;
-            y *= recipLen;
-            z *= recipLen;
-        }
-        float nc = 1.0f - c;
-        float xy = x * y;
-        float yz = y * z;
-        float zx = z * x;
-        float xs = x * s;
-        float ys = y * s;
-        float zs = z * s;
-        mMat[0] = x*x*nc +  c;
-        mMat[3] =  xy*nc - zs;
-        mMat[6] =  zx*nc + ys;
-        mMat[1] =  xy*nc + zs;
-        mMat[4] = y*y*nc +  c;
-        mMat[7] =  yz*nc - xs;
-        mMat[2] =  zx*nc - ys;
-        mMat[5] =  yz*nc + xs;
-        mMat[8] = z*z*nc +  c;
-    }
-
-    /**
-    * Makes the upper 2x2 a rotation matrix of the given angle
-    *
-    * @param rot rotation angle
-    */
-    public void loadRotate(float rot) {
-        loadIdentity();
-        float c, s;
-        rot *= (float)(java.lang.Math.PI / 180.0f);
-        c = (float)java.lang.Math.cos(rot);
-        s = (float)java.lang.Math.sin(rot);
-        mMat[0] = c;
-        mMat[1] = -s;
-        mMat[3] = s;
-        mMat[4] = c;
-    }
-
-    /**
-    * Makes the upper 2x2 a scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    */
-    public void loadScale(float x, float y) {
-        loadIdentity();
-        mMat[0] = x;
-        mMat[4] = y;
-    }
-
-    /**
-    * Sets current values to be a scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    * @param z scale component z
-    */
-    public void loadScale(float x, float y, float z) {
-        loadIdentity();
-        mMat[0] = x;
-        mMat[4] = y;
-        mMat[8] = z;
-    }
-
-    /**
-    * Sets current values to be a translation matrix of given
-    * dimensions
-    *
-    * @param x translation component x
-    * @param y translation component y
-    */
-    public void loadTranslate(float x, float y) {
-        loadIdentity();
-        mMat[6] = x;
-        mMat[7] = y;
-    }
-
-    /**
-    * Sets current values to be the result of multiplying two given
-    * matrices
-    *
-    * @param lhs left hand side matrix
-    * @param rhs right hand side matrix
-    */
-    public void loadMultiply(Matrix3f lhs, Matrix3f rhs) {
-        for (int i=0 ; i<3 ; i++) {
-            float ri0 = 0;
-            float ri1 = 0;
-            float ri2 = 0;
-            for (int j=0 ; j<3 ; j++) {
-                float rhs_ij = rhs.get(i,j);
-                ri0 += lhs.get(j,0) * rhs_ij;
-                ri1 += lhs.get(j,1) * rhs_ij;
-                ri2 += lhs.get(j,2) * rhs_ij;
-            }
-            set(i,0, ri0);
-            set(i,1, ri1);
-            set(i,2, ri2);
-        }
-    }
-
-    /**
-    * Post-multiplies the current matrix by a given parameter
-    *
-    * @param rhs right hand side to multiply by
-    */
-    public void multiply(Matrix3f rhs) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadMultiply(this, rhs);
-        load(tmp);
-    }
-
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * rotation matrix of certain angle about a given axis
-    *
-    * @param rot angle of rotation
-    * @param x rotation axis x
-    * @param y rotation axis y
-    * @param z rotation axis z
-    */
-    public void rotate(float rot, float x, float y, float z) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadRotate(rot, x, y, z);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the upper 2x2 of the current matrix by
-    * post-multiplying it with a rotation matrix of given angle
-    *
-    * @param rot angle of rotation
-    */
-    public void rotate(float rot) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadRotate(rot);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the upper 2x2 of the current matrix by
-    * post-multiplying it with a scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    */
-    public void scale(float x, float y) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadScale(x, y);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    * @param z scale component z
-    */
-    public void scale(float x, float y, float z) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadScale(x, y, z);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * translation matrix of given dimensions
-    *
-    * @param x translation component x
-    * @param y translation component y
-    */
-    public void translate(float x, float y) {
-        Matrix3f tmp = new Matrix3f();
-        tmp.loadTranslate(x, y);
-        multiply(tmp);
-    }
-
-    /**
-    * Sets the current matrix to its transpose
-    */
-    public void transpose() {
-        for(int i = 0; i < 2; ++i) {
-            for(int j = i + 1; j < 3; ++j) {
-                float temp = mMat[i*3 + j];
-                mMat[i*3 + j] = mMat[j*3 + i];
-                mMat[j*3 + i] = temp;
-            }
-        }
-    }
-
-    final float[] mMat;
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix4f.java b/v8/renderscript/java/src/android/support/v8/renderscript/Matrix4f.java
deleted file mode 100644
index b9e5636..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Matrix4f.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2009-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript rs_matrix4x4 type back to the Android system.
- *
- **/
-public class Matrix4f {
-
-    /**
-    * Creates a new identity 4x4 matrix
-    */
-    public Matrix4f() {
-        mMat = new float[16];
-        loadIdentity();
-    }
-
-    /**
-    * Creates a new matrix and sets its values from the given
-    * parameter
-    *
-    * @param dataArray values to set the matrix to, must be 16
-    *                  floats long
-    */
-    public Matrix4f(float[] dataArray) {
-        mMat = new float[16];
-        System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Return a reference to the internal array representing matrix
-    * values. Modifying this array will also change the matrix
-    *
-    * @return internal array representing the matrix
-    */
-    public float[] getArray() {
-        return mMat;
-    }
-
-    /**
-    * Returns the value for a given row and column
-    *
-    * @param x column of the value to return
-    * @param y row of the value to return
-    *
-    * @return value in the yth row and xth column
-    */
-    public float get(int x, int y) {
-        return mMat[x*4 + y];
-    }
-
-    /**
-    * Sets the value for a given row and column
-    *
-    * @param x column of the value to set
-    * @param y row of the value to set
-    */
-    public void set(int x, int y, float v) {
-        mMat[x*4 + y] = v;
-    }
-
-    /**
-    * Sets the matrix values to identity
-    */
-    public void loadIdentity() {
-        mMat[0] = 1;
-        mMat[1] = 0;
-        mMat[2] = 0;
-        mMat[3] = 0;
-
-        mMat[4] = 0;
-        mMat[5] = 1;
-        mMat[6] = 0;
-        mMat[7] = 0;
-
-        mMat[8] = 0;
-        mMat[9] = 0;
-        mMat[10] = 1;
-        mMat[11] = 0;
-
-        mMat[12] = 0;
-        mMat[13] = 0;
-        mMat[14] = 0;
-        mMat[15] = 1;
-    }
-
-    /**
-    * Sets the values of the matrix to those of the parameter
-    *
-    * @param src matrix to load the values from
-    */
-    public void load(Matrix4f src) {
-        System.arraycopy(src.getArray(), 0, mMat, 0, mMat.length);
-    }
-
-    /**
-    * Sets the values of the matrix to those of the parameter
-    *
-    * @param src matrix to load the values from
-    * @hide
-    */
-    public void load(Matrix3f src) {
-        mMat[0] = src.mMat[0];
-        mMat[1] = src.mMat[1];
-        mMat[2] = src.mMat[2];
-        mMat[3] = 0;
-
-        mMat[4] = src.mMat[3];
-        mMat[5] = src.mMat[4];
-        mMat[6] = src.mMat[5];
-        mMat[7] = 0;
-
-        mMat[8] = src.mMat[6];
-        mMat[9] = src.mMat[7];
-        mMat[10] = src.mMat[8];
-        mMat[11] = 0;
-
-        mMat[12] = 0;
-        mMat[13] = 0;
-        mMat[14] = 0;
-        mMat[15] = 1;
-    }
-
-    /**
-    * Sets current values to be a rotation matrix of certain angle
-    * about a given axis
-    *
-    * @param rot angle of rotation
-    * @param x rotation axis x
-    * @param y rotation axis y
-    * @param z rotation axis z
-    */
-    public void loadRotate(float rot, float x, float y, float z) {
-        float c, s;
-        mMat[3] = 0;
-        mMat[7] = 0;
-        mMat[11]= 0;
-        mMat[12]= 0;
-        mMat[13]= 0;
-        mMat[14]= 0;
-        mMat[15]= 1;
-        rot *= (float)(java.lang.Math.PI / 180.0f);
-        c = (float)java.lang.Math.cos(rot);
-        s = (float)java.lang.Math.sin(rot);
-
-        float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
-        if (!(len != 1)) {
-            float recipLen = 1.f / len;
-            x *= recipLen;
-            y *= recipLen;
-            z *= recipLen;
-        }
-        float nc = 1.0f - c;
-        float xy = x * y;
-        float yz = y * z;
-        float zx = z * x;
-        float xs = x * s;
-        float ys = y * s;
-        float zs = z * s;
-        mMat[ 0] = x*x*nc +  c;
-        mMat[ 4] =  xy*nc - zs;
-        mMat[ 8] =  zx*nc + ys;
-        mMat[ 1] =  xy*nc + zs;
-        mMat[ 5] = y*y*nc +  c;
-        mMat[ 9] =  yz*nc - xs;
-        mMat[ 2] =  zx*nc - ys;
-        mMat[ 6] =  yz*nc + xs;
-        mMat[10] = z*z*nc +  c;
-    }
-
-    /**
-    * Sets current values to be a scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    * @param z scale component z
-    */
-    public void loadScale(float x, float y, float z) {
-        loadIdentity();
-        mMat[0] = x;
-        mMat[5] = y;
-        mMat[10] = z;
-    }
-
-    /**
-    * Sets current values to be a translation matrix of given
-    * dimensions
-    *
-    * @param x translation component x
-    * @param y translation component y
-    * @param z translation component z
-    */
-    public void loadTranslate(float x, float y, float z) {
-        loadIdentity();
-        mMat[12] = x;
-        mMat[13] = y;
-        mMat[14] = z;
-    }
-
-    /**
-    * Sets current values to be the result of multiplying two given
-    * matrices
-    *
-    * @param lhs left hand side matrix
-    * @param rhs right hand side matrix
-    */
-    public void loadMultiply(Matrix4f lhs, Matrix4f rhs) {
-        for (int i=0 ; i<4 ; i++) {
-            float ri0 = 0;
-            float ri1 = 0;
-            float ri2 = 0;
-            float ri3 = 0;
-            for (int j=0 ; j<4 ; j++) {
-                float rhs_ij = rhs.get(i,j);
-                ri0 += lhs.get(j,0) * rhs_ij;
-                ri1 += lhs.get(j,1) * rhs_ij;
-                ri2 += lhs.get(j,2) * rhs_ij;
-                ri3 += lhs.get(j,3) * rhs_ij;
-            }
-            set(i,0, ri0);
-            set(i,1, ri1);
-            set(i,2, ri2);
-            set(i,3, ri3);
-        }
-    }
-
-    /**
-    * Set current values to be an orthographic projection matrix
-    *
-    * @param l location of the left vertical clipping plane
-    * @param r location of the right vertical clipping plane
-    * @param b location of the bottom horizontal clipping plane
-    * @param t location of the top horizontal clipping plane
-    * @param n location of the near clipping plane
-    * @param f location of the far clipping plane
-    */
-    public void loadOrtho(float l, float r, float b, float t, float n, float f) {
-        loadIdentity();
-        mMat[0] = 2 / (r - l);
-        mMat[5] = 2 / (t - b);
-        mMat[10]= -2 / (f - n);
-        mMat[12]= -(r + l) / (r - l);
-        mMat[13]= -(t + b) / (t - b);
-        mMat[14]= -(f + n) / (f - n);
-    }
-
-    /**
-    * Set current values to be an orthographic projection matrix
-    * with the right and bottom clipping planes set to the given
-    * values. Left and top clipping planes are set to 0. Near and
-    * far are set to -1, 1 respectively
-    *
-    * @param w location of the right vertical clipping plane
-    * @param h location of the bottom horizontal clipping plane
-    *
-    */
-    public void loadOrthoWindow(int w, int h) {
-        loadOrtho(0,w, h,0, -1,1);
-    }
-
-    /**
-    * Sets current values to be a perspective projection matrix
-    *
-    * @param l location of the left vertical clipping plane
-    * @param r location of the right vertical clipping plane
-    * @param b location of the bottom horizontal clipping plane
-    * @param t location of the top horizontal clipping plane
-    * @param n location of the near clipping plane, must be positive
-    * @param f location of the far clipping plane, must be positive
-    *
-    */
-    public void loadFrustum(float l, float r, float b, float t, float n, float f) {
-        loadIdentity();
-        mMat[0] = 2 * n / (r - l);
-        mMat[5] = 2 * n / (t - b);
-        mMat[8] = (r + l) / (r - l);
-        mMat[9] = (t + b) / (t - b);
-        mMat[10]= -(f + n) / (f - n);
-        mMat[11]= -1;
-        mMat[14]= -2*f*n / (f - n);
-        mMat[15]= 0;
-    }
-
-    /**
-    * Sets current values to be a perspective projection matrix
-    *
-    * @param fovy vertical field of view angle in degrees
-    * @param aspect aspect ratio of the screen
-    * @param near near cliping plane, must be positive
-    * @param far far clipping plane, must be positive
-    */
-    public void loadPerspective(float fovy, float aspect, float near, float far) {
-        float top = near * (float)Math.tan((float) (fovy * Math.PI / 360.0f));
-        float bottom = -top;
-        float left = bottom * aspect;
-        float right = top * aspect;
-        loadFrustum(left, right, bottom, top, near, far);
-    }
-
-    /**
-    * Helper function to set the current values to a perspective
-    * projection matrix with aspect ratio defined by the parameters
-    * and (near, far), (bottom, top) mapping to (-1, 1) at z = 0
-    *
-    * @param w screen width
-    * @param h screen height
-    */
-    public void loadProjectionNormalized(int w, int h) {
-        // range -1,1 in the narrow axis at z = 0.
-        Matrix4f m1 = new Matrix4f();
-        Matrix4f m2 = new Matrix4f();
-
-        if(w > h) {
-            float aspect = ((float)w) / h;
-            m1.loadFrustum(-aspect,aspect,  -1,1,  1,100);
-        } else {
-            float aspect = ((float)h) / w;
-            m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
-        }
-
-        m2.loadRotate(180, 0, 1, 0);
-        m1.loadMultiply(m1, m2);
-
-        m2.loadScale(-2, 2, 1);
-        m1.loadMultiply(m1, m2);
-
-        m2.loadTranslate(0, 0, 2);
-        m1.loadMultiply(m1, m2);
-
-        load(m1);
-    }
-
-    /**
-    * Post-multiplies the current matrix by a given parameter
-    *
-    * @param rhs right hand side to multiply by
-    */
-    public void multiply(Matrix4f rhs) {
-        Matrix4f tmp = new Matrix4f();
-        tmp.loadMultiply(this, rhs);
-        load(tmp);
-    }
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * rotation matrix of certain angle about a given axis
-    *
-    * @param rot angle of rotation
-    * @param x rotation axis x
-    * @param y rotation axis y
-    * @param z rotation axis z
-    */
-    public void rotate(float rot, float x, float y, float z) {
-        Matrix4f tmp = new Matrix4f();
-        tmp.loadRotate(rot, x, y, z);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * scale matrix of given dimensions
-    *
-    * @param x scale component x
-    * @param y scale component y
-    * @param z scale component z
-    */
-    public void scale(float x, float y, float z) {
-        Matrix4f tmp = new Matrix4f();
-        tmp.loadScale(x, y, z);
-        multiply(tmp);
-    }
-
-    /**
-    * Modifies the current matrix by post-multiplying it with a
-    * translation matrix of given dimensions
-    *
-    * @param x translation component x
-    * @param y translation component y
-    * @param z translation component z
-    */
-    public void translate(float x, float y, float z) {
-        Matrix4f tmp = new Matrix4f();
-        tmp.loadTranslate(x, y, z);
-        multiply(tmp);
-    }
-    private float computeCofactor(int i, int j) {
-        int c0 = (i+1) % 4;
-        int c1 = (i+2) % 4;
-        int c2 = (i+3) % 4;
-        int r0 = (j+1) % 4;
-        int r1 = (j+2) % 4;
-        int r2 = (j+3) % 4;
-
-        float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] -
-                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r1]))
-                     - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] -
-                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r0]))
-                     + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] -
-                                            mMat[c1 + 4*r1] * mMat[c2 + 4*r0]));
-
-        float cofactor = ((i+j) & 1) != 0 ? -minor : minor;
-        return cofactor;
-    }
-
-    /**
-    * Sets the current matrix to its inverse
-    */
-    public boolean inverse() {
-
-        Matrix4f result = new Matrix4f();
-
-        for (int i = 0; i < 4; ++i) {
-            for (int j = 0; j < 4; ++j) {
-                result.mMat[4*i + j] = computeCofactor(i, j);
-            }
-        }
-
-        // Dot product of 0th column of source and 0th row of result
-        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] +
-                     mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3];
-
-        if (Math.abs(det) < 1e-6) {
-            return false;
-        }
-
-        det = 1.0f / det;
-        for (int i = 0; i < 16; ++i) {
-            mMat[i] = result.mMat[i] * det;
-        }
-
-        return true;
-    }
-
-    /**
-    * Sets the current matrix to its inverse transpose
-    */
-    public boolean inverseTranspose() {
-
-        Matrix4f result = new Matrix4f();
-
-        for (int i = 0; i < 4; ++i) {
-            for (int j = 0; j < 4; ++j) {
-                result.mMat[4*j + i] = computeCofactor(i, j);
-            }
-        }
-
-        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] +
-                     mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12];
-
-        if (Math.abs(det) < 1e-6) {
-            return false;
-        }
-
-        det = 1.0f / det;
-        for (int i = 0; i < 16; ++i) {
-            mMat[i] = result.mMat[i] * det;
-        }
-
-        return true;
-    }
-
-    /**
-    * Sets the current matrix to its transpose
-    */
-    public void transpose() {
-        for(int i = 0; i < 3; ++i) {
-            for(int j = i + 1; j < 4; ++j) {
-                float temp = mMat[i*4 + j];
-                mMat[i*4 + j] = mMat[j*4 + i];
-                mMat[j*4 + i] = temp;
-            }
-        }
-    }
-
-    final float[] mMat;
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RSIllegalArgumentException.java b/v8/renderscript/java/src/android/support/v8/renderscript/RSIllegalArgumentException.java
deleted file mode 100644
index 378a49c..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RSIllegalArgumentException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2010 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.v8.renderscript;
-
-
-/**
- * Base class for all exceptions thrown by the Android
- * RenderScript
- */
-public class RSIllegalArgumentException extends RSRuntimeException {
-    public RSIllegalArgumentException(String string) {
-        super(string);
-    }
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RSInvalidStateException.java b/v8/renderscript/java/src/android/support/v8/renderscript/RSInvalidStateException.java
deleted file mode 100644
index a5676a3..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RSInvalidStateException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2010 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.v8.renderscript;
-
-
-/**
- * Base class for all exceptions thrown by the Android
- * RenderScript
- */
-public class RSInvalidStateException extends RSRuntimeException {
-    public RSInvalidStateException(String string) {
-        super(string);
-    }
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RSRuntimeException.java b/v8/renderscript/java/src/android/support/v8/renderscript/RSRuntimeException.java
deleted file mode 100644
index ec83365..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RSRuntimeException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2010 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.v8.renderscript;
-
-
-/**
- * Base class for all exceptions thrown by the Android
- * RenderScript
- */
-public class RSRuntimeException
-  extends java.lang.RuntimeException {
-    public RSRuntimeException(String string) {
-        super(string);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
deleted file mode 100644
index a5c6f93..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ /dev/null
@@ -1,1721 +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.v8.renderscript;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.ArrayList;
-import java.nio.ByteBuffer;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Process;
-import android.util.Log;
-import android.view.Surface;
-
-/**
- * This class provides access to a RenderScript context, which controls RenderScript
- * initialization, resource management, and teardown. An instance of the RenderScript
- * class must be created before any other RS objects can be created.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about creating an application that uses RenderScript, read the
- * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> developer guide.</p>
- * </div>
- **/
-public class RenderScript {
-    static final String LOG_TAG = "RenderScript_jni";
-    static final boolean DEBUG  = false;
-    @SuppressWarnings({"UnusedDeclaration", "deprecation"})
-    static final boolean LOG_ENABLED = false;
-    static final int SUPPORT_LIB_API = 23;
-    static final int SUPPORT_LIB_VERSION = 2301;
-
-    static private ArrayList<RenderScript> mProcessContextList = new ArrayList<RenderScript>();
-    private boolean mIsProcessContext = false;
-    private boolean mEnableMultiInput = false;
-    private int mDispatchAPILevel = 0;
-
-    private int mContextFlags = 0;
-    private int mContextSdkVersion = 0;
-
-    private Context mApplicationContext;
-    private String mNativeLibDir;
-
-    static private String mBlackList = "";
-     /**
-     * Sets the blackList of Models to only use support lib runtime.
-     * Should be used before context create.
-     *
-     * @param blackList User provided black list string.
-     *
-     * Format: "(MANUFACTURER1:PRODUCT1:MODEL1), (MANUFACTURER2:PRODUCT2:MODEL2)..."
-     * e.g. : To Blacklist Nexus 7(2013) and Nexus 5.
-     *        mBlackList = "(asus:razor:Nexus 7), (LGE:hammerhead:Nexus 5)";
-     */
-    static public void setBlackList(String blackList) {
-        if (blackList != null) {
-            mBlackList = blackList;
-        }
-    }
-     /**
-     * Force using support lib runtime.
-     * Should be used before context create.
-     *
-     */
-    static public void forceCompat() {
-        sNative = 0;
-    }
-    /*
-     * We use a class initializer to allow the native code to cache some
-     * field offsets.
-     */
-    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
-    static boolean sInitialized;
-    static boolean sUseGCHooks;
-    static Object sRuntime;
-    static Method registerNativeAllocation;
-    static Method registerNativeFree;
-
-    static Object lock = new Object();
-
-    // Non-threadsafe functions.
-    native boolean nLoadSO(boolean useNative, int deviceApi, String libPath);
-    native boolean nLoadIOSO();
-    native long nDeviceCreate();
-    native void nDeviceDestroy(long dev);
-    native void nDeviceSetConfig(long dev, int param, int value);
-    native int nContextGetUserMessage(long con, int[] data);
-    native String nContextGetErrorMessage(long con);
-    native int  nContextPeekMessage(long con, int[] subID);
-    native void nContextInitToClient(long con);
-    native void nContextDeinitToClient(long con);
-
-    static private int sNative = -1;
-    static private int sSdkVersion = -1;
-    static private boolean useIOlib = false;
-    static private boolean useNative;
-
-    /*
-     * Context creation flag that specifies a normal context.
-     * RenderScript Support lib only support normal context.
-     */
-    public static final int CREATE_FLAG_NONE = 0x0000;
-
-    int getDispatchAPILevel() {
-        return mDispatchAPILevel;
-    }
-
-    boolean isUseNative() {
-        return useNative;
-    }
-    /*
-     * Detect the bitness of the VM to allow FieldPacker to do the right thing.
-     */
-    static native int rsnSystemGetPointerSize();
-    static int sPointerSize;
-
-    /**
-     * Determines whether or not we should be thunking into the native
-     * RenderScript layer or actually using the compatibility library.
-     */
-    static private boolean setupNative(int sdkVersion, Context ctx) {
-        // if targetSdkVersion is higher than the device api version, always use compat mode.
-        // Workaround for KK
-        if (android.os.Build.VERSION.SDK_INT < sdkVersion &&
-            android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
-            sNative = 0;
-        }
-
-        if (sNative == -1) {
-
-            // get the value of the debug.rs.forcecompat property
-            int forcecompat = 0;
-            try {
-                Class<?> sysprop = Class.forName("android.os.SystemProperties");
-                Class[] signature = {String.class, Integer.TYPE};
-                Method getint = sysprop.getDeclaredMethod("getInt", signature);
-                Object[] args = {"debug.rs.forcecompat", new Integer(0)};
-                forcecompat = ((java.lang.Integer)getint.invoke(null, args)).intValue();
-            } catch (Exception e) {
-
-            }
-
-            if ((android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
-                     && forcecompat == 0) {
-                sNative = 1;
-            } else {
-                sNative = 0;
-            }
-
-
-            if (sNative == 1) {
-                // Workarounds that may disable thunking go here
-                ApplicationInfo info;
-                try {
-                    info = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(),
-                                                                      PackageManager.GET_META_DATA);
-                } catch (PackageManager.NameNotFoundException e) {
-                    // assume no workarounds needed
-                    return true;
-                }
-                long minorVersion = 0;
-
-                // load minorID from reflection
-                try {
-                    Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
-                    Method getMinorID = javaRS.getDeclaredMethod("getMinorID");
-                    minorVersion = ((java.lang.Long)getMinorID.invoke(null)).longValue();
-                } catch (Exception e) {
-                    // minor version remains 0 on devices with no possible WARs
-                }
-
-                if (info.metaData != null) {
-                    // asynchronous teardown: minor version 1+
-                    if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
-                        if (minorVersion == 0) {
-                            sNative = 0;
-                        }
-                    }
-
-                    // blur issues on some drivers with 4.4
-                    if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
-                        if (android.os.Build.VERSION.SDK_INT <= 19) {
-                            //android.util.Log.e("rs", "war on");
-                            sNative = 0;
-                        }
-                    }
-                }
-                // end of workarounds
-            }
-        }
-
-        if (sNative == 1) {
-            // check against the blacklist
-            if (mBlackList.length() > 0) {
-                String deviceInfo = '(' +
-                                    android.os.Build.MANUFACTURER +
-                                    ':' +
-                                    android.os.Build.PRODUCT +
-                                    ':' +
-                                    android.os.Build.MODEL +
-                                    ')';
-                if (mBlackList.contains(deviceInfo)) {
-                    sNative = 0;
-                    return false;
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Name of the file that holds the object cache.
-     */
-    private static final String CACHE_PATH = "com.android.renderscript.cache";
-    static String mCachePath;
-
-     /**
-     * Sets the directory to use as a persistent storage for the
-     * renderscript object file cache.
-     *
-     * @hide
-     * @param cacheDir A directory the current process can write to
-     */
-    public static void setupDiskCache(File cacheDir) {
-        File f = new File(cacheDir, CACHE_PATH);
-        mCachePath = f.getAbsolutePath();
-        f.mkdirs();
-    }
-
-    /**
-     * ContextType specifies the specific type of context to be created.
-     *
-     */
-    public enum ContextType {
-        /**
-         * NORMAL context, this is the default and what shipping apps should
-         * use.
-         */
-        NORMAL (0),
-
-        /**
-         * DEBUG context, perform extra runtime checks to validate the
-         * kernels and APIs are being used as intended.  Get and SetElementAt
-         * will be bounds checked in this mode.
-         */
-        DEBUG (1),
-
-        /**
-         * PROFILE context, Intended to be used once the first time an
-         * application is run on a new device.  This mode allows the runtime to
-         * do additional testing and performance tuning.
-         */
-        PROFILE (2);
-
-        int mID;
-        ContextType(int id) {
-            mID = id;
-        }
-    }
-
-    ContextType mContextType;
-    // Methods below are wrapped to protect the non-threadsafe
-    // lockless fifo.
-
-    native long  rsnContextCreate(long dev, int ver, int sdkVer, int contextType, String nativeLibDir);
-    synchronized long nContextCreate(long dev, int ver, int sdkVer, int contextType, String nativeLibDir) {
-        return rsnContextCreate(dev, ver, sdkVer, contextType, nativeLibDir);
-    }
-    native void rsnContextDestroy(long con);
-    synchronized void nContextDestroy() {
-        validate();
-
-        // take teardown lock
-        // teardown lock can only be taken when no objects are being destroyed
-        ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
-        wlock.lock();
-
-        long curCon = mContext;
-        // context is considered dead as of this point
-        mContext = 0;
-
-        wlock.unlock();
-        rsnContextDestroy(curCon);
-    }
-    native void rsnContextSetPriority(long con, int p);
-    synchronized void nContextSetPriority(int p) {
-        validate();
-        rsnContextSetPriority(mContext, p);
-    }
-    native void rsnContextDump(long con, int bits);
-    synchronized void nContextDump(int bits) {
-        validate();
-        rsnContextDump(mContext, bits);
-    }
-    native void rsnContextFinish(long con);
-    synchronized void nContextFinish() {
-        validate();
-        rsnContextFinish(mContext);
-    }
-
-    native void rsnContextSendMessage(long con, int id, int[] data);
-    synchronized void nContextSendMessage(int id, int[] data) {
-        validate();
-        rsnContextSendMessage(mContext, id, data);
-    }
-
-    // nObjDestroy is explicitly _not_ synchronous to prevent crashes in finalizers
-    native void rsnObjDestroy(long con, long id);
-    void nObjDestroy(long id) {
-        // There is a race condition here.  The calling code may be run
-        // by the gc while teardown is occuring.  This protects againts
-        // deleting dead objects.
-        if (mContext != 0) {
-            rsnObjDestroy(mContext, id);
-        }
-    }
-
-    native long  rsnElementCreate(long con, long type, int kind, boolean norm, int vecSize);
-    synchronized long nElementCreate(long type, int kind, boolean norm, int vecSize) {
-        validate();
-        return rsnElementCreate(mContext, type, kind, norm, vecSize);
-    }
-    native long  rsnElementCreate2(long con, long[] elements, String[] names, int[] arraySizes);
-    synchronized long nElementCreate2(long[] elements, String[] names, int[] arraySizes) {
-        validate();
-        return rsnElementCreate2(mContext, elements, names, arraySizes);
-    }
-    native void rsnElementGetNativeData(long con, long id, int[] elementData);
-    synchronized void nElementGetNativeData(long id, int[] elementData) {
-        validate();
-        rsnElementGetNativeData(mContext, id, elementData);
-    }
-    native void rsnElementGetSubElements(long con, long id,
-                                         long[] IDs, String[] names, int[] arraySizes);
-    synchronized void nElementGetSubElements(long id, long[] IDs, String[] names, int[] arraySizes) {
-        validate();
-        rsnElementGetSubElements(mContext, id, IDs, names, arraySizes);
-    }
-
-    native long rsnTypeCreate(long con, long eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
-    synchronized long nTypeCreate(long eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
-        validate();
-        return rsnTypeCreate(mContext, eid, x, y, z, mips, faces, yuv);
-    }
-
-    native void rsnTypeGetNativeData(long con, long id, long[] typeData);
-    synchronized void nTypeGetNativeData(long id, long[] typeData) {
-        validate();
-        rsnTypeGetNativeData(mContext, id, typeData);
-    }
-
-    native long  rsnAllocationCreateTyped(long con, long type, int mip, int usage, long pointer);
-    synchronized long nAllocationCreateTyped(long type, int mip, int usage, long pointer) {
-        validate();
-        return rsnAllocationCreateTyped(mContext, type, mip, usage, pointer);
-    }
-    native long  rsnAllocationCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage);
-    synchronized long nAllocationCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
-        validate();
-        return rsnAllocationCreateFromBitmap(mContext, type, mip, bmp, usage);
-    }
-
-    native long  rsnAllocationCreateBitmapBackedAllocation(long con, long type, int mip, Bitmap bmp, int usage);
-    synchronized long nAllocationCreateBitmapBackedAllocation(long type, int mip, Bitmap bmp, int usage) {
-        validate();
-        return rsnAllocationCreateBitmapBackedAllocation(mContext, type, mip, bmp, usage);
-    }
-
-
-    native long  rsnAllocationCubeCreateFromBitmap(long con, long type, int mip, Bitmap bmp, int usage);
-    synchronized long nAllocationCubeCreateFromBitmap(long type, int mip, Bitmap bmp, int usage) {
-        validate();
-        return rsnAllocationCubeCreateFromBitmap(mContext, type, mip, bmp, usage);
-    }
-    native long  rsnAllocationCreateBitmapRef(long con, long type, Bitmap bmp);
-    synchronized long nAllocationCreateBitmapRef(long type, Bitmap bmp) {
-        validate();
-        return rsnAllocationCreateBitmapRef(mContext, type, bmp);
-    }
-    native long  rsnAllocationCreateFromAssetStream(long con, int mips, int assetStream, int usage);
-    synchronized long nAllocationCreateFromAssetStream(int mips, int assetStream, int usage) {
-        validate();
-        return rsnAllocationCreateFromAssetStream(mContext, mips, assetStream, usage);
-    }
-
-    native void  rsnAllocationCopyToBitmap(long con, long alloc, Bitmap bmp);
-    synchronized void nAllocationCopyToBitmap(long alloc, Bitmap bmp) {
-        validate();
-        rsnAllocationCopyToBitmap(mContext, alloc, bmp);
-    }
-
-
-    native void rsnAllocationSyncAll(long con, long alloc, int src);
-    synchronized void nAllocationSyncAll(long alloc, int src) {
-        validate();
-        rsnAllocationSyncAll(mContext, alloc, src);
-    }
-
-    native void rsnAllocationSetSurface(long con, long alloc, Surface sur);
-    synchronized void nAllocationSetSurface(long alloc, Surface sur) {
-        validate();
-        rsnAllocationSetSurface(mContext, alloc, sur);
-    }
-
-    native void rsnAllocationIoSend(long con, long alloc);
-    synchronized void nAllocationIoSend(long alloc) {
-        validate();
-        rsnAllocationIoSend(mContext, alloc);
-    }
-    native void rsnAllocationIoReceive(long con, long alloc);
-    synchronized void nAllocationIoReceive(long alloc) {
-        validate();
-        rsnAllocationIoReceive(mContext, alloc);
-    }
-    native ByteBuffer rsnAllocationGetByteBuffer(long con, long alloc, int xBytesSize, int dimY, int dimZ);
-    synchronized ByteBuffer nAllocationGetByteBuffer(long alloc, int xBytesSize, int dimY, int dimZ) {
-        validate();
-        return rsnAllocationGetByteBuffer(mContext, alloc, xBytesSize, dimY, dimZ);
-    }
-    native long rsnAllocationGetStride(long con, long alloc);
-    synchronized long nAllocationGetStride(long alloc) {
-        validate();
-        return rsnAllocationGetStride(mContext, alloc);
-    }
-
-    native void rsnAllocationGenerateMipmaps(long con, long alloc);
-    synchronized void nAllocationGenerateMipmaps(long alloc) {
-        validate();
-        rsnAllocationGenerateMipmaps(mContext, alloc);
-    }
-    native void  rsnAllocationCopyFromBitmap(long con, long alloc, Bitmap bmp);
-    synchronized void nAllocationCopyFromBitmap(long alloc, Bitmap bmp) {
-        validate();
-        rsnAllocationCopyFromBitmap(mContext, alloc, bmp);
-    }
-
-
-    native void rsnAllocationData1D(long con, long id, int off, int mip, int count, Object d, int sizeBytes, int dt,
-                                    int mSize, boolean usePadding);
-    synchronized void nAllocationData1D(long id, int off, int mip, int count, Object d, int sizeBytes, Element.DataType dt,
-                                        int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationData1D(mContext, id, off, mip, count, d, sizeBytes, dt.mID, mSize, usePadding);
-    }
-
-    native void rsnAllocationElementData1D(long con,long id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes);
-    synchronized void nAllocationElementData1D(long id, int xoff, int mip, int compIdx, byte[] d, int sizeBytes) {
-        validate();
-        rsnAllocationElementData1D(mContext, id, xoff, mip, compIdx, d, sizeBytes);
-    }
-    /*
-    native void rsnAllocationElementData(long con,long id, int xoff, int yoff, int zoff, int mip, int compIdx, byte[] d, int sizeBytes);
-    synchronized void nAllocationElementData(long id, int xoff, int yoff, int zoff, int mip, int compIdx, byte[] d, int sizeBytes) {
-        validate();
-        rsnAllocationElementData(mContext, id, xoff, yoff, zoff, mip, compIdx, d, sizeBytes);
-    }
-    */
-
-    native void rsnAllocationData2D(long con,
-                                    long dstAlloc, int dstXoff, int dstYoff,
-                                    int dstMip, int dstFace,
-                                    int width, int height,
-                                    long srcAlloc, int srcXoff, int srcYoff,
-                                    int srcMip, int srcFace);
-    synchronized void nAllocationData2D(long dstAlloc, int dstXoff, int dstYoff,
-                                        int dstMip, int dstFace,
-                                        int width, int height,
-                                        long srcAlloc, int srcXoff, int srcYoff,
-                                        int srcMip, int srcFace) {
-        validate();
-        rsnAllocationData2D(mContext,
-                            dstAlloc, dstXoff, dstYoff,
-                            dstMip, dstFace,
-                            width, height,
-                            srcAlloc, srcXoff, srcYoff,
-                            srcMip, srcFace);
-    }
-
-    native void rsnAllocationData2D(long con, long id, int xoff, int yoff, int mip, int face,
-                                    int w, int h, Object d, int sizeBytes, int dt,
-                                    int mSize, boolean usePadding);
-    synchronized void nAllocationData2D(long id, int xoff, int yoff, int mip, int face,
-                                        int w, int h, Object d, int sizeBytes, Element.DataType dt,
-                                        int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes, dt.mID, mSize, usePadding);
-    }
-
-    native void rsnAllocationData2D(long con, long id, int xoff, int yoff, int mip, int face, Bitmap b);
-    synchronized void nAllocationData2D(long id, int xoff, int yoff, int mip, int face, Bitmap b) {
-        validate();
-        rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, b);
-    }
-
-    native void rsnAllocationData3D(long con,
-                                    long dstAlloc, int dstXoff, int dstYoff, int dstZoff,
-                                    int dstMip,
-                                    int width, int height, int depth,
-                                    long srcAlloc, int srcXoff, int srcYoff, int srcZoff,
-                                    int srcMip);
-    synchronized void nAllocationData3D(long dstAlloc, int dstXoff, int dstYoff, int dstZoff,
-                                        int dstMip,
-                                        int width, int height, int depth,
-                                        long srcAlloc, int srcXoff, int srcYoff, int srcZoff,
-                                        int srcMip) {
-        validate();
-        rsnAllocationData3D(mContext,
-                            dstAlloc, dstXoff, dstYoff, dstZoff,
-                            dstMip, width, height, depth,
-                            srcAlloc, srcXoff, srcYoff, srcZoff, srcMip);
-    }
-
-
-    native void rsnAllocationData3D(long con, long id, int xoff, int yoff, int zoff, int mip,
-                                    int w, int h, int depth, Object d, int sizeBytes, int dt,
-                                    int mSize, boolean usePadding);
-    synchronized void nAllocationData3D(long id, int xoff, int yoff, int zoff, int mip,
-                                        int w, int h, int depth, Object d, int sizeBytes, Element.DataType dt,
-                                        int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes,
-                            dt.mID, mSize, usePadding);
-    }
-
-    native void rsnAllocationRead(long con, long id, Object d, int dt, int mSize, boolean usePadding);
-    synchronized void nAllocationRead(long id, Object d, Element.DataType dt, int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationRead(mContext, id, d, dt.mID, mSize, usePadding);
-    }
-
-    native void rsnAllocationRead1D(long con, long id, int off, int mip, int count, Object d,
-                                    int sizeBytes, int dt, int mSize, boolean usePadding);
-    synchronized void nAllocationRead1D(long id, int off, int mip, int count, Object d,
-                                        int sizeBytes, Element.DataType dt, int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationRead1D(mContext, id, off, mip, count, d, sizeBytes, dt.mID, mSize, usePadding);
-    }
-
-    /*
-    native void rsnAllocationElementRead(long con,long id, int xoff, int yoff, int zoff,
-                                         int mip, int compIdx, byte[] d, int sizeBytes);
-    synchronized void nAllocationElementRead(long id, int xoff, int yoff, int zoff,
-                                             int mip, int compIdx, byte[] d, int sizeBytes) {
-        validate();
-        rsnAllocationElementRead(mContext, id, xoff, yoff, zoff, mip, compIdx, d, sizeBytes);
-    }
-    */
-
-    native void rsnAllocationRead2D(long con, long id, int xoff, int yoff, int mip, int face,
-                                    int w, int h, Object d, int sizeBytes, int dt,
-                                    int mSize, boolean usePadding);
-    synchronized void nAllocationRead2D(long id, int xoff, int yoff, int mip, int face,
-                                        int w, int h, Object d, int sizeBytes, Element.DataType dt,
-                                        int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationRead2D(mContext, id, xoff, yoff, mip, face, w, h, d, sizeBytes, dt.mID, mSize, usePadding);
-    }
-
-    /*
-    native void rsnAllocationRead3D(long con, long id, int xoff, int yoff, int zoff, int mip,
-                                    int w, int h, int depth, Object d, int sizeBytes, int dt,
-                                    int mSize, boolean usePadding);
-    synchronized void nAllocationRead3D(long id, int xoff, int yoff, int zoff, int mip,
-                                        int w, int h, int depth, Object d, int sizeBytes, Element.DataType dt,
-                                        int mSize, boolean usePadding) {
-        validate();
-        rsnAllocationRead3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes, dt.mID, mSize, usePadding);
-    }
-    */
-
-    native long  rsnAllocationGetType(long con, long id);
-    synchronized long nAllocationGetType(long id) {
-        validate();
-        return rsnAllocationGetType(mContext, id);
-    }
-
-    native void rsnAllocationResize1D(long con, long id, int dimX);
-    synchronized void nAllocationResize1D(long id, int dimX) {
-        validate();
-        rsnAllocationResize1D(mContext, id, dimX);
-    }
-    native void rsnAllocationResize2D(long con, long id, int dimX, int dimY);
-    synchronized void nAllocationResize2D(long id, int dimX, int dimY) {
-        validate();
-        rsnAllocationResize2D(mContext, id, dimX, dimY);
-    }
-
-    native void rsnScriptBindAllocation(long con, long script, long alloc, int slot, boolean mUseInc);
-    synchronized void nScriptBindAllocation(long script, long alloc, int slot, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptBindAllocation(curCon, script, alloc, slot, mUseInc);
-    }
-    native void rsnScriptSetTimeZone(long con, long script, byte[] timeZone, boolean mUseInc);
-    synchronized void nScriptSetTimeZone(long script, byte[] timeZone, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetTimeZone(curCon, script, timeZone, mUseInc);
-    }
-    native void rsnScriptInvoke(long con, long id, int slot, boolean mUseInc);
-    synchronized void nScriptInvoke(long id, int slot, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptInvoke(curCon, id, slot, mUseInc);
-    }
-    native void rsnScriptForEach(long con, long incCon, long id, int slot, long ain, long aout, byte[] params, boolean mUseInc);
-    native void rsnScriptForEach(long con, long incCon, long id, int slot, long ain, long aout, boolean mUseInc);
-    native void rsnScriptForEachClipped(long con, long incCon, long id, int slot, long ain, long aout, byte[] params,
-                                        int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc);
-    native void rsnScriptForEachClipped(long con, long incCon, long id, int slot, long ain, long aout,
-                                        int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc);
-    synchronized void nScriptForEach(long id, int slot, long ain, long aout, byte[] params, boolean mUseInc) {
-        validate();
-        if (params == null) {
-            rsnScriptForEach(mContext, mIncCon, id, slot, ain, aout, mUseInc);
-        } else {
-            rsnScriptForEach(mContext, mIncCon, id, slot, ain, aout, params, mUseInc);
-        }
-    }
-
-    synchronized void nScriptForEachClipped(long id, int slot, long ain, long aout, byte[] params,
-                                            int xstart, int xend, int ystart, int yend, int zstart, int zend, boolean mUseInc) {
-        validate();
-        if (params == null) {
-            rsnScriptForEachClipped(mContext, mIncCon, id, slot, ain, aout, xstart, xend, ystart, yend, zstart, zend, mUseInc);
-        } else {
-            rsnScriptForEachClipped(mContext, mIncCon, id, slot, ain, aout, params, xstart, xend, ystart, yend, zstart, zend, mUseInc);
-        }
-    }
-
-    native void rsnScriptForEach(long con, long id, int slot, long[] ains,
-                                 long aout, byte[] params, int[] limits);
-
-    synchronized void nScriptForEach(long id, int slot, long[] ains, long aout,
-                                     byte[] params, int[] limits) {
-        if (!mEnableMultiInput) {
-            Log.e(LOG_TAG, "Multi-input kernels are not supported, please change targetSdkVersion to >= 23");
-            throw new RSRuntimeException("Multi-input kernels are not supported before API 23)");
-        }
-        validate();
-        rsnScriptForEach(mContext, id, slot, ains, aout, params, limits);
-    }
-
-    native void rsnScriptReduce(long con, long id, int slot, long[] ains,
-                                long aout, int[] limits);
-    synchronized void nScriptReduce(long id, int slot, long ains[], long aout,
-                                    int[] limits) {
-        validate();
-        rsnScriptReduce(mContext, id, slot, ains, aout, limits);
-    }
-
-    native void rsnScriptInvokeV(long con, long id, int slot, byte[] params, boolean mUseInc);
-    synchronized void nScriptInvokeV(long id, int slot, byte[] params, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptInvokeV(curCon, id, slot, params, mUseInc);
-    }
-    native void rsnScriptSetVarI(long con, long id, int slot, int val, boolean mUseInc);
-    synchronized void nScriptSetVarI(long id, int slot, int val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarI(curCon, id, slot, val, mUseInc);
-    }
-    native void rsnScriptSetVarJ(long con, long id, int slot, long val, boolean mUseInc);
-    synchronized void nScriptSetVarJ(long id, int slot, long val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarJ(curCon, id, slot, val, mUseInc);
-    }
-    native void rsnScriptSetVarF(long con, long id, int slot, float val, boolean mUseInc);
-    synchronized void nScriptSetVarF(long id, int slot, float val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarF(curCon, id, slot, val, mUseInc);
-    }
-    native void rsnScriptSetVarD(long con, long id, int slot, double val, boolean mUseInc);
-    synchronized void nScriptSetVarD(long id, int slot, double val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarD(curCon, id, slot, val, mUseInc);
-    }
-    native void rsnScriptSetVarV(long con, long id, int slot, byte[] val, boolean mUseInc);
-    synchronized void nScriptSetVarV(long id, int slot, byte[] val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarV(curCon, id, slot, val, mUseInc);
-    }
-    native void rsnScriptSetVarVE(long con, long id, int slot, byte[] val,
-                                  long e, int[] dims, boolean mUseInc);
-    synchronized void nScriptSetVarVE(long id, int slot, byte[] val,
-                                      long e, int[] dims, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarVE(curCon, id, slot, val, e, dims, mUseInc);
-    }
-    native void rsnScriptSetVarObj(long con, long id, int slot, long val, boolean mUseInc);
-    synchronized void nScriptSetVarObj(long id, int slot, long val, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        rsnScriptSetVarObj(curCon, id, slot, val, mUseInc);
-    }
-
-    native long  rsnScriptCCreate(long con, String resName, String cacheDir,
-                                 byte[] script, int length);
-    synchronized long nScriptCCreate(String resName, String cacheDir, byte[] script, int length) {
-        validate();
-        return rsnScriptCCreate(mContext, resName, cacheDir, script, length);
-    }
-
-    native long  rsnScriptIntrinsicCreate(long con, int id, long eid, boolean mUseInc);
-    synchronized long nScriptIntrinsicCreate(int id, long eid, boolean mUseInc) {
-        validate();
-        if (mUseInc) {
-            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
-                Log.e(LOG_TAG, "Incremental Intrinsics are not supported, please change targetSdkVersion to >= 21");
-                throw new RSRuntimeException("Incremental Intrinsics are not supported before Lollipop (API 21)");
-            }
-
-            if (!mIncLoaded) {
-                try {
-                    System.loadLibrary("RSSupport");
-                } catch (UnsatisfiedLinkError e) {
-                    Log.e(LOG_TAG, "Error loading RS Compat library for Incremental Intrinsic Support: " + e);
-                    throw new RSRuntimeException("Error loading RS Compat library for Incremental Intrinsic Support: " + e);
-                }
-                if (!nIncLoadSO(SUPPORT_LIB_API, mNativeLibDir + "/libRSSupport.so")) {
-                    throw new RSRuntimeException("Error loading libRSSupport library for Incremental Intrinsic Support");
-                }
-                mIncLoaded = true;
-            }
-            if (mIncCon == 0) {
-                //Create a dummy compat context (synchronous).
-                long device = nIncDeviceCreate();
-                mIncCon = nIncContextCreate(device, 0, 0, 0);
-            }
-            return rsnScriptIntrinsicCreate(mIncCon, id, eid, mUseInc);
-        } else {
-            return rsnScriptIntrinsicCreate(mContext, id, eid, mUseInc);
-        }
-    }
-
-    native long  rsnScriptKernelIDCreate(long con, long sid, int slot, int sig, boolean mUseInc);
-    synchronized long nScriptKernelIDCreate(long sid, int slot, int sig, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        return rsnScriptKernelIDCreate(curCon, sid, slot, sig, mUseInc);
-    }
-
-    native long  rsnScriptInvokeIDCreate(long con, long sid, int slot);
-    synchronized long nScriptInvokeIDCreate(long sid, int slot) {
-        validate();
-        return rsnScriptInvokeIDCreate(mContext, sid, slot);
-    }
-
-    native long  rsnScriptFieldIDCreate(long con, long sid, int slot, boolean mUseInc);
-    synchronized long nScriptFieldIDCreate(long sid, int slot, boolean mUseInc) {
-        validate();
-        long curCon = mContext;
-        if (mUseInc) {
-            curCon = mIncCon;
-        }
-        return rsnScriptFieldIDCreate(curCon, sid, slot, mUseInc);
-    }
-
-    native long  rsnScriptGroupCreate(long con, long[] kernels, long[] src, long[] dstk, long[] dstf, long[] types);
-    synchronized long nScriptGroupCreate(long[] kernels, long[] src, long[] dstk, long[] dstf, long[] types) {
-        validate();
-        return rsnScriptGroupCreate(mContext, kernels, src, dstk, dstf, types);
-    }
-
-    native void rsnScriptGroupSetInput(long con, long group, long kernel, long alloc);
-    synchronized void nScriptGroupSetInput(long group, long kernel, long alloc) {
-        validate();
-        rsnScriptGroupSetInput(mContext, group, kernel, alloc);
-    }
-
-    native void rsnScriptGroupSetOutput(long con, long group, long kernel, long alloc);
-    synchronized void nScriptGroupSetOutput(long group, long kernel, long alloc) {
-        validate();
-        rsnScriptGroupSetOutput(mContext, group, kernel, alloc);
-    }
-
-    native void rsnScriptGroupExecute(long con, long group);
-    synchronized void nScriptGroupExecute(long group) {
-        validate();
-        rsnScriptGroupExecute(mContext, group);
-    }
-
-    native long  rsnSamplerCreate(long con, int magFilter, int minFilter,
-                                 int wrapS, int wrapT, int wrapR, float aniso);
-    synchronized long nSamplerCreate(int magFilter, int minFilter,
-                                 int wrapS, int wrapT, int wrapR, float aniso) {
-        validate();
-        return rsnSamplerCreate(mContext, magFilter, minFilter, wrapS, wrapT, wrapR, aniso);
-    }
-
-// entry points for ScriptGroup2
-    native long rsnClosureCreate(long con, long kernelID, long returnValue,
-        long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
-        long[] depFieldIDs);
-    synchronized long nClosureCreate(long kernelID, long returnValue,
-        long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
-        long[] depFieldIDs) {
-      validate();
-      long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
-          sizes, depClosures, depFieldIDs);
-      if (c == 0) {
-          throw new RSRuntimeException("Failed creating closure.");
-      }
-      return c;
-    }
-
-    native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
-        long[] fieldIDs, long[] values, int[] sizes);
-    synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
-        long[] fieldIDs, long[] values, int[] sizes) {
-      validate();
-      long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
-          values, sizes);
-      if (c == 0) {
-          throw new RSRuntimeException("Failed creating closure.");
-      }
-      return c;
-    }
-
-    native void rsnClosureSetArg(long con, long closureID, int index,
-      long value, int size);
-    synchronized void nClosureSetArg(long closureID, int index, long value,
-        int size) {
-      validate();
-      rsnClosureSetArg(mContext, closureID, index, value, size);
-    }
-
-    native void rsnClosureSetGlobal(long con, long closureID, long fieldID,
-        long value, int size);
-    // Does this have to be synchronized?
-    synchronized void nClosureSetGlobal(long closureID, long fieldID,
-        long value, int size) {
-      validate(); // TODO: is this necessary?
-      rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
-    }
-
-    native long rsnScriptGroup2Create(long con, String name, String cachePath,
-                                      long[] closures);
-    synchronized long nScriptGroup2Create(String name, String cachePath,
-                                          long[] closures) {
-      validate();
-      return rsnScriptGroup2Create(mContext, name, cachePath, closures);
-    }
-
-    native void rsnScriptGroup2Execute(long con, long groupID);
-    synchronized void nScriptGroup2Execute(long groupID) {
-      validate();
-      rsnScriptGroup2Execute(mContext, groupID);
-    }
-
-    native void rsnScriptIntrinsicBLAS_Single(long con, long incCon, long id, int func, int TransA,
-                                              int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                              float alpha, long A, long B, float beta, long C, int incX, int incY,
-                                              int KL, int KU, boolean mUseInc);
-    synchronized void nScriptIntrinsicBLAS_Single(long id, int func, int TransA,
-                                                  int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                                  float alpha, long A, long B, float beta, long C, int incX, int incY,
-                                                  int KL, int KU, boolean mUseInc) {
-        validate();
-        rsnScriptIntrinsicBLAS_Single(mContext, mIncCon, id, func, TransA, TransB, Side, Uplo, Diag, M, N, K, alpha, A, B, beta, C, incX, incY, KL, KU, mUseInc);
-    }
-
-    native void rsnScriptIntrinsicBLAS_Double(long con, long incCon, long id, int func, int TransA,
-                                              int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                              double alpha, long A, long B, double beta, long C, int incX, int incY,
-                                              int KL, int KU, boolean mUseInc);
-    synchronized void nScriptIntrinsicBLAS_Double(long id, int func, int TransA,
-                                                  int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                                  double alpha, long A, long B, double beta, long C, int incX, int incY,
-                                                  int KL, int KU, boolean mUseInc) {
-        validate();
-        rsnScriptIntrinsicBLAS_Double(mContext, mIncCon, id, func, TransA, TransB, Side, Uplo, Diag, M, N, K, alpha, A, B, beta, C, incX, incY, KL, KU, mUseInc);
-    }
-
-    native void rsnScriptIntrinsicBLAS_Complex(long con, long incCon, long id, int func, int TransA,
-                                               int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                               float alphaX, float alphaY, long A, long B, float betaX, float betaY, long C, int incX, int incY,
-                                               int KL, int KU, boolean mUseInc);
-    synchronized void nScriptIntrinsicBLAS_Complex(long id, int func, int TransA,
-                                                   int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                                   float alphaX, float alphaY, long A, long B, float betaX, float betaY, long C, int incX, int incY,
-                                                   int KL, int KU, boolean mUseInc) {
-        validate();
-        rsnScriptIntrinsicBLAS_Complex(mContext, mIncCon, id, func, TransA, TransB, Side, Uplo, Diag, M, N, K, alphaX, alphaY, A, B, betaX, betaY, C, incX, incY, KL, KU, mUseInc);
-    }
-
-    native void rsnScriptIntrinsicBLAS_Z(long con, long incCon, long id, int func, int TransA,
-                                         int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                         double alphaX, double alphaY, long A, long B, double betaX, double betaY, long C, int incX, int incY,
-                                         int KL, int KU, boolean mUseInc);
-    synchronized void nScriptIntrinsicBLAS_Z(long id, int func, int TransA,
-                                             int TransB, int Side, int Uplo, int Diag, int M, int N, int K,
-                                             double alphaX, double alphaY, long A, long B, double betaX, double betaY, long C, int incX, int incY,
-                                             int KL, int KU, boolean mUseInc) {
-        validate();
-        rsnScriptIntrinsicBLAS_Z(mContext, mIncCon, id, func, TransA, TransB, Side, Uplo, Diag, M, N, K, alphaX, alphaY, A, B, betaX, betaY, C, incX, incY, KL, KU, mUseInc);
-    }
-
-    native void rsnScriptIntrinsicBLAS_BNNM(long con, long incCon, long id, int M, int N, int K,
-                                             long A, int a_offset, long B, int b_offset, long C, int c_offset,
-                                             int c_mult_int, boolean mUseInc);
-    synchronized void nScriptIntrinsicBLAS_BNNM(long id, int M, int N, int K,
-                                             long A, int a_offset, long B, int b_offset, long C, int c_offset,
-                                             int c_mult_int, boolean mUseInc) {
-        validate();
-        rsnScriptIntrinsicBLAS_BNNM(mContext, mIncCon, id, M, N, K, A, a_offset, B, b_offset, C, c_offset, c_mult_int, mUseInc);
-    }
-
-// Additional Entry points For inc libRSSupport
-
-    native boolean nIncLoadSO(int deviceApi, String libPath);
-    native long nIncDeviceCreate();
-    native void nIncDeviceDestroy(long dev);
-    // Methods below are wrapped to protect the non-threadsafe
-    // lockless fifo.
-    native long  rsnIncContextCreate(long dev, int ver, int sdkVer, int contextType);
-    synchronized long nIncContextCreate(long dev, int ver, int sdkVer, int contextType) {
-        return rsnIncContextCreate(dev, ver, sdkVer, contextType);
-    }
-    native void rsnIncContextDestroy(long con);
-    synchronized void nIncContextDestroy() {
-        validate();
-
-        // take teardown lock
-        // teardown lock can only be taken when no objects are being destroyed
-        ReentrantReadWriteLock.WriteLock wlock = mRWLock.writeLock();
-        wlock.lock();
-
-        long curCon = mIncCon;
-        // context is considered dead as of this point
-        mIncCon = 0;
-
-        wlock.unlock();
-        rsnIncContextDestroy(curCon);
-    }
-
-    native void rsnIncContextFinish(long con);
-    synchronized void nIncContextFinish() {
-        validate();
-        rsnIncContextFinish(mIncCon);
-    }
-
-    native void rsnIncObjDestroy(long con, long id);
-    void nIncObjDestroy(long id) {
-        // There is a race condition here.  The calling code may be run
-        // by the gc while teardown is occuring.  This protects againts
-        // deleting dead objects.
-        if (mIncCon != 0) {
-            rsnIncObjDestroy(mIncCon, id);
-        }
-    }
-    native long  rsnIncElementCreate(long con, long type, int kind, boolean norm, int vecSize);
-    synchronized long nIncElementCreate(long type, int kind, boolean norm, int vecSize) {
-        validate();
-        return rsnIncElementCreate(mIncCon, type, kind, norm, vecSize);
-    }
-    native long rsnIncTypeCreate(long con, long eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
-    synchronized long nIncTypeCreate(long eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
-        validate();
-        return rsnIncTypeCreate(mIncCon, eid, x, y, z, mips, faces, yuv);
-    }
-    native long  rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type, int xBytesSize);
-    synchronized long nIncAllocationCreateTyped(long alloc, long type, int xBytesSize) {
-        validate();
-        return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type, xBytesSize);
-    }
-
-    long     mContext;
-    private boolean mDestroyed = false;
-    //Dummy device & context for Inc Support Lib
-    long     mIncCon;
-    //indicator of whether inc support lib has been loaded or not.
-    boolean  mIncLoaded;
-    ReentrantReadWriteLock mRWLock;
-    @SuppressWarnings({"FieldCanBeLocal"})
-    MessageThread mMessageThread;
-
-    Element mElement_U8;
-    Element mElement_I8;
-    Element mElement_U16;
-    Element mElement_I16;
-    Element mElement_U32;
-    Element mElement_I32;
-    Element mElement_U64;
-    Element mElement_I64;
-    Element mElement_F32;
-    Element mElement_F64;
-    Element mElement_BOOLEAN;
-
-    Element mElement_ELEMENT;
-    Element mElement_TYPE;
-    Element mElement_ALLOCATION;
-    Element mElement_SAMPLER;
-    Element mElement_SCRIPT;
-
-    Element mElement_A_8;
-    Element mElement_RGB_565;
-    Element mElement_RGB_888;
-    Element mElement_RGBA_5551;
-    Element mElement_RGBA_4444;
-    Element mElement_RGBA_8888;
-
-    Element mElement_FLOAT_2;
-    Element mElement_FLOAT_3;
-    Element mElement_FLOAT_4;
-
-    Element mElement_DOUBLE_2;
-    Element mElement_DOUBLE_3;
-    Element mElement_DOUBLE_4;
-
-    Element mElement_UCHAR_2;
-    Element mElement_UCHAR_3;
-    Element mElement_UCHAR_4;
-
-    Element mElement_CHAR_2;
-    Element mElement_CHAR_3;
-    Element mElement_CHAR_4;
-
-    Element mElement_USHORT_2;
-    Element mElement_USHORT_3;
-    Element mElement_USHORT_4;
-
-    Element mElement_SHORT_2;
-    Element mElement_SHORT_3;
-    Element mElement_SHORT_4;
-
-    Element mElement_UINT_2;
-    Element mElement_UINT_3;
-    Element mElement_UINT_4;
-
-    Element mElement_INT_2;
-    Element mElement_INT_3;
-    Element mElement_INT_4;
-
-    Element mElement_ULONG_2;
-    Element mElement_ULONG_3;
-    Element mElement_ULONG_4;
-
-    Element mElement_LONG_2;
-    Element mElement_LONG_3;
-    Element mElement_LONG_4;
-
-    Element mElement_MATRIX_4X4;
-    Element mElement_MATRIX_3X3;
-    Element mElement_MATRIX_2X2;
-
-    Sampler mSampler_CLAMP_NEAREST;
-    Sampler mSampler_CLAMP_LINEAR;
-    Sampler mSampler_CLAMP_LINEAR_MIP_LINEAR;
-    Sampler mSampler_WRAP_NEAREST;
-    Sampler mSampler_WRAP_LINEAR;
-    Sampler mSampler_WRAP_LINEAR_MIP_LINEAR;
-    Sampler mSampler_MIRRORED_REPEAT_NEAREST;
-    Sampler mSampler_MIRRORED_REPEAT_LINEAR;
-    Sampler mSampler_MIRRORED_REPEAT_LINEAR_MIP_LINEAR;
-
-
-    ///////////////////////////////////////////////////////////////////////////////////
-    //
-
-    /**
-     * The base class from which an application should derive in order
-     * to receive RS messages from scripts. When a script calls {@code
-     * rsSendToClient}, the data fields will be filled, and the run
-     * method will be called on a separate thread.  This will occur
-     * some time after {@code rsSendToClient} completes in the script,
-     * as {@code rsSendToClient} is asynchronous. Message handlers are
-     * not guaranteed to have completed when {@link
-     * android.support.v8.renderscript.RenderScript#finish} returns.
-     *
-     */
-    public static class RSMessageHandler implements Runnable {
-        protected int[] mData;
-        protected int mID;
-        protected int mLength;
-        public void run() {
-        }
-    }
-    /**
-     * If an application is expecting messages, it should set this
-     * field to an instance of {@link RSMessageHandler}.  This
-     * instance will receive all the user messages sent from {@code
-     * sendToClient} by scripts from this context.
-     *
-     */
-    RSMessageHandler mMessageCallback = null;
-
-    public void setMessageHandler(RSMessageHandler msg) {
-        mMessageCallback = msg;
-    }
-    public RSMessageHandler getMessageHandler() {
-        return mMessageCallback;
-    }
-
-    /**
-     * Place a message into the message queue to be sent back to the message
-     * handler once all previous commands have been executed.
-     *
-     * @param id
-     * @param data
-     */
-    public void sendMessage(int id, int[] data) {
-        nContextSendMessage(id, data);
-    }
-
-    /**
-     * The runtime error handler base class.  An application should derive from this class
-     * if it wishes to install an error handler.  When errors occur at runtime,
-     * the fields in this class will be filled, and the run method will be called.
-     *
-     */
-    public static class RSErrorHandler implements Runnable {
-        protected String mErrorMessage;
-        protected int mErrorNum;
-        public void run() {
-        }
-    }
-
-    /**
-     * Application Error handler.  All runtime errors will be dispatched to the
-     * instance of RSAsyncError set here.  If this field is null a
-     * {@link RSRuntimeException} will instead be thrown with details about the error.
-     * This will cause program termaination.
-     *
-     */
-    RSErrorHandler mErrorCallback = null;
-
-    public void setErrorHandler(RSErrorHandler msg) {
-        mErrorCallback = msg;
-    }
-    public RSErrorHandler getErrorHandler() {
-        return mErrorCallback;
-    }
-
-    /**
-     * RenderScript worker thread priority enumeration.  The default value is
-     * NORMAL.  Applications wishing to do background processing should set
-     * their priority to LOW to avoid starving forground processes.
-     */
-    public enum Priority {
-        LOW (Process.THREAD_PRIORITY_BACKGROUND + (5 * Process.THREAD_PRIORITY_LESS_FAVORABLE)),
-        NORMAL (Process.THREAD_PRIORITY_DISPLAY);
-
-        int mID;
-        Priority(int id) {
-            mID = id;
-        }
-    }
-
-    void validateObject(BaseObj o) {
-        if (o != null) {
-            if (o.mRS != this) {
-                throw new RSIllegalArgumentException("Attempting to use an object across contexts.");
-            }
-        }
-    }
-
-    void validate() {
-        if (mContext == 0) {
-            throw new RSInvalidStateException("Calling RS with no Context active.");
-        }
-    }
-
-    /**
-     * check if IO support lib is available.
-     */
-    boolean usingIO() {
-        return useIOlib;
-    }
-    /**
-     * Change the priority of the worker threads for this context.
-     *
-     * @param p New priority to be set.
-     */
-    public void setPriority(Priority p) {
-        validate();
-        nContextSetPriority(p.mID);
-    }
-
-    static class MessageThread extends Thread {
-        RenderScript mRS;
-        boolean mRun = true;
-        int[] mAuxData = new int[2];
-
-        static final int RS_MESSAGE_TO_CLIENT_NONE = 0;
-        static final int RS_MESSAGE_TO_CLIENT_EXCEPTION = 1;
-        static final int RS_MESSAGE_TO_CLIENT_RESIZE = 2;
-        static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
-
-        static final int RS_MESSAGE_TO_CLIENT_USER = 4;
-        static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
-
-        MessageThread(RenderScript rs) {
-            super("RSMessageThread");
-            mRS = rs;
-
-        }
-
-        public void run() {
-            // This function is a temporary solution.  The final solution will
-            // used typed allocations where the message id is the type indicator.
-            int[] rbuf = new int[16];
-            mRS.nContextInitToClient(mRS.mContext);
-            while(mRun) {
-                rbuf[0] = 0;
-                int msg = mRS.nContextPeekMessage(mRS.mContext, mAuxData);
-                int size = mAuxData[1];
-                int subID = mAuxData[0];
-
-                if (msg == RS_MESSAGE_TO_CLIENT_USER) {
-                    if ((size>>2) >= rbuf.length) {
-                        rbuf = new int[(size + 3) >> 2];
-                    }
-                    if (mRS.nContextGetUserMessage(mRS.mContext, rbuf) !=
-                        RS_MESSAGE_TO_CLIENT_USER) {
-                        throw new RSDriverException("Error processing message from RenderScript.");
-                    }
-
-                    if(mRS.mMessageCallback != null) {
-                        mRS.mMessageCallback.mData = rbuf;
-                        mRS.mMessageCallback.mID = subID;
-                        mRS.mMessageCallback.mLength = size;
-                        mRS.mMessageCallback.run();
-                    } else {
-                        throw new RSInvalidStateException("Received a message from the script with no message handler installed.");
-                    }
-                    continue;
-                }
-
-                if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
-                    String e = mRS.nContextGetErrorMessage(mRS.mContext);
-
-                    if (subID >= RS_ERROR_FATAL_UNKNOWN) {
-                        throw new RSRuntimeException("Fatal error " + subID + ", details: " + e);
-                    }
-
-                    if(mRS.mErrorCallback != null) {
-                        mRS.mErrorCallback.mErrorMessage = e;
-                        mRS.mErrorCallback.mErrorNum = subID;
-                        mRS.mErrorCallback.run();
-                    } else {
-                        android.util.Log.e(LOG_TAG, "non fatal RS error, " + e);
-                        // Do not throw here. In these cases, we do not have
-                        // a fatal error.
-                    }
-                    continue;
-                }
-
-                // 2: teardown.
-                // But we want to avoid starving other threads during
-                // teardown by yielding until the next line in the destructor
-                // can execute to set mRun = false
-                try {
-                    sleep(1, 0);
-                } catch(InterruptedException e) {
-                }
-            }
-            //Log.d(LOG_TAG, "MessageThread exiting.");
-        }
-    }
-
-    RenderScript(Context ctx) {
-        mContextType = ContextType.NORMAL;
-        if (ctx != null) {
-            mApplicationContext = ctx.getApplicationContext();
-            // Only set mNativeLibDir for API 9+.
-            mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
-        }
-        mIncCon = 0;
-        mIncLoaded = false;
-        mRWLock = new ReentrantReadWriteLock();
-    }
-
-    /**
-     * Gets the application context associated with the RenderScript context.
-     *
-     * @return The application context.
-     */
-    public final Context getApplicationContext() {
-        return mApplicationContext;
-    }
-
-    /**
-     * Create a RenderScript context.
-     *
-     * @param ctx The context.
-     * @return RenderScript
-     */
-    private static RenderScript internalCreate(Context ctx, int sdkVersion, ContextType ct, int flags) {
-        RenderScript rs = new RenderScript(ctx);
-
-        if (sSdkVersion == -1) {
-            sSdkVersion = sdkVersion;
-        } else if (sSdkVersion != sdkVersion) {
-            throw new RSRuntimeException("Can't have two contexts with different SDK versions in support lib");
-        }
-        useNative = setupNative(sSdkVersion, ctx);
-        synchronized(lock) {
-            if (sInitialized == false) {
-                try {
-                    Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
-                    Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
-                    sRuntime = get_runtime.invoke(null);
-                    registerNativeAllocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
-                    registerNativeFree = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
-                    sUseGCHooks = true;
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "No GC methods");
-                    sUseGCHooks = false;
-                }
-                try {
-                    // For API 9 - 22, always use the absolute path of librsjni.so
-                    // http://b/25226912
-                    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
-                        rs.mNativeLibDir != null) {
-                        System.load(rs.mNativeLibDir + "/librsjni.so");
-                    } else {
-                        System.loadLibrary("rsjni");
-                    }
-                    sInitialized = true;
-                    sPointerSize = rsnSystemGetPointerSize();
-                } catch (UnsatisfiedLinkError e) {
-                    Log.e(LOG_TAG, "Error loading RS jni library: " + e);
-                    throw new RSRuntimeException("Error loading RS jni library: " + e + " Support lib API: " + SUPPORT_LIB_VERSION);
-                }
-            }
-        }
-
-        if (useNative) {
-            android.util.Log.v(LOG_TAG, "RS native mode");
-        } else {
-            android.util.Log.v(LOG_TAG, "RS compat mode");
-        }
-
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            useIOlib = true;
-        }
-
-        // The target API level used to init dispatchTable.
-        int dispatchAPI = sdkVersion;
-        if (sdkVersion < android.os.Build.VERSION.SDK_INT) {
-            // If the device API is higher than target API level, init dispatch table based on device API.
-            dispatchAPI = android.os.Build.VERSION.SDK_INT;
-        }
-
-        String rssupportPath = null;
-        // For API 9 - 22, always use the absolute path of libRSSupport.so
-        // http://b/25226912
-        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
-            rs.mNativeLibDir != null) {
-            rssupportPath = rs.mNativeLibDir + "/libRSSupport.so";
-        }
-        if (!rs.nLoadSO(useNative, dispatchAPI, rssupportPath)) {
-            if (useNative) {
-                android.util.Log.v(LOG_TAG, "Unable to load libRS.so, falling back to compat mode");
-                useNative = false;
-            }
-            try {
-                if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
-                    rs.mNativeLibDir != null) {
-                    System.load(rssupportPath);
-                } else {
-                    System.loadLibrary("RSSupport");
-                }
-            } catch (UnsatisfiedLinkError e) {
-                Log.e(LOG_TAG, "Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
-                throw new RSRuntimeException("Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
-            }
-            if (!rs.nLoadSO(false, dispatchAPI, rssupportPath)) {
-                Log.e(LOG_TAG, "Error loading RS Compat library: nLoadSO() failed; Support lib version: " + SUPPORT_LIB_VERSION);
-                throw new RSRuntimeException("Error loading libRSSupport library, Support lib version: " + SUPPORT_LIB_VERSION);
-            }
-        }
-
-        if (useIOlib) {
-            try {
-                System.loadLibrary("RSSupportIO");
-            } catch (UnsatisfiedLinkError e) {
-                useIOlib = false;
-            }
-            if (!useIOlib || !rs.nLoadIOSO()) {
-                android.util.Log.v(LOG_TAG, "Unable to load libRSSupportIO.so, USAGE_IO not supported");
-                useIOlib = false;
-            }
-        }
-
-        // For old APIs with dlopen bug, need to load blas lib in Java first.
-        // Only try load to blasV8 when the desired API level includes IntrinsicBLAS.
-        if (dispatchAPI >= 23) {
-            // Enable multi-input kernels only when diapatchAPI is M+.
-            rs.mEnableMultiInput = true;
-            try {
-                System.loadLibrary("blasV8");
-            } catch (UnsatisfiedLinkError e) {
-                Log.v(LOG_TAG, "Unable to load BLAS lib, ONLY BNNM will be supported: " + e);
-            }
-        }
-
-        long device = rs.nDeviceCreate();
-        rs.mContext = rs.nContextCreate(device, 0, sdkVersion, ct.mID, rs.mNativeLibDir);
-        rs.mContextType = ct;
-        rs.mContextFlags = flags;
-        rs.mContextSdkVersion = sdkVersion;
-        rs.mDispatchAPILevel = dispatchAPI;
-        if (rs.mContext == 0) {
-            throw new RSDriverException("Failed to create RS context.");
-        }
-        rs.mMessageThread = new MessageThread(rs);
-        rs.mMessageThread.start();
-        return rs;
-    }
-
-    /**
-     * Create a RenderScript context.
-     *
-     * See documentation for @create for details
-     *
-     * @param ctx The context.
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx) {
-        return create(ctx, ContextType.NORMAL);
-    }
-
-    /**
-     * calls create(ctx, ct, CREATE_FLAG_NONE)
-     *
-     * See documentation for @create for details
-     *
-     * @param ctx The context.
-     * @param ct The type of context to be created.
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx, ContextType ct) {
-        return create(ctx, ct, CREATE_FLAG_NONE);
-    }
-
-    /**
-     * Gets or creates a RenderScript context of the specified type.
-     *
-     * The returned context will be cached for future reuse within
-     * the process. When an application is finished using
-     * RenderScript it should call releaseAllContexts()
-     *
-     * A process context is a context designed for easy creation and
-     * lifecycle management.  Multiple calls to this function will
-     * return the same object provided they are called with the same
-     * options.  This allows it to be used any time a RenderScript
-     * context is needed.
-     *
-     *
-     * @param ctx The context.
-     * @param ct The type of context to be created.
-     * @param flags The OR of the CREATE_FLAG_* options desired
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx, ContextType ct, int flags) {
-        int v = ctx.getApplicationInfo().targetSdkVersion;
-        return create(ctx, v, ct, flags);
-    }
-
-    /**
-     * calls create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE)
-     *
-     * Used by the RenderScriptThunker to maintain backward compatibility.
-     *
-     * @hide
-     * @param ctx The context.
-     * @param sdkVersion The target SDK Version.
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx, int sdkVersion) {
-        return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
-    }
-
-
-    /**
-     * calls create(ctx, sdkVersion, ct, CREATE_FLAG_NONE)
-     * Create a RenderScript context.
-     *
-     * @hide
-     * @param ctx The context.
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) {
-        return create(ctx, sdkVersion, ct, CREATE_FLAG_NONE);
-    }
-
-     /**
-     * Gets or creates a RenderScript context of the specified type.
-     *
-     * @param ctx The context.
-     * @param ct The type of context to be created.
-     * @param sdkVersion The target SDK Version.
-     * @param flags The OR of the CREATE_FLAG_* options desired
-     * @return RenderScript
-     */
-    public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
-        synchronized (mProcessContextList) {
-            for (RenderScript prs : mProcessContextList) {
-                if ((prs.mContextType == ct) &&
-                    (prs.mContextFlags == flags) &&
-                    (prs.mContextSdkVersion == sdkVersion)) {
-
-                    return prs;
-                }
-            }
-
-            RenderScript prs = internalCreate(ctx, sdkVersion, ct, flags);
-            prs.mIsProcessContext = true;
-            mProcessContextList.add(prs);
-            return prs;
-        }
-    }
-
-    /**
-     *
-     * Releases all the process contexts.  This is the same as
-     * calling .destroy() on each unique context retreived with
-     * create(...). If no contexts have been created this
-     * function does nothing.
-     *
-     * Typically you call this when your application is losing focus
-     * and will not be using a context for some time.
-     *
-     * This has no effect on a context created with
-     * createMultiContext()
-     */
-    public static void releaseAllContexts() {
-        ArrayList<RenderScript> oldList;
-        synchronized (mProcessContextList) {
-            oldList = mProcessContextList;
-            mProcessContextList = new ArrayList<RenderScript>();
-        }
-
-        for (RenderScript prs : oldList) {
-            prs.mIsProcessContext = false;
-            prs.destroy();
-        }
-        oldList.clear();
-    }
-
-
-
-    /**
-     * Create a RenderScript context.
-     *
-     * This is an advanced function intended for applications which
-     * need to create more than one RenderScript context to be used
-     * at the same time.
-     *
-     * If you need a single context please use create()
-     *
-     * @param ctx The context.
-     * @return RenderScript
-     */
-    public static RenderScript createMultiContext(Context ctx, ContextType ct, int flags, int API_number) {
-        return internalCreate(ctx, API_number, ct, flags);
-    }
-
-    /**
-     * Print the currently available debugging information about the state of
-     * the RS context to the log.
-     *
-     */
-    public void contextDump() {
-        validate();
-        nContextDump(0);
-    }
-
-    /**
-     * Wait for any pending asynchronous opeations (such as copies to a RS
-     * allocation or RS script executions) to complete.
-     *
-     */
-    public void finish() {
-        nContextFinish();
-    }
-
-    private void helpDestroy() {
-        boolean shouldDestroy = false;
-        synchronized(this) {
-            if (!mDestroyed) {
-                shouldDestroy = true;
-                mDestroyed = true;
-            }
-        }
-
-        if (shouldDestroy) {
-            nContextFinish();
-            if (mIncCon != 0) {
-                nIncContextFinish();
-                nIncContextDestroy();
-                mIncCon = 0;
-            }
-            nContextDeinitToClient(mContext);
-            mMessageThread.mRun = false;
-            // Interrupt mMessageThread so it gets to see immediately that mRun is false
-            // and exit rightaway.
-            mMessageThread.interrupt();
-
-            // Wait for mMessageThread to join.  Try in a loop, in case this thread gets interrupted
-            // during the wait.  If interrupted, set the "interrupted" status of the current thread.
-            boolean hasJoined = false, interrupted = false;
-            while (!hasJoined) {
-                try {
-                    mMessageThread.join();
-                    hasJoined = true;
-                } catch (InterruptedException e) {
-                    interrupted = true;
-                }
-            }
-            if (interrupted) {
-                Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join");
-                Thread.currentThread().interrupt();
-            }
-
-            nContextDestroy();
-        }
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        helpDestroy();
-        super.finalize();
-    }
-
-    /**
-     * Destroys this RenderScript context.  Once this function is called,
-     * using this context or any objects belonging to this context is
-     * illegal.
-     *
-     * This function is a NOP if the context was created
-     * with create().  Please use releaseAllContexts() to clean up
-     * contexts created with the create function.
-     */
-    public void destroy() {
-        if (mIsProcessContext) {
-            // users cannot destroy a process context
-            return;
-        }
-        validate();
-        helpDestroy();
-    }
-
-    boolean isAlive() {
-        return mContext != 0;
-    }
-
-    long safeID(BaseObj o) {
-        if(o != null) {
-            return o.getID(this);
-        }
-        return 0;
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java b/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java
deleted file mode 100644
index 7119e8c..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Sampler.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.util.Log;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-/**
- * Sampler object that defines how Allocations can be read as textures within a
- * kernel. Samplers are used in conjunction with the {@code rsSample} runtime
- * function to return values from normalized coordinates.
- *
- * Any Allocation used with a Sampler must have been created with {@link
- * android.support.v8.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE}; using a
- * Sampler on an {@link android.support.v8.renderscript.Allocation} that was not
- * created with
- * {@link android.support.v8.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE} is
- * undefined.
- **/
-public class Sampler extends BaseObj {
-    public enum Value {
-        NEAREST (0),
-        LINEAR (1),
-        LINEAR_MIP_LINEAR (2),
-        LINEAR_MIP_NEAREST (5),
-        WRAP (3),
-        CLAMP (4),
-        MIRRORED_REPEAT (6);
-
-        int mID;
-        Value(int id) {
-            mID = id;
-        }
-    }
-
-    Value mMin;
-    Value mMag;
-    Value mWrapS;
-    Value mWrapT;
-    Value mWrapR;
-    float mAniso;
-
-    Sampler(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * @return minification setting for the sampler
-     */
-    public Value getMinification() {
-        return mMin;
-    }
-
-    /**
-     * @return magnification setting for the sampler
-     */
-    public Value getMagnification() {
-        return mMag;
-    }
-
-    /**
-     * @return S wrapping mode for the sampler
-     */
-    public Value getWrapS() {
-        return mWrapS;
-    }
-
-    /**
-     * @return T wrapping mode for the sampler
-     */
-    public Value getWrapT() {
-        return mWrapT;
-    }
-
-    /**
-     * @return anisotropy setting for the sampler
-     */
-    public float getAnisotropy() {
-        return mAniso;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to nearest and wrap modes set to
-     * clamp.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler CLAMP_NEAREST(RenderScript rs) {
-        if(rs.mSampler_CLAMP_NEAREST == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.NEAREST);
-            b.setMagnification(Value.NEAREST);
-            b.setWrapS(Value.CLAMP);
-            b.setWrapT(Value.CLAMP);
-            rs.mSampler_CLAMP_NEAREST = b.create();
-        }
-        return rs.mSampler_CLAMP_NEAREST;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to linear and wrap modes set to
-     * clamp.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler CLAMP_LINEAR(RenderScript rs) {
-        if(rs.mSampler_CLAMP_LINEAR == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.LINEAR);
-            b.setMagnification(Value.LINEAR);
-            b.setWrapS(Value.CLAMP);
-            b.setWrapT(Value.CLAMP);
-            rs.mSampler_CLAMP_LINEAR = b.create();
-        }
-        return rs.mSampler_CLAMP_LINEAR;
-    }
-
-    /**
-     * Retrieve a sampler with mag set to linear, min linear mipmap linear, and
-     * wrap modes set to clamp.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler CLAMP_LINEAR_MIP_LINEAR(RenderScript rs) {
-        if(rs.mSampler_CLAMP_LINEAR_MIP_LINEAR == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.LINEAR_MIP_LINEAR);
-            b.setMagnification(Value.LINEAR);
-            b.setWrapS(Value.CLAMP);
-            b.setWrapT(Value.CLAMP);
-            rs.mSampler_CLAMP_LINEAR_MIP_LINEAR = b.create();
-        }
-        return rs.mSampler_CLAMP_LINEAR_MIP_LINEAR;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to nearest and wrap modes set to
-     * wrap.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler WRAP_NEAREST(RenderScript rs) {
-        if(rs.mSampler_WRAP_NEAREST == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.NEAREST);
-            b.setMagnification(Value.NEAREST);
-            b.setWrapS(Value.WRAP);
-            b.setWrapT(Value.WRAP);
-            rs.mSampler_WRAP_NEAREST = b.create();
-        }
-        return rs.mSampler_WRAP_NEAREST;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to linear and wrap modes set to
-     * wrap.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler WRAP_LINEAR(RenderScript rs) {
-        if(rs.mSampler_WRAP_LINEAR == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.LINEAR);
-            b.setMagnification(Value.LINEAR);
-            b.setWrapS(Value.WRAP);
-            b.setWrapT(Value.WRAP);
-            rs.mSampler_WRAP_LINEAR = b.create();
-        }
-        return rs.mSampler_WRAP_LINEAR;
-    }
-
-    /**
-     * Retrieve a sampler with mag set to linear, min linear mipmap linear, and
-     * wrap modes set to wrap.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler WRAP_LINEAR_MIP_LINEAR(RenderScript rs) {
-        if(rs.mSampler_WRAP_LINEAR_MIP_LINEAR == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.LINEAR_MIP_LINEAR);
-            b.setMagnification(Value.LINEAR);
-            b.setWrapS(Value.WRAP);
-            b.setWrapT(Value.WRAP);
-            rs.mSampler_WRAP_LINEAR_MIP_LINEAR = b.create();
-        }
-        return rs.mSampler_WRAP_LINEAR_MIP_LINEAR;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to nearest and wrap modes set to
-     * mirrored repeat.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler MIRRORED_REPEAT_NEAREST(RenderScript rs) {
-        if(rs.mSampler_MIRRORED_REPEAT_NEAREST == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.NEAREST);
-            b.setMagnification(Value.NEAREST);
-            b.setWrapS(Value.MIRRORED_REPEAT);
-            b.setWrapT(Value.MIRRORED_REPEAT);
-            rs.mSampler_MIRRORED_REPEAT_NEAREST = b.create();
-        }
-        return rs.mSampler_MIRRORED_REPEAT_NEAREST;
-    }
-
-    /**
-     * Retrieve a sampler with min and mag set to linear and wrap modes set to
-     * mirrored repeat.
-     *
-     * @param rs Context to which the sampler will belong.
-     *
-     * @return Sampler
-     */
-    public static Sampler MIRRORED_REPEAT_LINEAR(RenderScript rs) {
-        if(rs.mSampler_MIRRORED_REPEAT_LINEAR == null) {
-            Builder b = new Builder(rs);
-            b.setMinification(Value.LINEAR);
-            b.setMagnification(Value.LINEAR);
-            b.setWrapS(Value.MIRRORED_REPEAT);
-            b.setWrapT(Value.MIRRORED_REPEAT);
-            rs.mSampler_MIRRORED_REPEAT_LINEAR = b.create();
-        }
-        return rs.mSampler_MIRRORED_REPEAT_LINEAR;
-    }
-
-    /**
-     * Builder for creating non-standard samplers.  This is only necessary if
-     * a Sampler with different min and mag modes is desired.
-     */
-    public static class Builder {
-        RenderScript mRS;
-        Value mMin;
-        Value mMag;
-        Value mWrapS;
-        Value mWrapT;
-        Value mWrapR;
-        float mAniso;
-
-        public Builder(RenderScript rs) {
-            mRS = rs;
-            mMin = Value.NEAREST;
-            mMag = Value.NEAREST;
-            mWrapS = Value.WRAP;
-            mWrapT = Value.WRAP;
-            mWrapR = Value.WRAP;
-            mAniso = 1.0f;
-        }
-
-        public void setMinification(Value v) {
-            if (v == Value.NEAREST ||
-                v == Value.LINEAR ||
-                v == Value.LINEAR_MIP_LINEAR ||
-                v == Value.LINEAR_MIP_NEAREST) {
-                mMin = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setMagnification(Value v) {
-            if (v == Value.NEAREST || v == Value.LINEAR) {
-                mMag = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setWrapS(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
-                mWrapS = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setWrapT(Value v) {
-            if (v == Value.WRAP || v == Value.CLAMP || v == Value.MIRRORED_REPEAT) {
-                mWrapT = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public void setAnisotropy(float v) {
-            if(v >= 0.0f) {
-                mAniso = v;
-            } else {
-                throw new IllegalArgumentException("Invalid value");
-            }
-        }
-
-        public Sampler create() {
-            mRS.validate();
-            long id = mRS.nSamplerCreate(mMag.mID, mMin.mID,
-                                        mWrapS.mID, mWrapT.mID, mWrapR.mID, mAniso);
-            Sampler sampler = new Sampler(id, mRS);
-            sampler.mMin = mMin;
-            sampler.mMag = mMag;
-            sampler.mWrapS = mWrapS;
-            sampler.mWrapT = mWrapT;
-            sampler.mWrapR = mWrapR;
-            sampler.mAniso = mAniso;
-            return sampler;
-        }
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
deleted file mode 100644
index ec3a7a9..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.SparseArray;
-
-/**
- * The parent class for all executable scripts. This should not be used by
- * applications.
- **/
-public class Script extends BaseObj {
-    /**
-     * Determine if Incremental Intrinsic Support is needed
-     *
-     */
-    private boolean mUseIncSupp;
-    protected void setIncSupp(boolean useInc) {
-        mUseIncSupp = useInc;
-    }
-    protected boolean isIncSupp() {
-        return mUseIncSupp;
-    }
-    /**
-     * An allocation for the compat context will be created when needed
-     * e.g. foreach(ain, aout), setVar(ain);
-     *
-     */
-    long getDummyAlloc(Allocation ain) {
-        long dInElement = 0;
-        long dInType = 0;
-        long dummyAlloc = 0;
-        if (ain != null) {
-            Type inType = ain.getType();
-            dInElement = inType.getElement().getDummyElement(mRS);
-            dInType = inType.getDummyType(mRS, dInElement);
-            int xBytesSize = inType.getX() * inType.getElement().getBytesSize();
-            dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType, xBytesSize);
-            ain.setIncAllocID(dummyAlloc);
-        }
-
-        return dummyAlloc;
-    }
-    /**
-     * KernelID is an identifier for a Script + root function pair. It is used
-     * as an identifier for ScriptGroup creation.
-     *
-     * This class should not be directly created. Instead use the method in the
-     * reflected or intrinsic code "getKernelID_funcname()".
-     *
-     */
-    public static final class KernelID extends BaseObj {
-        android.renderscript.Script.KernelID mN;
-        Script mScript;
-        int mSlot;
-        int mSig;
-        KernelID(long id, RenderScript rs, Script s, int slot, int sig) {
-            super(id, rs);
-            mScript = s;
-            mSlot = slot;
-            mSig = sig;
-        }
-    }
-
-    private final SparseArray<KernelID> mKIDs = new SparseArray<KernelID>();
-    /**
-     * Only to be used by generated reflected classes.
-     *
-     *
-     * @param slot
-     * @param sig
-     * @param ein
-     * @param eout
-     *
-     * @return KernelID
-     */
-    protected KernelID createKernelID(int slot, int sig, Element ein, Element eout) {
-        KernelID k = mKIDs.get(slot);
-        if (k != null) {
-            return k;
-        }
-
-        long id = mRS.nScriptKernelIDCreate(getID(mRS), slot, sig, mUseIncSupp);
-        if (id == 0) {
-            throw new RSDriverException("Failed to create KernelID");
-        }
-
-        k = new KernelID(id, mRS, this, slot, sig);
-
-        mKIDs.put(slot, k);
-        return k;
-    }
-
-    /**
-     * InvokeID is an identifier for a invoke function. It is used
-     * as an identifier for ScriptGroup creation.
-     *
-     * This class should not be directly created. Instead use the method in the
-     * reflected or intrinsic code "getInvokeID_funcname()".
-     *
-     */
-    public static final class InvokeID extends BaseObj {
-        Script mScript;
-        int mSlot;
-        InvokeID(long id, RenderScript rs, Script s, int slot) {
-            super(id, rs);
-            mScript = s;
-            mSlot = slot;
-        }
-    }
-
-    private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
-    /**
-     * Only to be used by generated reflected classes.
-     */
-    protected InvokeID createInvokeID(int slot) {
-        InvokeID i = mIIDs.get(slot);
-        if (i != null) {
-            return i;
-        }
-
-        long id = mRS.nScriptInvokeIDCreate(getID(mRS), slot);
-        if (id == 0) {
-            throw new RSDriverException("Failed to create KernelID");
-        }
-
-        i = new InvokeID(id, mRS, this, slot);
-        mIIDs.put(slot, i);
-        return i;
-    }
-
-    /**
-     * FieldID is an identifier for a Script + exported field pair. It is used
-     * as an identifier for ScriptGroup creation.
-     *
-     * This class should not be directly created. Instead use the method in the
-     * reflected or intrinsic code "getFieldID_funcname()".
-     *
-     */
-    public static final class FieldID extends BaseObj {
-        android.renderscript.Script.FieldID mN;
-        Script mScript;
-        int mSlot;
-        FieldID(long id, RenderScript rs, Script s, int slot) {
-            super(id, rs);
-            mScript = s;
-            mSlot = slot;
-        }
-    }
-
-    private final SparseArray<FieldID> mFIDs = new SparseArray();
-    /**
-     * Only to be used by generated reflected classes.
-     *
-     * @param slot
-     * @param e
-     *
-     * @return FieldID
-     */
-    protected FieldID createFieldID(int slot, Element e) {
-        FieldID f = mFIDs.get(slot);
-        if (f != null) {
-            return f;
-        }
-
-        long id = mRS.nScriptFieldIDCreate(getID(mRS), slot, mUseIncSupp);
-        if (id == 0) {
-            throw new RSDriverException("Failed to create FieldID");
-        }
-
-        f = new FieldID(id, mRS, this, slot);
-        mFIDs.put(slot, f);
-        return f;
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param slot
-     */
-    protected void invoke(int slot) {
-        mRS.nScriptInvoke(getID(mRS), slot, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param slot
-     * @param v
-     */
-    protected void invoke(int slot, FieldPacker v) {
-        if (v != null) {
-            mRS.nScriptInvokeV(getID(mRS), slot, v.getData(), mUseIncSupp);
-        } else {
-            mRS.nScriptInvoke(getID(mRS), slot, mUseIncSupp);
-        }
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param va
-     * @param slot
-     */
-    public void bindAllocation(Allocation va, int slot) {
-        mRS.validate();
-        if (va != null) {
-            mRS.nScriptBindAllocation(getID(mRS), va.getID(mRS), slot, mUseIncSupp);
-        } else {
-            mRS.nScriptBindAllocation(getID(mRS), 0, slot, mUseIncSupp);
-        }
-    }
-
-    public void setTimeZone(String timeZone) {
-        mRS.validate();
-        try {
-            mRS.nScriptSetTimeZone(getID(mRS), timeZone.getBytes("UTF-8"), mUseIncSupp);
-        } catch (java.io.UnsupportedEncodingException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param slot
-     * @param ain
-     * @param aout
-     * @param v
-     */
-    protected void forEach(int slot, Allocation ain, Allocation aout, FieldPacker v) {
-        if (ain == null && aout == null) {
-            throw new RSIllegalArgumentException(
-                "At least one of ain or aout is required to be non-null.");
-        }
-        long in_id = 0;
-        long out_id = 0;
-        if (ain != null) {
-            in_id = ain.getID(mRS);
-        }
-        if (aout != null) {
-            out_id = aout.getID(mRS);
-        }
-
-        byte[] params = null;
-        if (v != null) {
-            params = v.getData();
-        }
-
-        if (mUseIncSupp) {
-            long ainInc = getDummyAlloc(ain);
-            long aoutInc = getDummyAlloc(aout);
-            mRS.nScriptForEach(getID(mRS), slot, ainInc, aoutInc, params, mUseIncSupp);
-        } else {
-            mRS.nScriptForEach(getID(mRS), slot, in_id, out_id, params, mUseIncSupp);
-        }
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param slot
-     * @param ain
-     * @param aout
-     * @param v
-     * @param sc
-     */
-    protected void forEach(int slot, Allocation ain, Allocation aout, FieldPacker v, LaunchOptions sc) {
-        if (ain == null && aout == null) {
-            throw new RSIllegalArgumentException(
-                "At least one of ain or aout is required to be non-null.");
-        }
-
-        if (sc == null) {
-            forEach(slot, ain, aout, v);
-            return;
-        }
-        long in_id = 0;
-        long out_id = 0;
-        if (ain != null) {
-            in_id = ain.getID(mRS);
-        }
-        if (aout != null) {
-            out_id = aout.getID(mRS);
-        }
-
-        byte[] params = null;
-        if (v != null) {
-            params = v.getData();
-        }
-        if (mUseIncSupp) {
-            long ainInc = getDummyAlloc(ain);
-            long aoutInc = getDummyAlloc(aout);
-            mRS.nScriptForEachClipped(getID(mRS), slot, ainInc, aoutInc, params, sc.xstart, sc.xend, sc.ystart, sc.yend, sc.zstart, sc.zend, mUseIncSupp);        
-        } else {
-            mRS.nScriptForEachClipped(getID(mRS), slot, in_id, out_id, params, sc.xstart, sc.xend, sc.ystart, sc.yend, sc.zstart, sc.zend, mUseIncSupp);
-        }
-    }
-
-    Script(long id, RenderScript rs) {
-        super(id, rs);
-        mUseIncSupp = false;
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @hide
-     */
-    protected void forEach(int slot, Allocation[] ains, Allocation aout,
-                           FieldPacker v) {
-        forEach(slot, ains, aout, v, null);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @hide
-     */
-    protected void forEach(int slot, Allocation[] ains, Allocation aout,
-                           FieldPacker v, LaunchOptions sc) {
-        // TODO: Is this necessary if nScriptForEach calls validate as well?
-        mRS.validate();
-        if (ains != null) {
-            for (Allocation ain : ains) {
-                mRS.validateObject(ain);
-            }
-        }
-        mRS.validateObject(aout);
-
-        if (ains == null && aout == null) {
-            throw new RSIllegalArgumentException(
-                "At least one of ain or aout is required to be non-null.");
-        }
-
-        long[] in_ids;
-        if (ains != null) {
-            in_ids = new long[ains.length];
-            for (int index = 0; index < ains.length; ++index) {
-                in_ids[index] = ains[index].getID(mRS);
-            }
-        } else {
-            in_ids = null;
-        }
-
-        long out_id = 0;
-        if (aout != null) {
-            out_id = aout.getID(mRS);
-        }
-
-        byte[] params = null;
-        if (v != null) {
-            params = v.getData();
-        }
-
-        int[] limits = null;
-        if (sc != null) {
-            limits = new int[6];
-
-            limits[0] = sc.xstart;
-            limits[1] = sc.xend;
-            limits[2] = sc.ystart;
-            limits[3] = sc.yend;
-            limits[4] = sc.zstart;
-            limits[5] = sc.zend;
-        }
-
-        mRS.nScriptForEach(getID(mRS), slot, in_ids, out_id, params, limits);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.  (General reduction)
-     *
-     * @hide
-     */
-    protected void reduce(int slot, Allocation[] ains, Allocation aout, LaunchOptions sc) {
-        mRS.validate();
-        if (ains == null || ains.length < 1) {
-            throw new RSIllegalArgumentException(
-                "At least one input is required.");
-        }
-        if (aout == null) {
-            throw new RSIllegalArgumentException(
-                "aout is required to be non-null.");
-        }
-        for (Allocation ain : ains) {
-            mRS.validateObject(ain);
-        }
-
-        long[] in_ids = new long[ains.length];
-        for (int index = 0; index < ains.length; ++index) {
-            in_ids[index] = ains[index].getID(mRS);
-        }
-        long out_id = aout.getID(mRS);
-
-        int[] limits = null;
-        if (sc != null) {
-            limits = new int[6];
-
-            limits[0] = sc.xstart;
-            limits[1] = sc.xend;
-            limits[2] = sc.ystart;
-            limits[3] = sc.yend;
-            limits[4] = sc.zstart;
-            limits[5] = sc.zend;
-        }
-
-        mRS.nScriptReduce(getID(mRS), slot, in_ids, out_id, limits);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, float v) {
-        mRS.nScriptSetVarF(getID(mRS), index, v, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, double v) {
-        mRS.nScriptSetVarD(getID(mRS), index, v, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, int v) {
-        mRS.nScriptSetVarI(getID(mRS), index, v, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, long v) {
-        mRS.nScriptSetVarJ(getID(mRS), index, v, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, boolean v) {
-        mRS.nScriptSetVarI(getID(mRS), index, v ? 1 : 0, mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param o
-     */
-    public void setVar(int index, BaseObj o) {
-        if (mUseIncSupp) {
-            long oInc = getDummyAlloc((Allocation)o);
-            mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : oInc, mUseIncSupp);            
-        } else {
-            mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : o.getID(mRS), mUseIncSupp);
-        }
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     */
-    public void setVar(int index, FieldPacker v) {
-        mRS.nScriptSetVarV(getID(mRS), index, v.getData(), mUseIncSupp);
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     * @param index
-     * @param v
-     * @param e
-     * @param dims
-     */
-    public void setVar(int index, FieldPacker v, Element e, int[] dims) {
-        if (mUseIncSupp) {
-            long dElement = e.getDummyElement(mRS);
-            mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), dElement, dims, mUseIncSupp);
-        } else {
-            mRS.nScriptSetVarVE(getID(mRS), index, v.getData(), e.getID(mRS), dims, mUseIncSupp);
-        }
-    }
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     */
-    public static class Builder {
-        RenderScript mRS;
-
-        Builder(RenderScript rs) {
-            mRS = rs;
-        }
-    }
-
-
-    /**
-     * Only intended for use by generated reflected code.
-     *
-     */
-    public static class FieldBase {
-        protected Element mElement;
-        protected Allocation mAllocation;
-
-        protected void init(RenderScript rs, int dimx) {
-            mAllocation = Allocation.createSized(rs, mElement, dimx, Allocation.USAGE_SCRIPT);
-        }
-
-        protected void init(RenderScript rs, int dimx, int usages) {
-            mAllocation = Allocation.createSized(rs, mElement, dimx, Allocation.USAGE_SCRIPT | usages);
-        }
-
-        protected FieldBase() {
-        }
-
-        public Element getElement() {
-            return mElement;
-        }
-
-        public Type getType() {
-            return mAllocation.getType();
-        }
-
-        public Allocation getAllocation() {
-            return mAllocation;
-        }
-
-        //@Override
-        public void updateAllocation() {
-        }
-    }
-
-
-    /**
-     * Class for specifying the specifics about how a kernel will be
-     * launched.
-     *
-     * This class can specify a potential range of cells on which to
-     * run a kernel.  If no set is called for a dimension then this
-     * class will have no impact on that dimension when the kernel
-     * is executed.
-     *
-     * The forEach kernel launch will operate over the intersection of
-     * the dimensions.
-     *
-     * Example:
-     * LaunchOptions with setX(5, 15)
-     * Allocation with dimension X=10, Y=10
-     * The resulting forEach run would execute over:
-     * x = 5 to 9 (inclusive) and
-     * y = 0 to 9 (inclusive).
-     *
-     */
-    public static final class LaunchOptions {
-        private int xstart = 0;
-        private int ystart = 0;
-        private int xend = 0;
-        private int yend = 0;
-        private int zstart = 0;
-        private int zend = 0;
-        private int strategy;
-
-        /**
-         * Set the X range. xstartArg is the lowest coordinate of the range,
-         * and xendArg-1 is the highest coordinate of the range.
-         *
-         * @param xstartArg Must be >= 0
-         * @param xendArg Must be > xstartArg
-         *
-         * @return LaunchOptions
-         */
-        public LaunchOptions setX(int xstartArg, int xendArg) {
-            if (xstartArg < 0 || xendArg <= xstartArg) {
-                throw new RSIllegalArgumentException("Invalid dimensions");
-            }
-            xstart = xstartArg;
-            xend = xendArg;
-            return this;
-        }
-
-        /**
-         * Set the Y range. ystartArg is the lowest coordinate of the range,
-         * and yendArg-1 is the highest coordinate of the range.
-         *
-         * @param ystartArg Must be >= 0
-         * @param yendArg Must be > ystartArg
-         *
-         * @return LaunchOptions
-         */
-        public LaunchOptions setY(int ystartArg, int yendArg) {
-            if (ystartArg < 0 || yendArg <= ystartArg) {
-                throw new RSIllegalArgumentException("Invalid dimensions");
-            }
-            ystart = ystartArg;
-            yend = yendArg;
-            return this;
-        }
-
-        /**
-         * Set the Z range. zstartArg is the lowest coordinate of the range,
-         * and zendArg-1 is the highest coordinate of the range.
-         *
-         * @param zstartArg Must be >= 0
-         * @param zendArg Must be > zstartArg
-         *
-         * @return LaunchOptions
-         */
-        public LaunchOptions setZ(int zstartArg, int zendArg) {
-            if (zstartArg < 0 || zendArg <= zstartArg) {
-                throw new RSIllegalArgumentException("Invalid dimensions");
-            }
-            zstart = zstartArg;
-            zend = zendArg;
-            return this;
-        }
-
-
-        /**
-         * Returns the current X start
-         *
-         * @return int current value
-         */
-        public int getXStart() {
-            return xstart;
-        }
-        /**
-         * Returns the current X end
-         *
-         * @return int current value
-         */
-        public int getXEnd() {
-            return xend;
-        }
-        /**
-         * Returns the current Y start
-         *
-         * @return int current value
-         */
-        public int getYStart() {
-            return ystart;
-        }
-        /**
-         * Returns the current Y end
-         *
-         * @return int current value
-         */
-        public int getYEnd() {
-            return yend;
-        }
-        /**
-         * Returns the current Z start
-         *
-         * @return int current value
-         */
-        public int getZStart() {
-            return zstart;
-        }
-        /**
-         * Returns the current Z end
-         *
-         * @return int current value
-         */
-        public int getZEnd() {
-            return zend;
-        }
-
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java
deleted file mode 100644
index 28e9613..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptC.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map.Entry;
-import java.util.HashMap;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
-/**
- * The superclass for all user-defined scripts. This is only
- * intended to be used by the generated derived classes.
- **/
-public class ScriptC extends Script {
-    private static final String TAG = "ScriptC";
-
-    /**
-     * Only intended for use by the generated derived classes.
-     *
-     * @param id
-     * @param rs
-     */
-    protected ScriptC(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Only intended for use by the generated derived classes.
-     *
-     *
-     * @param rs
-     * @param resources
-     * @param resourceID
-     */
-    protected ScriptC(RenderScript rs, Resources resources, int resourceID) {
-        super(0, rs);
-        long id = internalCreate(rs, resources, resourceID);
-        if (id == 0) {
-            throw new RSRuntimeException("Loading of ScriptC script failed.");
-        }
-        setID(id);
-    }
-
-    /**
-     * Only intended for use by the generated derived classes.
-     *
-     * @param rs
-     * @param resName
-     * @param bitcode32
-     * @param bitcode64
-     */
-    protected ScriptC(RenderScript rs, String resName, byte[] bitcode32, byte[] bitcode64) {
-        super(0, rs);
-        long id = 0;
-        if (RenderScript.sPointerSize == 4) {
-            id = internalStringCreate(rs, resName, bitcode32);
-        } else {
-            id = internalStringCreate(rs, resName, bitcode64);
-        }
-        if (id == 0) {
-            throw new RSRuntimeException("Loading of ScriptC script failed.");
-        }
-        setID(id);
-    }
-
-    private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
-        byte[] pgm;
-        int pgmLength;
-        InputStream is = resources.openRawResource(resourceID);
-        try {
-            try {
-                pgm = new byte[1024];
-                pgmLength = 0;
-                while(true) {
-                    int bytesLeft = pgm.length - pgmLength;
-                    if (bytesLeft == 0) {
-                        byte[] buf2 = new byte[pgm.length * 2];
-                        System.arraycopy(pgm, 0, buf2, 0, pgm.length);
-                        pgm = buf2;
-                        bytesLeft = pgm.length - pgmLength;
-                    }
-                    int bytesRead = is.read(pgm, pgmLength, bytesLeft);
-                    if (bytesRead <= 0) {
-                        break;
-                    }
-                    pgmLength += bytesRead;
-                }
-            } finally {
-                is.close();
-            }
-        } catch(IOException e) {
-            throw new Resources.NotFoundException();
-        }
-
-        String resName = resources.getResourceEntryName(resourceID);
-        String cachePath = rs.getApplicationContext().getCacheDir().toString();
-
-        //        Log.v(TAG, "Create script for resource = " + resName + ", " + pgmLength + ", " + pgm);
-        //Log.v(TAG, " path = " + cachePath);
-        return rs.nScriptCCreate(resName, cachePath, pgm, pgmLength);
-    }
-
-    private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
-        //        Log.v(TAG, "Create script for resource = " + resName);
-        String cachePath = rs.getApplicationContext().getCacheDir().toString();
-        return rs.nScriptCCreate(resName, cachePath, bitcode, bitcode.length);
-    }
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
deleted file mode 100644
index 139fde2..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
+++ /dev/null
@@ -1,1180 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-import android.util.Pair;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A group of kernels that are executed
- * together with one execution call as if they were a single kernel
- * <p>
- * In addition to kernels, a script group may contain invocable functions as well.
- * A script group may take inputs and generate outputs, which are consumed and
- * produced by its member kernels.
- * Inside a script group, outputs from one kernel can be passed to another kernel as inputs.
- * The API disallows cyclic dependencies among kernels in a script group,
- * effectively making it a directed acyclic graph (DAG) of kernels.
- * <p>
- * Grouping kernels together allows for more efficient execution. For example,
- * runtime and compiler optimization can be applied to reduce computation and
- * communication overhead, and to make better use of the CPU and the GPU.
- **/
-public final class ScriptGroup extends BaseObj {
-    //FIXME: Change 23 to the codename when that is decided.
-    private static final int MIN_API_VERSION = 23;
-    private static final String TAG = "ScriptGroup";
-    IO mOutputs[];
-    IO mInputs[];
-    private boolean mUseIncSupp = false;
-    private ArrayList<Node> mNodes = new ArrayList<Node>();
-
-    static class IO {
-        Script.KernelID mKID;
-        Allocation mAllocation;
-
-        IO(Script.KernelID s) {
-            mKID = s;
-        }
-    }
-
-    static class ConnectLine {
-        ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
-            mFrom = from;
-            mToK = to;
-            mAllocationType = t;
-        }
-
-        ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
-            mFrom = from;
-            mToF = to;
-            mAllocationType = t;
-        }
-
-        Script.FieldID mToF;
-        Script.KernelID mToK;
-        Script.KernelID mFrom;
-        Type mAllocationType;
-        Allocation mAllocation;
-    }
-
-    static class Node {
-        Script mScript;
-        ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
-        ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
-        ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
-        int dagNumber;
-        boolean mSeen;
-        int mOrder;
-
-        Node mNext;
-
-        Node(Script s) {
-            mScript = s;
-        }
-    }
-
-    /**
-     * An opaque class for closures
-     * <p>
-     * A closure represents a function call to a kernel or invocable function,
-     * combined with arguments and values for global variables. A closure is
-     * created using the {@link Builder2#addKernel} or
-     * {@link Builder2#addInvoke}
-     * method.
-     */
-
-    public static final class Closure extends BaseObj {
-        private Object[] mArgs;
-        private Allocation mReturnValue;
-        private Map<Script.FieldID, Object> mBindings;
-
-        private Future mReturnFuture;
-        private Map<Script.FieldID, Future> mGlobalFuture;
-
-        private FieldPacker mFP;
-
-        private static final String TAG = "Closure";
-
-        Closure(long id, RenderScript rs) {
-            super(id, rs);
-        }
-
-        Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
-                       Object[] args, Map<Script.FieldID, Object> globals) {
-            super(0, rs);
-
-            if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
-                throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
-            }
-
-            mArgs = args;
-            mReturnValue = Allocation.createTyped(rs, returnType);
-            mBindings = globals;
-            mGlobalFuture = new HashMap<Script.FieldID, Future>();
-
-            int numValues = args.length + globals.size();
-
-            long[] fieldIDs = new long[numValues];
-            long[] values = new long[numValues];
-            int[] sizes = new int[numValues];
-            long[] depClosures = new long[numValues];
-            long[] depFieldIDs = new long[numValues];
-
-            int i;
-            for (i = 0; i < args.length; i++) {
-                fieldIDs[i] = 0;
-                retrieveValueAndDependenceInfo(rs, i, null, args[i],
-                                               values, sizes, depClosures, depFieldIDs);
-            }
-            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
-                Object obj = entry.getValue();
-                Script.FieldID fieldID = entry.getKey();
-                fieldIDs[i] = fieldID.getID(rs);
-                retrieveValueAndDependenceInfo(rs, i, fieldID, obj,
-                                               values, sizes, depClosures, depFieldIDs);
-                i++;
-            }
-
-            long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
-                                        fieldIDs, values, sizes, depClosures, depFieldIDs);
-
-            setID(id);
-        }
-
-        Closure(RenderScript rs, Script.InvokeID invokeID,
-                Object[] args, Map<Script.FieldID, Object> globals) {
-            super(0, rs);
-
-            if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
-                throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
-            }
-
-            mFP = FieldPacker.createFromArray(args);
-
-            mArgs = args;
-            mBindings = globals;
-            mGlobalFuture = new HashMap<Script.FieldID, Future>();
-
-            int numValues = globals.size();
-
-            long[] fieldIDs = new long[numValues];
-            long[] values = new long[numValues];
-            int[] sizes = new int[numValues];
-            long[] depClosures = new long[numValues];
-            long[] depFieldIDs = new long[numValues];
-
-            int i = 0;
-            for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
-                Object obj = entry.getValue();
-                Script.FieldID fieldID = entry.getKey();
-                fieldIDs[i] = fieldID.getID(rs);
-                retrieveValueAndDependenceInfo(rs, i, fieldID, obj, values,
-                                               sizes, depClosures, depFieldIDs);
-                i++;
-            }
-
-            long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
-                                              values, sizes);
-
-            setID(id);
-        }
-
-        private void retrieveValueAndDependenceInfo(RenderScript rs,
-                                                    int index, Script.FieldID fid, Object obj,
-                                                    long[] values, int[] sizes,
-                                                    long[] depClosures,
-                                                    long[] depFieldIDs) {
-
-            if (obj instanceof Future) {
-                Future f = (Future)obj;
-                obj = f.getValue();
-                depClosures[index] = f.getClosure().getID(rs);
-                Script.FieldID fieldID = f.getFieldID();
-                depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
-            } else {
-                depClosures[index] = 0;
-                depFieldIDs[index] = 0;
-            }
-
-            if (obj instanceof Input) {
-                Input unbound = (Input)obj;
-                if (index < mArgs.length) {
-                    unbound.addReference(this, index);
-                } else {
-                    unbound.addReference(this, fid);
-                }
-                values[index] = 0;
-                sizes[index] = 0;
-            } else {
-                ValueAndSize vs = new ValueAndSize(rs, obj);
-                values[index] = vs.value;
-                sizes[index] = vs.size;
-            }
-        }
-
-        /**
-         * Returns the future for the return value
-         *
-         * @return a future
-         */
-
-        public Future getReturn() {
-            if (mReturnFuture == null) {
-                mReturnFuture = new Future(this, null, mReturnValue);
-            }
-
-            return mReturnFuture;
-        }
-
-        /**
-         * Returns the future for a global variable
-         *
-         * @param field the field ID for the global variable
-         * @return a future
-         */
-
-        public Future getGlobal(Script.FieldID field) {
-            Future f = mGlobalFuture.get(field);
-
-            if (f == null) {
-                // If the field is not bound to this closure, this will return a future
-                // without an associated value (reference). So this is not working for
-                // cross-module (cross-script) linking in this case where a field not
-                // explicitly bound.
-                Object obj = mBindings.get(field);
-                if (obj instanceof Future) {
-                    obj = ((Future)obj).getValue();
-                }
-                f = new Future(this, field, obj);
-                mGlobalFuture.put(field, f);
-            }
-
-            return f;
-        }
-
-        void setArg(int index, Object obj) {
-            if (obj instanceof Future) {
-                obj = ((Future)obj).getValue();
-            }
-            mArgs[index] = obj;
-            ValueAndSize vs = new ValueAndSize(mRS, obj);
-            mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
-        }
-
-        void setGlobal(Script.FieldID fieldID, Object obj) {
-            if (obj instanceof Future) {
-                obj = ((Future)obj).getValue();
-            }
-            mBindings.put(fieldID, obj);
-            ValueAndSize vs = new ValueAndSize(mRS, obj);
-            mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
-        }
-
-        private static final class ValueAndSize {
-            public ValueAndSize(RenderScript rs, Object obj) {
-                if (obj instanceof Allocation) {
-                    value = ((Allocation)obj).getID(rs);
-                    size = -1;
-                } else if (obj instanceof Boolean) {
-                    value = ((Boolean)obj).booleanValue() ? 1 : 0;
-                    size = 4;
-                } else if (obj instanceof Integer) {
-                    value = ((Integer)obj).longValue();
-                    size = 4;
-                } else if (obj instanceof Long) {
-                    value = ((Long)obj).longValue();
-                    size = 8;
-                } else if (obj instanceof Float) {
-                    value = Float.floatToRawIntBits(((Float)obj).floatValue());
-                    size = 4;
-                } else if (obj instanceof Double) {
-                    value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
-                    size = 8;
-                }
-            }
-            public long value;
-            public int size;
-        }
-    }
-
-    /**
-     * An opaque class for futures
-     * <p>
-     * A future represents an output of a closure, either the return value of
-     * the function, or the value of a global variable written by the function.
-     * A future is created by calling the {@link Closure#getReturn}  or
-     * {@link Closure#getGlobal} method.
-     */
-
-    public static final class Future {
-        Closure mClosure;
-        Script.FieldID mFieldID;
-        Object mValue;
-
-        Future(Closure closure, Script.FieldID fieldID, Object value) {
-            mClosure = closure;
-            mFieldID = fieldID;
-            mValue = value;
-        }
-
-        Closure getClosure() { return mClosure; }
-        Script.FieldID getFieldID() { return mFieldID; }
-        Object getValue() { return mValue; }
-    }
-
-    /**
-     * An opaque class for unbound values (used for script group inputs)
-     * <p>
-     * Created by calling the {@link Builder2#addInput} method. The value
-     * is assigned in {@link ScriptGroup#execute(Object...)} method as
-     * one of its arguments. Arguments to the execute method should be in
-     * the same order as intputs are added using the addInput method.
-     */
-
-    public static final class Input {
-        // Either mFieldID or mArgIndex should be set but not both.
-        List<Pair<Closure, Script.FieldID>> mFieldID;
-        // -1 means unset. Legal values are 0 .. n-1, where n is the number of
-        // arguments for the referencing closure.
-        List<Pair<Closure, Integer>> mArgIndex;
-        Object mValue;
-
-        Input() {
-            mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
-            mArgIndex = new ArrayList<Pair<Closure, Integer>>();
-        }
-
-        void addReference(Closure closure, int index) {
-            mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
-        }
-
-        void addReference(Closure closure, Script.FieldID fieldID) {
-            mFieldID.add(Pair.create(closure, fieldID));
-        }
-
-        void set(Object value) {
-            mValue = value;
-            for (Pair<Closure, Integer> p : mArgIndex) {
-                Closure closure = p.first;
-                int index = p.second.intValue();
-                closure.setArg(index, value);
-            }
-            for (Pair<Closure, Script.FieldID> p : mFieldID) {
-                Closure closure = p.first;
-                Script.FieldID fieldID = p.second;
-                closure.setGlobal(fieldID, value);
-            }
-        }
-
-        Object get() { return mValue; }
-    }
-
-    private String mName;
-    private List<Closure> mClosures;
-    private List<Input> mInputs2;
-    private Future[] mOutputs2;
-
-    ScriptGroup(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    ScriptGroup(RenderScript rs, String name, List<Closure> closures,
-                List<Input> inputs, Future[] outputs) {
-        super(0, rs);
-
-        if (android.os.Build.VERSION.SDK_INT < MIN_API_VERSION && rs.isUseNative()) {
-            throw new RSRuntimeException("ScriptGroup2 not supported in this API level");
-        }
-        mName = name;
-        mClosures = closures;
-        mInputs2 = inputs;
-        mOutputs2 = outputs;
-
-        long[] closureIDs = new long[closures.size()];
-        for (int i = 0; i < closureIDs.length; i++) {
-            closureIDs[i] = closures.get(i).getID(rs);
-        }
-        String cachePath = rs.getApplicationContext().getCacheDir().toString();
-        long id = rs.nScriptGroup2Create(name, cachePath, closureIDs);
-        setID(id);
-    }
-
-    /**
-     * Executes a script group
-     *
-     * @param inputs inputs to the script group
-     * @return outputs of the script group as an array of objects
-     */
-
-    public Object[] execute(Object... inputs) {
-        if (inputs.length < mInputs2.size()) {
-            Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
-                  "less than expected " + mInputs2.size());
-            return null;
-        }
-
-        if (inputs.length > mInputs2.size()) {
-            Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
-                  "more than expected " + mInputs2.size());
-        }
-
-        for (int i = 0; i < mInputs2.size(); i++) {
-            Object obj = inputs[i];
-            if (obj instanceof Future || obj instanceof Input) {
-                Log.e(TAG, this.toString() + ": input " + i +
-                      " is a future or unbound value");
-                return null;
-            }
-            Input unbound = mInputs2.get(i);
-            unbound.set(obj);
-        }
-
-        mRS.nScriptGroup2Execute(getID(mRS));
-
-        Object[] outputObjs = new Object[mOutputs2.length];
-        int i = 0;
-        for (Future f : mOutputs2) {
-            Object output = f.getValue();
-            if (output instanceof Input) {
-                output = ((Input)output).get();
-            }
-            outputObjs[i++] = output;
-        }
-        return outputObjs;
-    }
-
-    /**
-     * Sets an input of the ScriptGroup. This specifies an
-     * Allocation to be used for kernels that require an input
-     * Allocation provided from outside of the ScriptGroup.
-     *
-     * @deprecated Set arguments to {@link #execute(Object...)} instead.
-     *
-     * @param s The ID of the kernel where the allocation should be
-     *          connected.
-     * @param a The allocation to connect.
-     */
-    @Deprecated
-    public void setInput(Script.KernelID s, Allocation a) {
-        for (int ct=0; ct < mInputs.length; ct++) {
-            if (mInputs[ct].mKID == s) {
-                mInputs[ct].mAllocation = a;
-                if (!mUseIncSupp) {
-                    mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
-                }
-                return;
-            }
-        }
-        throw new RSIllegalArgumentException("Script not found");
-    }
-
-    /**
-     * Sets an output of the ScriptGroup. This specifies an
-     * Allocation to be used for the kernels that require an output
-     * Allocation visible after the ScriptGroup is executed.
-     *
-     * @deprecated Use return value of {@link #execute(Object...)} instead.
-     *
-     * @param s The ID of the kernel where the allocation should be
-     *          connected.
-     * @param a The allocation to connect.
-     */
-    @Deprecated
-    public void setOutput(Script.KernelID s, Allocation a) {
-        for (int ct=0; ct < mOutputs.length; ct++) {
-            if (mOutputs[ct].mKID == s) {
-                mOutputs[ct].mAllocation = a;
-                if (!mUseIncSupp) {
-                    mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
-                }
-                return;
-            }
-        }
-        throw new RSIllegalArgumentException("Script not found");
-    }
-
-    /**
-     * Execute the ScriptGroup.  This will run all the kernels in
-     * the ScriptGroup.  No internal connection results will be visible
-     * after execution of the ScriptGroup.
-     *
-     * If Incremental Support for intrinsics is needed, the execution
-     * will take the naive path: execute kernels one by one in the
-     * correct order.
-     *
-     * @deprecated Use {@link #execute} instead.
-     */
-    @Deprecated
-    public void execute() {
-        if (!mUseIncSupp) {
-            mRS.nScriptGroupExecute(getID(mRS));
-        } else {
-            // setup the allocations.
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                Node n = mNodes.get(ct);
-                for (int ct2=0; ct2 < n.mOutputs.size(); ct2++) {
-                    ConnectLine l = n.mOutputs.get(ct2);
-                    if (l.mAllocation !=null) {
-                        continue;
-                    }
-
-                    //create allocation here
-                    Allocation alloc = Allocation.createTyped(mRS, l.mAllocationType,
-                                                              Allocation.MipmapControl.MIPMAP_NONE,
-                                                              Allocation.USAGE_SCRIPT);
-
-                    l.mAllocation = alloc;
-                    for (int ct3=ct2+1; ct3 < n.mOutputs.size(); ct3++) {
-                        if (n.mOutputs.get(ct3).mFrom == l.mFrom) {
-                            n.mOutputs.get(ct3).mAllocation = alloc;
-                        }
-                    }
-                }
-            }
-            for (Node node : mNodes) {
-                for (Script.KernelID kernel : node.mKernels) {
-                    Allocation ain  = null;
-                    Allocation aout = null;
-
-                    for (ConnectLine nodeInput : node.mInputs) {
-                        if (nodeInput.mToK == kernel) {
-                            ain = nodeInput.mAllocation;
-                        }
-                    }
-
-                    for (IO sgInput : mInputs) {
-                        if (sgInput.mKID == kernel) {
-                            ain = sgInput.mAllocation;
-                        }
-                    }
-
-                    for (ConnectLine nodeOutput : node.mOutputs) {
-                        if (nodeOutput.mFrom == kernel) {
-                            aout = nodeOutput.mAllocation;
-                        }
-                    }
-
-                    for (IO sgOutput : mOutputs) {
-                        if (sgOutput.mKID == kernel) {
-                            aout = sgOutput.mAllocation;
-                        }
-                    }
-
-                    kernel.mScript.forEach(kernel.mSlot, ain, aout, null);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Helper class to build a ScriptGroup. A ScriptGroup is
-     * created in two steps.
-     * <p>
-     * First, all kernels to be used by the ScriptGroup should be added.
-     * <p>
-     * Second, add connections between kernels. There are two types
-     * of connections: kernel to kernel and kernel to field.
-     * Kernel to kernel allows a kernel's output to be passed to
-     * another kernel as input. Kernel to field allows the output of
-     * one kernel to be bound as a script global. Kernel to kernel is
-     * higher performance and should be used where possible.
-     * <p>
-     * A ScriptGroup must contain a single directed acyclic graph (DAG); it
-     * cannot contain cycles. Currently, all kernels used in a ScriptGroup
-     * must come from different Script objects. Additionally, all kernels
-     * in a ScriptGroup must have at least one input, output, or internal
-     * connection.
-     * <p>
-     * Once all connections are made, a call to {@link #create} will
-     * return the ScriptGroup object.
-     *
-     * @deprecated Use {@link Builder2} instead.
-     *
-     */
-    @Deprecated
-    public static final class Builder {
-        private RenderScript mRS;
-        private ArrayList<Node> mNodes = new ArrayList<Node>();
-        private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
-        private int mKernelCount;
-        private boolean mUseIncSupp = false;
-
-        /**
-         * Create a Builder for generating a ScriptGroup.
-         *
-         *
-         * @param rs The RenderScript context.
-         */
-        public Builder(RenderScript rs) {
-            mRS = rs;
-        }
-
-        // do a DFS from original node, looking for original node
-        // any cycle that could be created must contain original node
-        private void validateCycle(Node target, Node original) {
-            for (int ct = 0; ct < target.mOutputs.size(); ct++) {
-                final ConnectLine cl = target.mOutputs.get(ct);
-                if (cl.mToK != null) {
-                    Node tn = findNode(cl.mToK.mScript);
-                    if (tn.equals(original)) {
-                        throw new RSInvalidStateException("Loops in group not allowed.");
-                    }
-                    validateCycle(tn, original);
-                }
-                if (cl.mToF != null) {
-                    Node tn = findNode(cl.mToF.mScript);
-                    if (tn.equals(original)) {
-                        throw new RSInvalidStateException("Loops in group not allowed.");
-                    }
-                    validateCycle(tn, original);
-                }
-            }
-        }
-
-        private void mergeDAGs(int valueUsed, int valueKilled) {
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                if (mNodes.get(ct).dagNumber == valueKilled)
-                    mNodes.get(ct).dagNumber = valueUsed;
-            }
-        }
-
-        private void validateDAGRecurse(Node n, int dagNumber) {
-            // combine DAGs if this node has been seen already
-            if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
-                mergeDAGs(n.dagNumber, dagNumber);
-                return;
-            }
-
-            n.dagNumber = dagNumber;
-            for (int ct=0; ct < n.mOutputs.size(); ct++) {
-                final ConnectLine cl = n.mOutputs.get(ct);
-                if (cl.mToK != null) {
-                    Node tn = findNode(cl.mToK.mScript);
-                    validateDAGRecurse(tn, dagNumber);
-                }
-                if (cl.mToF != null) {
-                    Node tn = findNode(cl.mToF.mScript);
-                    validateDAGRecurse(tn, dagNumber);
-                }
-            }
-        }
-
-        private void validateDAG() {
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                Node n = mNodes.get(ct);
-                if (n.mInputs.size() == 0) {
-                    if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
-                        String msg = "Groups cannot contain unconnected scripts";
-                        throw new RSInvalidStateException(msg);
-                    }
-                    validateDAGRecurse(n, ct+1);
-                }
-            }
-            int dagNumber = mNodes.get(0).dagNumber;
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                if (mNodes.get(ct).dagNumber != dagNumber) {
-                    throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
-                }
-            }
-        }
-
-        private Node findNode(Script s) {
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                if (s == mNodes.get(ct).mScript) {
-                    return mNodes.get(ct);
-                }
-            }
-            return null;
-        }
-
-        private Node findNode(Script.KernelID k) {
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                Node n = mNodes.get(ct);
-                for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
-                    if (k == n.mKernels.get(ct2)) {
-                        return n;
-                    }
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Adds a Kernel to the group.
-         *
-         *
-         * @param k The kernel to add.
-         *
-         * @return Builder Returns this.
-         */
-        public Builder addKernel(Script.KernelID k) {
-            if (mLines.size() != 0) {
-                throw new RSInvalidStateException(
-                    "Kernels may not be added once connections exist.");
-            }
-            if (k.mScript.isIncSupp()) {
-                mUseIncSupp = true;
-            }
-            //android.util.Log.v("RSR", "addKernel 1 k=" + k);
-            if (findNode(k) != null) {
-                return this;
-            }
-            //android.util.Log.v("RSR", "addKernel 2 ");
-            mKernelCount++;
-            Node n = findNode(k.mScript);
-            if (n == null) {
-                //android.util.Log.v("RSR", "addKernel 3 ");
-                n = new Node(k.mScript);
-                mNodes.add(n);
-            }
-            n.mKernels.add(k);
-            return this;
-        }
-
-        /**
-         * Adds a connection to the group.
-         *
-         *
-         * @param t The type of the connection. This is used to
-         *          determine the kernel launch sizes on the source side
-         *          of this connection.
-         * @param from The source for the connection.
-         * @param to The destination of the connection.
-         *
-         * @return Builder Returns this
-         */
-        public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
-            //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
-            Node nf = findNode(from);
-            if (nf == null) {
-                throw new RSInvalidStateException("From script not found.");
-            }
-
-            Node nt = findNode(to.mScript);
-            if (nt == null) {
-                throw new RSInvalidStateException("To script not found.");
-            }
-
-            ConnectLine cl = new ConnectLine(t, from, to);
-            mLines.add(new ConnectLine(t, from, to));
-
-            nf.mOutputs.add(cl);
-            nt.mInputs.add(cl);
-
-            validateCycle(nf, nf);
-            return this;
-        }
-
-        /**
-         * Adds a connection to the group.
-         *
-         *
-         * @param t The type of the connection. This is used to
-         *          determine the kernel launch sizes for both sides of
-         *          this connection.
-         * @param from The source for the connection.
-         * @param to The destination of the connection.
-         *
-         * @return Builder Returns this
-         */
-        public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
-            //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
-            Node nf = findNode(from);
-            if (nf == null) {
-                throw new RSInvalidStateException("From script not found.");
-            }
-
-            Node nt = findNode(to);
-            if (nt == null) {
-                throw new RSInvalidStateException("To script not found.");
-            }
-
-            ConnectLine cl = new ConnectLine(t, from, to);
-            mLines.add(new ConnectLine(t, from, to));
-
-            nf.mOutputs.add(cl);
-            nt.mInputs.add(cl);
-
-            validateCycle(nf, nf);
-            return this;
-        }
-
-        /**
-         * Calculate the order of each node.
-         *
-         *
-         * @return Success or Fail
-         */
-        private boolean calcOrderRecurse(Node node0, int depth) {
-            node0.mSeen = true;
-            if (node0.mOrder < depth) {
-                node0.mOrder = depth;
-            }
-            boolean ret = true;
-
-            for (ConnectLine link : node0.mOutputs) {
-                Node node1 = null;
-                if (link.mToF != null) {
-                    node1 = findNode(link.mToF.mScript);
-                } else {
-                    node1 = findNode(link.mToK.mScript);
-                }
-                if (node1.mSeen) {
-                    return false;
-                }
-                ret &= calcOrderRecurse(node1, node0.mOrder + 1);
-            }
-
-            return ret;
-        }
-
-        private boolean calcOrder() {
-            boolean ret = true;
-            for (Node n0 : mNodes) {
-                if (n0.mInputs.size() == 0) {
-                    for (Node n1 : mNodes) {
-                        n1.mSeen = false;
-                    }
-                    ret &= calcOrderRecurse(n0, 1);
-                }
-            }
-
-            Collections.sort(mNodes, new Comparator<Node>() {
-                public int compare(Node n1, Node n2) {
-                    return n1.mOrder - n2.mOrder;
-                }
-            });
-
-            return ret;
-        }
-
-        /**
-         * Creates the Script group.
-         *
-         *
-         * @return ScriptGroup The new ScriptGroup
-         */
-        public ScriptGroup create() {
-
-            if (mNodes.size() == 0) {
-                throw new RSInvalidStateException("Empty script groups are not allowed");
-            }
-
-            // reset DAG numbers in case we're building a second group
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                mNodes.get(ct).dagNumber = 0;
-            }
-            validateDAG();
-
-            ArrayList<IO> inputs = new ArrayList<IO>();
-            ArrayList<IO> outputs = new ArrayList<IO>();
-
-            long[] kernels = new long[mKernelCount];
-            int idx = 0;
-            for (int ct=0; ct < mNodes.size(); ct++) {
-                Node n = mNodes.get(ct);
-                for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
-                    final Script.KernelID kid = n.mKernels.get(ct2);
-                    kernels[idx++] = kid.getID(mRS);
-
-                    boolean hasInput = false;
-                    boolean hasOutput = false;
-                    for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
-                        if (n.mInputs.get(ct3).mToK == kid) {
-                            hasInput = true;
-                        }
-                    }
-                    for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
-                        if (n.mOutputs.get(ct3).mFrom == kid) {
-                            hasOutput = true;
-                        }
-                    }
-                    if (!hasInput) {
-                        inputs.add(new IO(kid));
-                    }
-                    if (!hasOutput) {
-                        outputs.add(new IO(kid));
-                    }
-                }
-            }
-            if (idx != mKernelCount) {
-                throw new RSRuntimeException("Count mismatch, should not happen.");
-            }
-
-            long id = 0;
-            if (!mUseIncSupp) {
-                long[] src = new long[mLines.size()];
-                long[] dstk = new long[mLines.size()];
-                long[] dstf = new long[mLines.size()];
-                long[] types = new long[mLines.size()];
-
-                for (int ct=0; ct < mLines.size(); ct++) {
-                    ConnectLine cl = mLines.get(ct);
-                    src[ct] = cl.mFrom.getID(mRS);
-                    if (cl.mToK != null) {
-                        dstk[ct] = cl.mToK.getID(mRS);
-                    }
-                    if (cl.mToF != null) {
-                        dstf[ct] = cl.mToF.getID(mRS);
-                    }
-                    types[ct] = cl.mAllocationType.getID(mRS);
-                }
-                id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
-                if (id == 0) {
-                    throw new RSRuntimeException("Object creation error, should not happen.");
-                }
-            } else {
-                //Calculate the order of the DAG so that script can run one after another.
-                calcOrder();
-            }
-
-            ScriptGroup sg = new ScriptGroup(id, mRS);
-            sg.mOutputs = new IO[outputs.size()];
-            for (int ct=0; ct < outputs.size(); ct++) {
-                sg.mOutputs[ct] = outputs.get(ct);
-            }
-
-            sg.mInputs = new IO[inputs.size()];
-            for (int ct=0; ct < inputs.size(); ct++) {
-                sg.mInputs[ct] = inputs.get(ct);
-            }
-            sg.mNodes = mNodes;
-            sg.mUseIncSupp = mUseIncSupp;
-            return sg;
-        }
-
-    }
-
-    /**
-     * Represents a binding of a value to a global variable in a
-     * kernel or invocable function. Used in closure creation.
-     */
-
-    public static final class Binding {
-        private final Script.FieldID mField;
-        private final Object mValue;
-
-        /**
-         * Returns a Binding object that binds value to field
-         *
-         * @param field the Script.FieldID of the global variable
-         * @param value the value
-         */
-
-        public Binding(Script.FieldID field, Object value) {
-            mField = field;
-            mValue = value;
-        }
-
-        /**
-         * Returns the field ID
-         */
-
-        public Script.FieldID getField() { return mField; }
-
-        /**
-         * Returns the value
-         */
-
-        public Object getValue() { return mValue; }
-    }
-
-    /**
-     * The builder class for creating script groups
-     * <p>
-     * A script group is created using closures (see class {@link Closure}).
-     * A closure is a function call to a kernel or
-     * invocable function. Each function argument or global variable accessed inside
-     * the function is bound to 1) a known value, 2) a script group input
-     * (see class {@link Input}), or 3) a
-     * future (see class {@link Future}).
-     * A future is the output of a closure, either the return value of the
-     * function or a global variable written by that function.
-     * <p>
-     * Closures are created using the {@link #addKernel} or {@link #addInvoke}
-     * methods.
-     * When a closure is created, futures from previously created closures
-     * can be used as its inputs.
-     * External script group inputs can be used as inputs to individual closures as well.
-     * An external script group input is created using the {@link #addInput} method.
-     * A script group is created by a call to the {@link #create} method, which
-     * accepts an array of futures as the outputs for the script group.
-     * <p>
-     * Closures in a script group can be evaluated in any order as long as the
-     * following conditions are met:
-     * 1) a closure must be evaluated before any other closures that take its
-     * futures as inputs;
-     * 2) all closures added before an invoke closure must be evaluated
-     * before it;
-     * and 3) all closures added after an invoke closure must be evaluated after
-     * it.
-     * As a special case, the order that the closures are added is a legal
-     * evaluation order. However, other evaluation orders are possible, including
-     * concurrently evaluating independent closures.
-     */
-
-    public static final class Builder2 {
-        RenderScript mRS;
-        List<Closure> mClosures;
-        List<Input> mInputs;
-        private static final String TAG = "ScriptGroup.Builder2";
-
-        /**
-         * Returns a Builder object
-         *
-         * @param rs the RenderScript context
-         */
-        public Builder2(RenderScript rs) {
-            mRS = rs;
-            mClosures = new ArrayList<Closure>();
-            mInputs = new ArrayList<Input>();
-        }
-
-        /**
-         * Adds a closure for a kernel
-         *
-         * @param k Kernel ID for the kernel function
-         * @param returnType Allocation type for the return value
-         * @param args arguments to the kernel function
-         * @param globalBindings bindings for global variables
-         * @return a closure
-         */
-
-        private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args,
-                                          Map<Script.FieldID, Object> globalBindings) {
-            Closure c = new Closure(mRS, k, returnType, args, globalBindings);
-            mClosures.add(c);
-            return c;
-        }
-
-        /**
-         * Adds a closure for an invocable function
-         *
-         * @param invoke Invoke ID for the invocable function
-         * @param args arguments to the invocable function
-         * @param globalBindings bindings for global variables
-         * @return a closure
-         */
-
-        private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args,
-                                          Map<Script.FieldID, Object> globalBindings) {
-            Closure c = new Closure(mRS, invoke, args, globalBindings);
-            mClosures.add(c);
-            return c;
-        }
-
-        /**
-         * Adds a script group input
-         *
-         * @return a script group input, which can be used as an argument or a value to
-         *     a global variable for creating closures
-         */
-
-        public Input addInput() {
-            Input unbound = new Input();
-            mInputs.add(unbound);
-            return unbound;
-        }
-
-        /**
-         * Adds a closure for a kernel
-         *
-         * @param k Kernel ID for the kernel function
-         * @param argsAndBindings arguments followed by bindings for global variables
-         * @return a closure
-         */
-
-        public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
-            ArrayList<Object> args = new ArrayList<Object>();
-            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
-            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
-                return null;
-            }
-            return addKernelInternal(k, returnType, args.toArray(), bindingMap);
-        }
-
-        /**
-         * Adds a closure for an invocable function
-         *
-         * @param invoke Invoke ID for the invocable function
-         * @param argsAndBindings arguments followed by bindings for global variables
-         * @return a closure
-         */
-
-        public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
-            ArrayList<Object> args = new ArrayList<Object>();
-            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
-            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
-                return null;
-            }
-            return addInvokeInternal(invoke, args.toArray(), bindingMap);
-        }
-
-        /**
-         * Creates a script group
-         *
-         * @param name name for the script group. Legal names can only contain letters, digits,
-         *        '-', or '_'. The name can be no longer than 100 characters.
-         * @param outputs futures intended as outputs of the script group
-         * @return a script group
-         */
-
-        public ScriptGroup create(String name, Future... outputs) {
-            if (name == null || name.isEmpty() || name.length() > 100 ||
-                !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
-                throw new RSIllegalArgumentException("invalid script group name");
-            }
-            ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
-            return ret;
-        }
-
-        private boolean seperateArgsAndBindings(Object[] argsAndBindings,
-                                                ArrayList<Object> args,
-                                                Map<Script.FieldID, Object> bindingMap) {
-            int i;
-            for (i = 0; i < argsAndBindings.length; i++) {
-                if (argsAndBindings[i] instanceof Binding) {
-                    break;
-                }
-                args.add(argsAndBindings[i]);
-            }
-
-            for (; i < argsAndBindings.length; i++) {
-                if (!(argsAndBindings[i] instanceof Binding)) {
-                    return false;
-                }
-                Binding b = (Binding)argsAndBindings[i];
-                bindingMap.put(b.getField(), b.getValue());
-            }
-
-            return true;
-        }
-
-    }
-
-}
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java
deleted file mode 100644
index e59cf6d..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-/**
- * Base class for all Intrinsic scripts. An intrinsic a script
- * which implements a pre-defined function. Intrinsics are
- * provided to provide effecient implemtations of common
- * operations.
- *
- * Not intended for direct use.
- **/
-public abstract class ScriptIntrinsic extends Script {
-    ScriptIntrinsic(long id, RenderScript rs) {
-        super(id, rs);
-        if (id == 0) {
-            throw new RSRuntimeException("Loading of ScriptIntrinsic failed.");
-        }
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java
deleted file mode 100644
index 7fc71f3..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsic3DLUT.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-
-/**
- *
- * Intrinsic for converting RGB to RGBA by using a 3D lookup table.  The
- * incoming r,g,b values are use as normalized x,y,z coordinates into a 3D
- * allocation.  The 8 nearest values are sampled and linearly interpolated.  The
- * result is placed in the output.
- *
- **/
-public class ScriptIntrinsic3DLUT extends ScriptIntrinsic {
-    private Allocation mLUT;
-    private Element mElement;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    protected ScriptIntrinsic3DLUT(long id, RenderScript rs, Element e) {
-        super(id, rs);
-        mElement = e;
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8_4}
-     *
-     * The defaults tables are identity.
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for intputs and outputs
-     *
-     * @return ScriptIntrinsic3DLUT
-     */
-    public static ScriptIntrinsic3DLUT create(RenderScript rs, Element e) {
-        if (!e.isCompatible(Element.U8_4(rs))) {
-            throw new RSIllegalArgumentException("Element must be compatible with uchar4.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(8, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsic3DLUT si = new ScriptIntrinsic3DLUT(id, rs, e);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-    }
-
-    /**
-     * Sets the {@link android.support.v8.renderscript.Allocation} to be used as
-     * the lookup table.
-     *
-     * The lookup table must use the same
-     * {@link android.support.v8.renderscript.Element} as the intrinsic.
-     *
-     */
-
-    public void setLUT(Allocation lut) {
-        final Type t = lut.getType();
-
-        if (t.getZ() == 0) {
-            throw new RSIllegalArgumentException("LUT must be 3d.");
-        }
-
-        if (!t.getElement().isCompatible(mElement)) {
-            throw new RSIllegalArgumentException("LUT element type must match.");
-        }
-
-        mLUT = lut;
-        setVar(0, mLUT);
-    }
-
-
-    /**
-     * Invoke the kernel and apply the lookup to each cell of ain
-     * and copy to aout.
-     *
-     * @param ain Input allocation
-     * @param aout Output allocation
-     */
-    public void forEach(Allocation ain, Allocation aout) {
-        forEach(0, ain, aout, null);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 3, null, null);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBLAS.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBLAS.java
deleted file mode 100644
index fe32989..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBLAS.java
+++ /dev/null
@@ -1,4190 +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.v8.renderscript;
-
-import android.support.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- *
- * ScriptIntrinsicBLAS class provides high performance RenderScript APIs to BLAS.
- *
- * The BLAS (Basic Linear Algebra Subprograms) are routines that provide standard
- * building blocks for performing basic vector and matrix operations.
- *
- * For detailed description of BLAS, please refer to http://www.netlib.org/blas/
- *
- **/
-public final class ScriptIntrinsicBLAS extends ScriptIntrinsic {
-    private Allocation mLUT;
-    private static final int INTRINSIC_API_LEVEL = 23;
-
-    private ScriptIntrinsicBLAS(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    private static final int RsBlas_sdsdot = 1;
-    private static final int RsBlas_dsdot = 2;
-    private static final int RsBlas_sdot = 3;
-    private static final int RsBlas_ddot = 4;
-    private static final int RsBlas_cdotu_sub = 5;
-    private static final int RsBlas_cdotc_sub = 6;
-    private static final int RsBlas_zdotu_sub = 7;
-    private static final int RsBlas_zdotc_sub = 8;
-    private static final int RsBlas_snrm2 = 9;
-    private static final int RsBlas_sasum = 10;
-    private static final int RsBlas_dnrm2 = 11;
-    private static final int RsBlas_dasum = 12;
-    private static final int RsBlas_scnrm2 = 13;
-    private static final int RsBlas_scasum = 14;
-    private static final int RsBlas_dznrm2 = 15;
-    private static final int RsBlas_dzasum = 16;
-    private static final int RsBlas_isamax = 17;
-    private static final int RsBlas_idamax = 18;
-    private static final int RsBlas_icamax = 19;
-    private static final int RsBlas_izamax = 20;
-    private static final int RsBlas_sswap = 21;
-    private static final int RsBlas_scopy = 22;
-    private static final int RsBlas_saxpy = 23;
-    private static final int RsBlas_dswap = 24;
-    private static final int RsBlas_dcopy = 25;
-    private static final int RsBlas_daxpy = 26;
-    private static final int RsBlas_cswap = 27;
-    private static final int RsBlas_ccopy = 28;
-    private static final int RsBlas_caxpy = 29;
-    private static final int RsBlas_zswap = 30;
-    private static final int RsBlas_zcopy = 31;
-    private static final int RsBlas_zaxpy = 32;
-    private static final int RsBlas_srotg = 33;
-    private static final int RsBlas_srotmg = 34;
-    private static final int RsBlas_srot = 35;
-    private static final int RsBlas_srotm = 36;
-    private static final int RsBlas_drotg = 37;
-    private static final int RsBlas_drotmg = 38;
-    private static final int RsBlas_drot = 39;
-    private static final int RsBlas_drotm = 40;
-    private static final int RsBlas_sscal = 41;
-    private static final int RsBlas_dscal = 42;
-    private static final int RsBlas_cscal = 43;
-    private static final int RsBlas_zscal = 44;
-    private static final int RsBlas_csscal = 45;
-    private static final int RsBlas_zdscal = 46;
-    private static final int RsBlas_sgemv = 47;
-    private static final int RsBlas_sgbmv = 48;
-    private static final int RsBlas_strmv = 49;
-    private static final int RsBlas_stbmv = 50;
-    private static final int RsBlas_stpmv = 51;
-    private static final int RsBlas_strsv = 52;
-    private static final int RsBlas_stbsv = 53;
-    private static final int RsBlas_stpsv = 54;
-    private static final int RsBlas_dgemv = 55;
-    private static final int RsBlas_dgbmv = 56;
-    private static final int RsBlas_dtrmv = 57;
-    private static final int RsBlas_dtbmv = 58;
-    private static final int RsBlas_dtpmv = 59;
-    private static final int RsBlas_dtrsv = 60;
-    private static final int RsBlas_dtbsv = 61;
-    private static final int RsBlas_dtpsv = 62;
-    private static final int RsBlas_cgemv = 63;
-    private static final int RsBlas_cgbmv = 64;
-    private static final int RsBlas_ctrmv = 65;
-    private static final int RsBlas_ctbmv = 66;
-    private static final int RsBlas_ctpmv = 67;
-    private static final int RsBlas_ctrsv = 68;
-    private static final int RsBlas_ctbsv = 69;
-    private static final int RsBlas_ctpsv = 70;
-    private static final int RsBlas_zgemv = 71;
-    private static final int RsBlas_zgbmv = 72;
-    private static final int RsBlas_ztrmv = 73;
-    private static final int RsBlas_ztbmv = 74;
-    private static final int RsBlas_ztpmv = 75;
-    private static final int RsBlas_ztrsv = 76;
-    private static final int RsBlas_ztbsv = 77;
-    private static final int RsBlas_ztpsv = 78;
-    private static final int RsBlas_ssymv = 79;
-    private static final int RsBlas_ssbmv = 80;
-    private static final int RsBlas_sspmv = 81;
-    private static final int RsBlas_sger = 82;
-    private static final int RsBlas_ssyr = 83;
-    private static final int RsBlas_sspr = 84;
-    private static final int RsBlas_ssyr2 = 85;
-    private static final int RsBlas_sspr2 = 86;
-    private static final int RsBlas_dsymv = 87;
-    private static final int RsBlas_dsbmv = 88;
-    private static final int RsBlas_dspmv = 89;
-    private static final int RsBlas_dger = 90;
-    private static final int RsBlas_dsyr = 91;
-    private static final int RsBlas_dspr = 92;
-    private static final int RsBlas_dsyr2 = 93;
-    private static final int RsBlas_dspr2 = 94;
-    private static final int RsBlas_chemv = 95;
-    private static final int RsBlas_chbmv = 96;
-    private static final int RsBlas_chpmv = 97;
-    private static final int RsBlas_cgeru = 98;
-    private static final int RsBlas_cgerc = 99;
-    private static final int RsBlas_cher = 100;
-    private static final int RsBlas_chpr = 101;
-    private static final int RsBlas_cher2 = 102;
-    private static final int RsBlas_chpr2 = 103;
-    private static final int RsBlas_zhemv = 104;
-    private static final int RsBlas_zhbmv = 105;
-    private static final int RsBlas_zhpmv = 106;
-    private static final int RsBlas_zgeru = 107;
-    private static final int RsBlas_zgerc = 108;
-    private static final int RsBlas_zher = 109;
-    private static final int RsBlas_zhpr = 110;
-    private static final int RsBlas_zher2 = 111;
-    private static final int RsBlas_zhpr2 = 112;
-    private static final int RsBlas_sgemm = 113;
-    private static final int RsBlas_ssymm = 114;
-    private static final int RsBlas_ssyrk = 115;
-    private static final int RsBlas_ssyr2k = 116;
-    private static final int RsBlas_strmm = 117;
-    private static final int RsBlas_strsm = 118;
-    private static final int RsBlas_dgemm = 119;
-    private static final int RsBlas_dsymm = 120;
-    private static final int RsBlas_dsyrk = 121;
-    private static final int RsBlas_dsyr2k = 122;
-    private static final int RsBlas_dtrmm = 123;
-    private static final int RsBlas_dtrsm = 124;
-    private static final int RsBlas_cgemm = 125;
-    private static final int RsBlas_csymm = 126;
-    private static final int RsBlas_csyrk = 127;
-    private static final int RsBlas_csyr2k = 128;
-    private static final int RsBlas_ctrmm = 129;
-    private static final int RsBlas_ctrsm = 130;
-    private static final int RsBlas_zgemm = 131;
-    private static final int RsBlas_zsymm = 132;
-    private static final int RsBlas_zsyrk = 133;
-    private static final int RsBlas_zsyr2k = 134;
-    private static final int RsBlas_ztrmm = 135;
-    private static final int RsBlas_ztrsm = 136;
-    private static final int RsBlas_chemm = 137;
-    private static final int RsBlas_cherk = 138;
-    private static final int RsBlas_cher2k = 139;
-    private static final int RsBlas_zhemm = 140;
-    private static final int RsBlas_zherk = 141;
-    private static final int RsBlas_zher2k = 142;
-
-    // BLAS extensions start here
-    private static final int RsBlas_bnnm = 1000;
-
-    /**
-     * Create an intrinsic to access BLAS subroutines.
-     *
-     * @param rs The RenderScript context
-     * @return ScriptIntrinsicBLAS
-     */
-    public static ScriptIntrinsicBLAS create(RenderScript rs) {
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(13, Element.U32(rs).getID(rs), mUseIncSupp);
-        ScriptIntrinsicBLAS si = new ScriptIntrinsicBLAS(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-    }
-
-    /**
-     * @hide
-     */
-    @IntDef({NO_TRANSPOSE, TRANSPOSE, CONJ_TRANSPOSE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Transpose {}
-
-    /**
-     * @hide
-     */
-    @IntDef({UPPER, LOWER})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Uplo {}
-
-    /**
-     * @hide
-     */
-    @IntDef({NON_UNIT, UNIT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Diag {}
-
-    /**
-     * @hide
-     */
-    @IntDef({LEFT, RIGHT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Side {}
-
-    public static final int NO_TRANSPOSE = 111;
-    public static final int TRANSPOSE = 112;
-    public static final int CONJ_TRANSPOSE = 113;
-
-    public static final int UPPER = 121;
-    public static final int LOWER = 122;
-
-    public static final int NON_UNIT = 131;
-    public static final int UNIT = 132;
-
-    public static final int LEFT = 141;
-    public static final int RIGHT = 142;
-
-    static void validateSide(@Side int Side) {
-        if (Side != LEFT && Side != RIGHT) {
-            throw new RSRuntimeException("Invalid side passed to BLAS");
-        }
-    }
-
-    static void validateTranspose(@Transpose int Trans) {
-        if (Trans != NO_TRANSPOSE && Trans != TRANSPOSE &&
-            Trans != CONJ_TRANSPOSE) {
-            throw new RSRuntimeException("Invalid transpose passed to BLAS");
-        }
-    }
-
-    static void validateConjTranspose(@Transpose int Trans) {
-        if (Trans != NO_TRANSPOSE &&
-            Trans != CONJ_TRANSPOSE) {
-            throw new RSRuntimeException("Invalid transpose passed to BLAS");
-        }
-    }
-
-    static void validateDiag(@Diag int Diag) {
-        if (Diag != NON_UNIT && Diag != UNIT) {
-            throw new RSRuntimeException("Invalid diag passed to BLAS");
-        }
-    }
-
-    static void validateUplo(@Uplo int Uplo) {
-        if (Uplo != UPPER && Uplo != LOWER) {
-            throw new RSRuntimeException("Invalid uplo passed to BLAS");
-        }
-    }
-
-
-    /**
-     * Level 2 BLAS
-     */
-
-    static void validateGEMV(Element e, int TransA, Allocation A, Allocation X, int incX, Allocation Y, int incY) {
-        validateTranspose(TransA);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = -1, expectedYDim = -1;
-        if (TransA == NO_TRANSPOSE) {
-            expectedXDim = 1 + (N - 1) * incX;
-            expectedYDim = 1 + (M - 1) * incY;
-        } else {
-            expectedXDim = 1 + (M - 1) * incX;
-            expectedYDim = 1 + (N - 1) * incY;
-        }
-        if (X.getType().getX() != expectedXDim ||
-            Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for GEMV");
-        }
-    }
-
-    /**
-     * SGEMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d58/sgemv_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void SGEMV(@Transpose int TransA, float alpha, Allocation A, Allocation X, int incX, float beta, Allocation Y, int incY) {
-        validateGEMV(Element.F32(mRS), TransA, A, X, incX, Y, incY);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sgemv, TransA, 0, 0, 0, 0, M, N, 0, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DGEMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dc/da8/dgemv_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void DGEMV(@Transpose int TransA, double alpha, Allocation A, Allocation X, int incX, double beta, Allocation Y, int incY) {
-        validateGEMV(Element.F64(mRS), TransA, A, X, incX, Y, incY);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dgemv, TransA, 0, 0, 0, 0, M, N, 0, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CGEMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y   or   y := alpha*A**H*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/d8a/cgemv_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void CGEMV(@Transpose int TransA, Float2 alpha, Allocation A, Allocation X, int incX, Float2 beta, Allocation Y, int incY) {
-        validateGEMV(Element.F32_2(mRS), TransA, A, X, incX, Y, incY);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cgemv, TransA, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZGEMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y   or   y := alpha*A**H*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d40/zgemv_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void ZGEMV(@Transpose int TransA, Double2 alpha, Allocation A, Allocation X, int incX, Double2 beta, Allocation Y, int incY) {
-        validateGEMV(Element.F64_2(mRS), TransA, A, X, incX, Y, incY);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zgemv, TransA, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SGBMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d46/sgbmv_8f.html
-     *
-     * Note: For a M*N matrix, the input Allocation should also be of size M*N (dimY = M, dimX = N),
-     *       but only the region M*(KL+KU+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert the original matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, m):
-     *              for j in range(max(0, i-kl), min(i+ku+1, n)):
-     *                  b[i, j-i+kl] = a[i, j]
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param KL The number of sub-diagonals of the matrix A.
-     * @param KU The number of super-diagonals of the matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains the band matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void SGBMV(@Transpose int TransA, int KL, int KU, float alpha, Allocation A, Allocation X, int incX, float beta, Allocation Y, int incY) {
-        // GBMV has the same validation requirements as GEMV + KL and KU >= 0
-        validateGEMV(Element.F32(mRS), TransA, A, X, incX, Y, incY);
-        if (KL < 0 || KU < 0) {
-            throw new RSRuntimeException("KL and KU must be greater than or equal to 0");
-        }
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sgbmv, TransA, 0, 0, 0, 0, M, N, 0, alpha, aID, xID, beta, yID, incX, incY, KL, KU, mUseIncSupp);
-    }
-
-    /**
-     * DGBMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d3f/dgbmv_8f.html
-     *
-     * Note: For a M*N matrix, the input Allocation should also be of size M*N (dimY = M, dimX = N),
-     *       but only the region M*(KL+KU+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert the original matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, m):
-     *              for j in range(max(0, i-kl), min(i+ku+1, n)):
-     *                  b[i, j-i+kl] = a[i, j]
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param KL The number of sub-diagonals of the matrix A.
-     * @param KU The number of super-diagonals of the matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains the band matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void DGBMV(@Transpose int TransA, int KL, int KU, double alpha, Allocation A, Allocation X, int incX, double beta, Allocation Y, int incY) {
-        // GBMV has the same validation requirements as GEMV + KL and KU >= 0
-        validateGEMV(Element.F64(mRS), TransA, A, X, incX, Y, incY);
-        if (KL < 0 || KU < 0) {
-            throw new RSRuntimeException("KL and KU must be greater than or equal to 0");
-        }
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dgbmv, TransA, 0, 0, 0, 0, M, N, 0, alpha, aID, xID, beta, yID, incX, incY, KL, KU, mUseIncSupp);
-    }
-
-    /**
-     * CGBMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y   or   y := alpha*A**H*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d75/cgbmv_8f.html
-     *
-     * Note: For a M*N matrix, the input Allocation should also be of size M*N (dimY = M, dimX = N),
-     *       but only the region M*(KL+KU+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert the original matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, m):
-     *              for j in range(max(0, i-kl), min(i+ku+1, n)):
-     *                  b[i, j-i+kl] = a[i, j]
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param KL The number of sub-diagonals of the matrix A.
-     * @param KU The number of super-diagonals of the matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains the band matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void CGBMV(@Transpose int TransA, int KL, int KU, Float2 alpha, Allocation A, Allocation X, int incX, Float2 beta, Allocation Y, int incY) {
-        // GBMV has the same validation requirements as GEMV + KL and KU >= 0
-        validateGEMV(Element.F32_2(mRS), TransA, A, X, incX, Y, incY);
-        if (KL < 0 || KU < 0) {
-            throw new RSRuntimeException("KL and KU must be greater than or equal to 0");
-        }
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cgbmv, TransA, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, KL, KU, mUseIncSupp);
-    }
-
-    /**
-     * ZGBMV performs one of the matrix-vector operations
-     * y := alpha*A*x + beta*y   or   y := alpha*A**T*x + beta*y   or   y := alpha*A**H*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d9/d46/zgbmv_8f.html
-     *
-     * Note: For a M*N matrix, the input Allocation should also be of size M*N (dimY = M, dimX = N),
-     *       but only the region M*(KL+KU+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert the original matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, m):
-     *              for j in range(max(0, i-kl), min(i+ku+1, n)):
-     *                  b[i, j-i+kl] = a[i, j]
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param KL The number of sub-diagonals of the matrix A.
-     * @param KU The number of super-diagonals of the matrix A.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains the band matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void ZGBMV(@Transpose int TransA, int KL, int KU, Double2 alpha, Allocation A, Allocation X, int incX, Double2 beta, Allocation Y, int incY) {
-        // GBMV has the same validation requirements as GEMV + KL and KU >= 0
-        validateGEMV(Element.F64_2(mRS), TransA, A, X, incX, Y, incY);
-        if (KL < 0 || KU < 0) {
-            throw new RSRuntimeException("KL and KU must be greater than or equal to 0");
-        }
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zgbmv, TransA, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, KL, KU, mUseIncSupp);
-    }
-
-    static void validateTRMV(Element e, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation A, Allocation X, int incX) {
-        validateTranspose(TransA);
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        int N = A.getType().getY();
-        if (A.getType().getX() != N) {
-            throw new RSRuntimeException("A must be a square matrix for TRMV");
-        }
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (incX <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for TRMV");
-        }
-    }
-
-    static int validateTPMV(Element e, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation Ap, Allocation X, int incX) {
-        validateTranspose(TransA);
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        if (!Ap.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (Ap.getType().getY() > 1) {
-            throw new RSRuntimeException("Ap must have a Y dimension of 0 or 1");
-        }
-
-        int N = (int)Math.sqrt((double)Ap.getType().getX() * 2);
-        //is it really doing anything?
-        if (Ap.getType().getX() != ((N * (N+1)) / 2)) {
-            throw new RSRuntimeException("Invalid dimension for Ap");
-        }
-        if (incX <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for TPMV");
-        }
-
-        return N;
-    }
-
-    /**
-     * STRMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d45/strmv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STRMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation A, Allocation X, int incX) {
-        validateTRMV(Element.F32(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_strmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTRMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dc/d7e/dtrmv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTRMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation A, Allocation X, int incX) {
-        validateTRMV(Element.F64(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTRMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d78/ctrmv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTRMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation A, Allocation X, int incX) {
-        validateTRMV(Element.F32_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTRMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/dd1/ztrmv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTRMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Allocation A, Allocation X, int incX) {
-        validateTRMV(Element.F64_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * STBMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d7d/stbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STBMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBMV has the same requirements as TRMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        validateTRMV(Element.F32(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_stbmv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTBMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d29/dtbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTBMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBMV has the same requirements as TRMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        validateTRMV(Element.F64(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtbmv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTBMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/dcd/ctbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTBMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBMV has the same requirements as TRMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        validateTRMV(Element.F32_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctbmv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTBMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d39/ztbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTBMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBMV has the same requirements as TRMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        validateTRMV(Element.F64_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztbmv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * STPMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/db1/stpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STPMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        int N = validateTPMV(Element.F32(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_stpmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, apID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTPMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dc/dcd/dtpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTPMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        int N = validateTPMV(Element.F64(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtpmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, apID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTPMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/dbb/ctpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTPMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        int N = validateTPMV(Element.F32_2(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctpmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, apID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTPMV performs one of the matrix-vector operations
-     * x := A*x   or   x := A**T*x   or   x := A**H*x
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d9e/ztpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTPMV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        int N = validateTPMV(Element.F64_2(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztpmv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, apID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * STRSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d2a/strsv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STRSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation A,  Allocation X,  int incX) {
-        // TRSV is the same as TRMV
-        validateTRMV(Element.F32(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_strsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-
-    }
-
-    /**
-     * DTRSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d96/dtrsv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTRSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation A,  Allocation X,  int incX) {
-        // TRSV is the same as TRMV
-        validateTRMV(Element.F64(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-
-    }
-
-    /**
-     * CTRSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/dc8/ctrsv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTRSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation A,  Allocation X,  int incX) {
-        // TRSV is the same as TRMV
-        validateTRMV(Element.F32_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-
-    }
-
-    /**
-     * ZTRSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d1/d2f/ztrsv_8f.html
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTRSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation A,  Allocation X,  int incX) {
-        // TRSV is the same as TRMV
-        validateTRMV(Element.F64_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-
-    }
-
-    /**
-     * STBSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d1f/stbsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STBSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBSV is the same as TRMV + K >= 0
-        validateTRMV(Element.F32(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-        if (K < 0) {
-            throw new RSRuntimeException("Number of diagonals must be positive");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_stbsv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTBSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/dcf/dtbsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTBSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBSV is the same as TRMV + K >= 0
-        validateTRMV(Element.F64(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-        if (K < 0) {
-            throw new RSRuntimeException("Number of diagonals must be positive");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtbsv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, aID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTBSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d9/d5f/ctbsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTBSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBSV is the same as TRMV + K >= 0
-        validateTRMV(Element.F32_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-        if (K < 0) {
-            throw new RSRuntimeException("Number of diagonals must be positive");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctbsv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTBSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/d5a/ztbsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param K The number of off-diagonals of the matrix A
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTBSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  int K, Allocation A,  Allocation X,  int incX) {
-        // TBSV is the same as TRMV + K >= 0
-        validateTRMV(Element.F64_2(mRS), Uplo, TransA, Diag, A, X, incX);
-        int N = A.getType().getY();
-        if (K < 0) {
-            throw new RSRuntimeException("Number of diagonals must be positive");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztbsv, TransA, 0, 0, Uplo, Diag, 0, N, K, 0, 0, aID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * STPSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d7c/stpsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void STPSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        // TPSV is same as TPMV
-        int N = validateTPMV(Element.F32(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_stpsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, apID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTPSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d9/d84/dtpsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void DTPSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        // TPSV is same as TPMV
-        int N = validateTPMV(Element.F64(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtpsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, apID, xID, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTPSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/d56/ctpsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void CTPSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        // TPSV is same as TPMV
-        int N = validateTPMV(Element.F32_2(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctpsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, apID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTPSV solves one of the systems of equations
-     * A*x = b   or   A**T*x = b   or   A**H*x = b
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/da/d57/ztpsv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the matrix is an upper or lower triangular matrix.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param Ap The input allocation contains packed matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     */
-    public void ZTPSV(@Uplo int Uplo, @Transpose int TransA, @Diag int Diag,  Allocation Ap,  Allocation X,  int incX) {
-        // TPSV is same as TPMV
-        int N = validateTPMV(Element.F64_2(mRS), Uplo, TransA, Diag, Ap, X, incX);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztpsv, TransA, 0, 0, Uplo, Diag, 0, N, 0, 0, 0, apID, xID, 0, 0, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * Level 2, S and D only
-     */
-    static int validateSYMV(Element e, @Uplo int Uplo, Allocation A, Allocation X, Allocation Y, int incX, int incY) {
-        validateUplo(Uplo);
-        int N = A.getType().getY();
-        if (A.getType().getX() != N) {
-            throw new RSRuntimeException("A must be a square matrix for SYMV");
-        }
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e) ) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SYMV");
-        }
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SYMV");
-        }
-        return N;
-    }
-    static int validateSPMV(Element e, @Uplo int Uplo, Allocation Ap, Allocation X, int incX, Allocation Y, int incY) {
-        validateUplo(Uplo);
-        if (!Ap.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (Ap.getType().getY() > 1) {
-            throw new RSRuntimeException("Ap must have a Y dimension of 0 or 1");
-        }
-
-        int N = (int)Math.sqrt((double)Ap.getType().getX() * 2);
-        if (Ap.getType().getX() != ((N * (N+1)) / 2)) {
-            throw new RSRuntimeException("Invalid dimension for Ap");
-        }
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SPMV");
-        }
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SPMV");
-        }
-
-        return N;
-    }
-    static void validateGER(Element e, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e) ) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        if (N < 1 || M < 1) {
-            throw new RSRuntimeException("M and N must be 1 or greater for GER");
-        }
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (M - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for GER");
-        }
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for GER");
-        }
-
-
-    }
-    static int validateSYR(Element e, @Uplo int Uplo, Allocation X, int incX, Allocation A) {
-        validateUplo(Uplo);
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-
-        int N = A.getType().getX();
-
-        if (X.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-        if (N != A.getType().getY()) {
-            throw new RSRuntimeException("A must be a symmetric matrix");
-        }
-        if (incX <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SYR");
-        }
-        return N;
-    }
-    static int validateSPR(Element e, @Uplo int Uplo, Allocation X, int incX, Allocation Ap) {
-        validateUplo(Uplo);
-        if (!Ap.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (Ap.getType().getY() > 1) {
-            throw new RSRuntimeException("Ap must have a Y dimension of 0 or 1");
-        }
-
-        int N = (int)Math.sqrt((double)Ap.getType().getX() * 2);
-        if (Ap.getType().getX() != ((N * (N+1)) / 2)) {
-            throw new RSRuntimeException("Invalid dimension for Ap");
-        }
-        if (incX <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SPR");
-        }
-
-        return N;
-    }
-
-    static int validateSYR2(Element e, @Uplo int Uplo, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        validateUplo(Uplo);
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        int N = A.getType().getX();
-
-        if (N != A.getType().getY()) {
-            throw new RSRuntimeException("A must be a symmetric matrix");
-        }
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (X.getType().getX() != expectedXDim || Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SYR");
-        }
-        return N;
-
-    }
-    static int validateSPR2(Element e, @Uplo int Uplo, Allocation X, int incX, Allocation Y, int incY, Allocation Ap) {
-        validateUplo(Uplo);
-        if (!Ap.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        if (Ap.getType().getY() > 1) {
-            throw new RSRuntimeException("Ap must have a Y dimension of 0 or 1");
-        }
-
-        int N = (int)Math.sqrt((double)Ap.getType().getX() * 2);
-        if (Ap.getType().getX() != ((N * (N+1)) / 2)) {
-            throw new RSRuntimeException("Invalid dimension for Ap");
-        }
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (N - 1) * incX;
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (X.getType().getX() != expectedXDim || Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for SPR2");
-        }
-
-        return N;
-    }
-
-    /**
-     * SSYMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d94/ssymv_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void SSYMV(@Uplo int Uplo, float alpha, Allocation A, Allocation X, int incX, float beta, Allocation Y, int incY) {
-        int N = validateSYMV(Element.F32(mRS), Uplo, A, X, Y, incX, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssymv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSBMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/da1/ssbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the band matrix A is being supplied.
-     * @param K The number of off-diagonals of the matrix A
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void SSBMV(@Uplo int Uplo, int K, float alpha, Allocation A, Allocation X, int incX, float beta, Allocation Y, int incY) {
-        // SBMV is the same as SYMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        int N = validateSYMV(Element.F32(mRS), Uplo, A, X, Y, incX, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssbmv, 0, 0, 0, Uplo, 0, 0, N, K, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSPMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/d68/sspmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the matrix A is supplied in packed form.
-     * @param alpha The scalar alpha.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void SSPMV(@Uplo int Uplo, float alpha, Allocation Ap, Allocation X, int incX, float beta, Allocation Y, int incY) {
-        int N = validateSPMV(Element.F32(mRS), Uplo, Ap, X, incX, Y, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sspmv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, apID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SGER performs the rank 1 operation
-     * A := alpha*x*y**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d5c/sger_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     */
-    public void SGER(float alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-        validateGER(Element.F32(mRS), X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sger, 0, 0, 0, 0, 0, M, N, 0, alpha, xID, yID, 0.f, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSYR performs the rank 1 operation
-     * A := alpha*x*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/dac/ssyr_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     */
-    public void SSYR(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation A) {
-        int N = validateSYR(Element.F32(mRS), Uplo, X, incX, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssyr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, aID, 0.f, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSPR performs the rank 1 operation
-     * A := alpha*x*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d9b/sspr_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     */
-    public void SSPR(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation Ap) {
-        int N = validateSPR(Element.F32(mRS), Uplo, X, incX, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sspr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, apID, 0.f, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSYR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**T + alpha*y*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d99/ssyr2_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     */
-    public void SSYR2(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        int N = validateSYR2(Element.F32(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssyr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, yID, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSPR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**T + alpha*y*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d3e/sspr2_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     */
-    public void SSPR2(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation Y, int incY, Allocation Ap) {
-        int N = validateSPR2(Element.F32(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sspr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, yID, 0, apID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/dbe/dsymv_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void DSYMV(@Uplo int Uplo, double alpha, Allocation A, Allocation X, int incX, double beta, Allocation Y, int incY) {
-        int N = validateSYMV(Element.F64(mRS), Uplo, A, X, Y, incX, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsymv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSBMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/d1e/dsbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the band matrix A is being supplied.
-     * @param K The number of off-diagonals of the matrix A
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void DSBMV(@Uplo int Uplo, int K, double alpha, Allocation A, Allocation X, int incX, double beta, Allocation Y, int incY) {
-        // SBMV is the same as SYMV + K >= 0
-        if (K < 0) {
-            throw new RSRuntimeException("K must be greater than or equal to 0");
-        }
-        int N = validateSYMV(Element.F64(mRS), Uplo, A, X, Y, incX, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsbmv, 0, 0, 0, Uplo, 0, 0, N, K, alpha, aID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSPMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/d85/dspmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the matrix A is supplied in packed form.
-     * @param alpha The scalar alpha.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void DSPMV(@Uplo int Uplo, double alpha, Allocation Ap, Allocation X, int incX, double beta, Allocation Y, int incY) {
-        int N = validateSPMV(Element.F64(mRS), Uplo, Ap, X, incX, Y, incY);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dspmv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, apID, xID, beta, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DGER performs the rank 1 operation
-     * A := alpha*x*y**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dc/da8/dger_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     */
-    public void DGER(double alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-        validateGER(Element.F64(mRS), X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dger, 0, 0, 0, 0, 0, M, N, 0, alpha, xID, yID, 0.f, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYR performs the rank 1 operation
-     * A := alpha*x*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d60/dsyr_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     */
-    public void DSYR(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation A) {
-        int N = validateSYR(Element.F64(mRS), Uplo, X, incX, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, aID, 0.f, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSPR performs the rank 1 operation
-     * A := alpha*x*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dd/dba/dspr_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     */
-    public void DSPR(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation Ap) {
-        int N = validateSPR(Element.F64(mRS), Uplo, X, incX, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dspr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, apID, 0.f, 0, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**T + alpha*y*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d41/dsyr2_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     */
-    public void DSYR2(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        int N = validateSYR2(Element.F64(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, yID, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSPR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**T + alpha*y*x**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dd/d9e/dspr2_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     */
-    public void DSPR2(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation Y, int incY, Allocation Ap) {
-        int N = validateSPR2(Element.F64(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dspr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, xID, yID, 0, apID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-
-    /**
-     * Level 2, C and Z only
-     */
-
-    static void validateGERU(Element e, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        if (!A.getType().getElement().isCompatible(e) ||
-            !X.getType().getElement().isCompatible(e) ||
-            !Y.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (X.getType().getY() > 1 || Y.getType().getY() > 1) {
-            throw new RSRuntimeException("BLAS vectors must have Y dimension of 0 or 1");
-        }
-
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-        if (incX <= 0 || incY <= 0) {
-            throw new RSRuntimeException("Vector increments must be greater than 0");
-        }
-        int expectedXDim = 1 + (M - 1) * incX;
-        if (X.getType().getX() != expectedXDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for GERU");
-        }
-        int expectedYDim = 1 + (N - 1) * incY;
-        if (Y.getType().getX() != expectedYDim) {
-            throw new RSRuntimeException("Incorrect vector dimensions for GERU");
-        }
-
-    }
-
-    /**
-     * CHEMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/d51/chemv_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void CHEMV(@Uplo int Uplo, Float2 alpha, Allocation A, Allocation X, int incX, Float2 beta, Allocation Y, int incY) {
-        // HEMV is the same as SYR2 validation-wise
-        int N = validateSYR2(Element.F32_2(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chemv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHBMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/dc2/chbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the band matrix A is being supplied.
-     * @param K The number of off-diagonals of the matrix A
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void CHBMV(@Uplo int Uplo, int K, Float2 alpha, Allocation A, Allocation X, int incX, Float2 beta, Allocation Y, int incY) {
-        // HBMV is the same as SYR2 validation-wise
-        int N = validateSYR2(Element.F32_2(mRS), Uplo, X, incX, Y, incY, A);
-        if (K < 0) {
-            throw new RSRuntimeException("K must be 0 or greater for HBMV");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chbmv, 0, 0, 0, Uplo, 0, 0, N, K, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHPMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d06/chpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the matrix A is supplied in packed form.
-     * @param alpha The scalar alpha.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void CHPMV(@Uplo int Uplo, Float2 alpha, Allocation Ap, Allocation X, int incX, Float2 beta, Allocation Y, int incY) {
-        // HPMV is the same as SPR2
-        int N = validateSPR2(Element.F32_2(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chpmv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, apID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CGERU performs the rank 1 operation
-     * A := alpha*x*y**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d5f/cgeru_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CGERU(Float2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        validateGERU(Element.F32_2(mRS), X, incX, Y, incY, A);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cgeru, 0, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CGERC performs the rank 1 operation
-     * A := alpha*x*y**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dd/d84/cgerc_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CGERC(Float2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        // same as GERU
-        validateGERU(Element.F32_2(mRS), X, incX, Y, incY, A);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cgerc, 0, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHER performs the rank 1 operation
-     * A := alpha*x*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d6d/cher_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CHER(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation A) {
-        // same as SYR
-        int N = validateSYR(Element.F32_2(mRS), Uplo, X, incX, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cher, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, 0, xID, 0, 0, 0, aID, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHPR performs the rank 1 operation
-     * A := alpha*x*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/dcd/chpr_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CHPR(@Uplo int Uplo, float alpha, Allocation X, int incX, Allocation Ap) {
-        // equivalent to SPR for validation
-        int N = validateSPR(Element.F32_2(mRS), Uplo, X, incX, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chpr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, 0, xID, 0, 0, 0, apID, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHER2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**H + alpha*y*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d87/cher2_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CHER2(@Uplo int Uplo, Float2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        // same as SYR2
-        int N = validateSYR2(Element.F32_2(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cher2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CHPR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**H + alpha*y*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d44/chpr2_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F32_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F32_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     */
-    public void CHPR2(@Uplo int Uplo, Float2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation Ap) {
-        // same as SPR2
-        int N = validateSPR2(Element.F32_2(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chpr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, apID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHEMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/ddd/zhemv_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void ZHEMV(@Uplo int Uplo, Double2 alpha, Allocation A, Allocation X, int incX, Double2 beta, Allocation Y, int incY) {
-        // HEMV is the same as SYR2 validation-wise
-        int N = validateSYR2(Element.F64_2(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhemv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHBMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d1a/zhbmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should also be of size N*N (dimY = N, dimX = N),
-     *       but only the region N*(K+1) will be referenced. The following subroutine can is an
-     *       example showing how to convert a UPPER trianglar matrix 'a' to row-based band matrix 'b'.
-     *           for i in range(0, n):
-     *              for j in range(i, min(i+k+1, n)):
-     *                  b[i, j-i] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the band matrix A is being supplied.
-     * @param K The number of off-diagonals of the matrix A
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void ZHBMV(@Uplo int Uplo, int K, Double2 alpha, Allocation A, Allocation X, int incX, Double2 beta, Allocation Y, int incY) {
-        // HBMV is the same as SYR2 validation-wise
-        int N = validateSYR2(Element.F64_2(mRS), Uplo, X, incX, Y, incY, A);
-        if (K < 0) {
-            throw new RSRuntimeException("K must be 0 or greater for HBMV");
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhbmv, 0, 0, 0, Uplo, 0, 0, N, K, alpha.x, alpha.y, aID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHPMV performs the matrix-vector operation
-     * y := alpha*A*x + beta*y
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d60/zhpmv_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of the matrix A is supplied in packed form.
-     * @param alpha The scalar alpha.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param beta The scalar beta.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     */
-    public void ZHPMV(@Uplo int Uplo, Double2 alpha, Allocation Ap, Allocation X, int incX, Double2 beta, Allocation Y, int incY) {
-        // HPMV is the same as SPR2
-        int N = validateSPR2(Element.F64_2(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhpmv, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, apID, xID, beta.x, beta.y, yID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZGERU performs the rank 1 operation
-     * A := alpha*x*y**T + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/d12/zgeru_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZGERU(Double2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        validateGERU(Element.F64_2(mRS), X, incX, Y, incY, A);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zgeru, 0, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZGERC performs the rank 1 operation
-     * A := alpha*x*y**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/dad/zgerc_8f.html
-     *
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZGERC(Double2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        // same as GERU
-        validateGERU(Element.F64_2(mRS), X, incX, Y, incY, A);
-        int M = A.getType().getY();
-        int N = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zgerc, 0, 0, 0, 0, 0, M, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHER performs the rank 1 operation
-     * A := alpha*x*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d0e/zher_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHER(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation A) {
-        // same as SYR
-        int N = validateSYR(Element.F64_2(mRS), Uplo, X, incX, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zher, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, 0, xID, 0, 0, 0, aID, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHPR performs the rank 1 operation
-     * A := alpha*x*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/de1/zhpr_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHPR(@Uplo int Uplo, double alpha, Allocation X, int incX, Allocation Ap) {
-        // equivalent to SPR for validation
-        int N = validateSPR(Element.F64_2(mRS), Uplo, X, incX, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhpr, 0, 0, 0, Uplo, 0, 0, N, 0, alpha, 0, xID, 0, 0, 0, apID, incX, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHER2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**H + alpha*y*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/da/d8a/zher2_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHER2(@Uplo int Uplo, Double2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation A) {
-        // same as SYR2
-        int N = validateSYR2(Element.F64_2(mRS), Uplo, X, incX, Y, incY, A);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zher2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, aID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHPR2 performs the symmetric rank 2 operation
-     * A := alpha*x*y**H + alpha*y*x**H + A
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d5/d52/zhpr2_8f.html
-     *
-     * Note: For a N*N matrix, the input Allocation should be a 1D allocation of size dimX = N*(N+1)/2,
-     *       The following subroutine can is an example showing how to convert a UPPER trianglar matrix
-     *       'a' to packed matrix 'b'.
-     *           k = 0
-     *           for i in range(0, n):
-     *              for j in range(i, n):
-     *                  b[k++] = a[i, j]
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part is to be supplied in the packed form.
-     * @param alpha The scalar alpha.
-     * @param X The input allocation contains vector x, supported elements type {@link Element#F64_2}.
-     * @param incX The increment for the elements of vector x, must be larger than zero.
-     * @param Y The input allocation contains vector y, supported elements type {@link Element#F64_2}.
-     * @param incY The increment for the elements of vector y, must be larger than zero.
-     * @param Ap The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHPR2(@Uplo int Uplo, Double2 alpha, Allocation X, int incX, Allocation Y, int incY, Allocation Ap) {
-        // same as SPR2
-        int N = validateSPR2(Element.F64_2(mRS), Uplo, X, incX, Y, incY, Ap);
-
-        boolean mUseIncSupp = isIncSupp();
-        long apID = Ap.getID(mRS);
-        long xID = X.getID(mRS);
-        long yID = Y.getID(mRS);
-        if (mUseIncSupp) {
-            apID = getDummyAlloc(Ap);
-            xID = getDummyAlloc(X);
-            yID = getDummyAlloc(Y);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhpr2, 0, 0, 0, Uplo, 0, 0, N, 0, alpha.x, alpha.y, xID, yID, 0, 0, apID, incX, incY, 0, 0, mUseIncSupp);
-    }
-
-
-    /**
-     * Level 3 BLAS
-     */
-
-    static void validateL3(Element e, int TransA, int TransB, int Side, Allocation A, Allocation B, Allocation C) {
-        int aM = -1, aN = -1, bM = -1, bN = -1, cM = -1, cN = -1;
-        if ((A != null && !A.getType().getElement().isCompatible(e)) ||
-            (B != null && !B.getType().getElement().isCompatible(e)) ||
-            (C != null && !C.getType().getElement().isCompatible(e))) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        if (C == null) {
-            //since matrix C is used to store the result, it cannot be null.
-            throw new RSRuntimeException("Allocation C cannot be null");
-        }
-        cM = C.getType().getY();
-        cN = C.getType().getX();
-
-        if (Side == RIGHT) {
-            if ((A == null && B != null) || (A != null && B == null)) {
-                throw new RSRuntimeException("Provided Matrix A without Matrix B, or vice versa");
-            }
-            if (B != null) {
-                bM = A.getType().getY();
-                bN = A.getType().getX();
-            }
-            if (A != null) {
-                aM = B.getType().getY();
-                aN = B.getType().getX();
-            }
-        } else {
-            if (A != null) {
-                if (TransA == TRANSPOSE || TransA == CONJ_TRANSPOSE) {
-                    aN = A.getType().getY();
-                    aM = A.getType().getX();
-                } else {
-                    aM = A.getType().getY();
-                    aN = A.getType().getX();
-                }
-            }
-            if (B != null) {
-                if (TransB == TRANSPOSE || TransB == CONJ_TRANSPOSE) {
-                    bN = B.getType().getY();
-                    bM = B.getType().getX();
-                } else {
-                    bM = B.getType().getY();
-                    bN = B.getType().getX();
-                }
-            }
-        }
-        if (A != null && B != null && C != null) {
-            if (aN != bM || aM != cM || bN != cN) {
-                throw new RSRuntimeException("Called BLAS with invalid dimensions");
-            }
-        } else if (A != null && C != null) {
-            // A and C only, for SYRK
-            if (cM != cN) {
-                throw new RSRuntimeException("Matrix C is not symmetric");
-            }
-            if (aM != cM) {
-                throw new RSRuntimeException("Called BLAS with invalid dimensions");
-            }
-        } else if (A != null && B != null) {
-            // A and B only
-            if (aN != bM) {
-                throw new RSRuntimeException("Called BLAS with invalid dimensions");
-            }
-        }
-
-    }
-
-    /**
-     * SGEMM performs one of the matrix-matrix operations
-     * C := alpha*op(A)*op(B) + beta*C   where op(X) is one of op(X) = X  or  op(X) = X**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/de2/sgemm_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param TransB The type of transpose applied to matrix B.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32}.
-     */
-    public void SGEMM(@Transpose int TransA, @Transpose int TransB, float alpha, Allocation A,
-                      Allocation B, float beta, Allocation C) {
-        validateTranspose(TransA);
-        validateTranspose(TransB);
-        validateL3(Element.F32(mRS), TransA, TransB, 0, A, B, C);
-
-        int M = -1, N = -1, K = -1;
-        if (TransA != NO_TRANSPOSE) {
-            M = A.getType().getX();
-            K = A.getType().getY();
-        } else {
-            M = A.getType().getY();
-            K = A.getType().getX();
-        }
-        if (TransB != NO_TRANSPOSE) {
-            N = B.getType().getY();
-        } else {
-            N = B.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_sgemm, TransA, TransB, 0, 0, 0, M, N, K,  alpha, aID, bID,
-                                        beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DGEMM performs one of the matrix-matrix operations
-     * C := alpha*op(A)*op(B) + beta*C   where op(X) is one of op(X) = X  or  op(X) = X**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/d2b/dgemm_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param TransB The type of transpose applied to matrix B.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64}.
-     */
-    public void DGEMM(@Transpose int TransA, @Transpose int TransB, double alpha, Allocation A,
-                      Allocation B, double beta, Allocation C) {
-        validateTranspose(TransA);
-        validateTranspose(TransB);
-        validateL3(Element.F64(mRS), TransA, TransB, 0, A, B, C);
-        int M = -1, N = -1, K = -1;
-        if (TransA != NO_TRANSPOSE) {
-            M = A.getType().getX();
-            K = A.getType().getY();
-        } else {
-            M = A.getType().getY();
-            K = A.getType().getX();
-        }
-        if (TransB != NO_TRANSPOSE) {
-            N = B.getType().getY();
-        } else {
-            N = B.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dgemm, TransA, TransB, 0, 0, 0, M, N, K,  alpha, aID, bID,
-                                        beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CGEMM performs one of the matrix-matrix operations
-     * C := alpha*op(A)*op(B) + beta*C   where op(X) is one of op(X) = X  or  op(X) = X**T  or  op(X) = X**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d5b/cgemm_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param TransB The type of transpose applied to matrix B.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CGEMM(@Transpose int TransA, @Transpose int TransB, Float2 alpha, Allocation A,
-                      Allocation B, Float2 beta, Allocation C) {
-        validateTranspose(TransA);
-        validateTranspose(TransB);
-        validateL3(Element.F32_2(mRS), TransA, TransB, 0, A, B, C);
-        int M = -1, N = -1, K = -1;
-        if (TransA != NO_TRANSPOSE) {
-            M = A.getType().getX();
-            K = A.getType().getY();
-        } else {
-            M = A.getType().getY();
-            K = A.getType().getX();
-        }
-        if (TransB != NO_TRANSPOSE) {
-            N = B.getType().getY();
-        } else {
-            N = B.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cgemm, TransA, TransB, 0, 0, 0, M, N, K,  alpha.x, alpha.y, aID, bID,
-                                         beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZGEMM performs one of the matrix-matrix operations
-     * C := alpha*op(A)*op(B) + beta*C   where op(X) is one of op(X) = X  or  op(X) = X**T  or  op(X) = X**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/d76/zgemm_8f.html
-     *
-     * @param TransA The type of transpose applied to matrix A.
-     * @param TransB The type of transpose applied to matrix B.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2
-     */
-    public void ZGEMM(@Transpose int TransA, @Transpose int TransB, Double2 alpha, Allocation A,
-                      Allocation B, Double2 beta, Allocation C) {
-        validateTranspose(TransA);
-        validateTranspose(TransB);
-        validateL3(Element.F64_2(mRS), TransA, TransB, 0, A, B, C);
-        int M = -1, N = -1, K = -1;
-        if (TransA != NO_TRANSPOSE) {
-            M = A.getType().getX();
-            K = A.getType().getY();
-        } else {
-            M = A.getType().getY();
-            K = A.getType().getX();
-        }
-        if (TransB != NO_TRANSPOSE) {
-            N = B.getType().getY();
-        } else {
-            N = B.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zgemm, TransA, TransB, 0, 0, 0, M, N, K,  alpha.x, alpha.y, aID, bID,
-                                   beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSYMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/d42/ssymm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32}.
-     */
-    public void SSYMM(@Side int Side, @Uplo int Uplo, float alpha, Allocation A,
-                      Allocation B, float beta, Allocation C) {
-        validateSide(Side);
-        validateUplo(Uplo);
-        //For SYMM, Matrix A should be symmetric
-        if (A.getType().getX() != A.getType().getY()) {
-            throw new RSRuntimeException("Matrix A is not symmetric");
-        }
-        validateL3(Element.F32(mRS), 0, 0, Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssymm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0, alpha, aID, bID,
-                                        beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/db0/dsymm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64}.
-     */
-    public void DSYMM(@Side int Side, @Uplo int Uplo, double alpha, Allocation A,
-                      Allocation B, double beta, Allocation C) {
-        validateSide(Side);
-        validateUplo(Uplo);
-        if (A.getType().getX() != A.getType().getY()) {
-            throw new RSRuntimeException("Matrix A is not symmetric");
-        }
-        validateL3(Element.F64(mRS), 0, 0, Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsymm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0, alpha, aID, bID,
-                                        beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CSYMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/db/d59/csymm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CSYMM(@Side int Side, @Uplo int Uplo, Float2 alpha, Allocation A,
-                      Allocation B, Float2 beta, Allocation C) {
-        validateSide(Side);
-        validateUplo(Uplo);
-        if (A.getType().getX() != A.getType().getY()) {
-            throw new RSRuntimeException("Matrix A is not symmetric");
-        }
-        validateL3(Element.F32_2(mRS), 0, 0, Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_csymm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0, alpha.x, alpha.y, aID, bID,
-                                         beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZSYMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d51/zsymm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZSYMM(@Side int Side, @Uplo int Uplo, Double2 alpha, Allocation A,
-                      Allocation B, Double2 beta, Allocation C) {
-        validateSide(Side);
-        validateUplo(Uplo);
-        if (A.getType().getX() != A.getType().getY()) {
-            throw new RSRuntimeException("Matrix A is not symmetric");
-        }
-        validateL3(Element.F64_2(mRS), 0, 0, Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zsymm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0, alpha.x, alpha.y, aID, bID,
-                                   beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * SSYRK performs one of the symmetric rank k operations
-     * C := alpha*A*A**T + beta*C   or   C := alpha*A**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d0/d40/ssyrk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32}.
-     */
-    public void SSYRK(@Uplo int Uplo, @Transpose int Trans, float alpha, Allocation A, float beta, Allocation C) {
-        validateTranspose(Trans);
-        validateUplo(Uplo);
-        validateL3(Element.F32(mRS), Trans, 0, 0, A, null, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssyrk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, aID, 0, beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYRK performs one of the symmetric rank k operations
-     * C := alpha*A*A**T + beta*C   or   C := alpha*A**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dc/d05/dsyrk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64}.
-     */
-    public void DSYRK(@Uplo int Uplo, @Transpose int Trans, double alpha, Allocation A, double beta, Allocation C) {
-        validateTranspose(Trans);
-        validateUplo(Uplo);
-        validateL3(Element.F64(mRS), Trans, 0, 0, A, null, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyrk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, aID, 0, beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CSYRK performs one of the symmetric rank k operations
-     * C := alpha*A*A**T + beta*C   or   C := alpha*A**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d6a/csyrk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CSYRK(@Uplo int Uplo, @Transpose int Trans, Float2 alpha, Allocation A, Float2 beta, Allocation C) {
-        validateTranspose(Trans);
-        validateUplo(Uplo);
-        validateL3(Element.F32_2(mRS), Trans, 0, 0, A, null, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_csyrk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, aID, 0, beta.x, beta.y,
-                                         C.getID(mRS), 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZSYRK performs one of the symmetric rank k operations
-     * C := alpha*A*A**T + beta*C   or   C := alpha*A**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d54/zsyrk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZSYRK(@Uplo int Uplo, @Transpose int Trans, Double2 alpha, Allocation A, Double2 beta, Allocation C) {
-        validateTranspose(Trans);
-        validateUplo(Uplo);
-        validateL3(Element.F64_2(mRS), Trans, 0, 0, A, null, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zsyrk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, aID, 0, beta.x, beta.y,
-                                   C.getID(mRS), 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateSYR2K(Element e, @Transpose int Trans, Allocation A, Allocation B, Allocation C) {
-        validateTranspose(Trans);
-        if (!A.getType().getElement().isCompatible(e) ||
-            !B.getType().getElement().isCompatible(e) ||
-            !C.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        int Cdim = -1;
-        // A is n x k if no transpose, k x n if transpose
-        // C is n x n
-        if (Trans == TRANSPOSE) {
-            // check columns versus C
-            Cdim = A.getType().getX();
-        } else {
-            // check rows versus C
-            Cdim = A.getType().getY();
-        }
-        if (C.getType().getX() != Cdim || C.getType().getY() != Cdim) {
-            throw new RSRuntimeException("Invalid symmetric matrix in SYR2K");
-        }
-        // A dims == B dims
-        if (A.getType().getX() != B.getType().getX() || A.getType().getY() != B.getType().getY()) {
-            throw new RSRuntimeException("Invalid A and B in SYR2K");
-        }
-    }
-
-    /**
-     * SSYR2K performs one of the symmetric rank 2k operations
-     * C := alpha*A*B**T + alpha*B*A**T + beta*C   or   C := alpha*A**T*B + alpha*B**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d3d/ssyr2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32}.
-     */
-    public void SSYR2K(@Uplo int Uplo, @Transpose int Trans, float alpha, Allocation A, Allocation B, float beta, Allocation C) {
-        validateUplo(Uplo);
-        validateSYR2K(Element.F32(mRS), Trans, A, B, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, aID, bID, beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DSYR2K performs one of the symmetric rank 2k operations
-     * C := alpha*A*B**T + alpha*B*A**T + beta*C   or   C := alpha*A**T*B + alpha*B**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d1/dec/dsyr2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64}.
-     */
-    public void DSYR2K(@Uplo int Uplo, @Transpose int Trans, double alpha, Allocation A, Allocation B, double beta, Allocation C) {
-        validateUplo(Uplo);
-        validateSYR2K(Element.F64(mRS), Trans, A, B, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, aID, bID, beta, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CSYR2K performs one of the symmetric rank 2k operations
-     * C := alpha*A*B**T + alpha*B*A**T + beta*C   or   C := alpha*A**T*B + alpha*B**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d7e/csyr2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CSYR2K(@Uplo int Uplo, @Transpose int Trans, Float2 alpha, Allocation A, Allocation B, Float2 beta, Allocation C) {
-        validateUplo(Uplo);
-        validateSYR2K(Element.F32_2(mRS), Trans, A, B, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_csyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, aID, bID, beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZSYR2K performs one of the symmetric rank 2k operations
-     * C := alpha*A*B**T + alpha*B*A**T + beta*C   or   C := alpha*A**T*B + alpha*B**T*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d20/zsyr2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZSYR2K(@Uplo int Uplo, @Transpose int Trans, Double2 alpha, Allocation A, Allocation B, Double2 beta, Allocation C) {
-        validateUplo(Uplo);
-        validateSYR2K(Element.F64_2(mRS), Trans, A, B, C);
-        int K = -1;
-        if (Trans != NO_TRANSPOSE) {
-            K = A.getType().getY();
-        } else {
-            K = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, aID, bID, beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateTRMM(Element e, @Side int Side, @Transpose int TransA, Allocation A, Allocation B) {
-        validateSide(Side);
-        validateTranspose(TransA);
-        int aM = -1, aN = -1, bM = -1, bN = -1;
-        if (!A.getType().getElement().isCompatible(e) ||
-            !B.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-
-        aM = A.getType().getY();
-        aN = A.getType().getX();
-        if (aM != aN) {
-            throw new RSRuntimeException("Called TRMM with a non-symmetric matrix A");
-        }
-
-        bM = B.getType().getY();
-        bN = B.getType().getX();
-        if (Side == LEFT) {
-            if (aN != bM) {
-                throw new RSRuntimeException("Called TRMM with invalid matrices");
-            }
-        } else {
-            if (bN != aM) {
-                throw new RSRuntimeException("Called TRMM with invalid matrices");
-            }
-        }
-    }
-
-    /**
-     * STRMM performs one of the matrix-matrix operations
-     * B := alpha*op(A)*B   or   B := alpha*B*op(A)
-     * op(A) is one of  op(A) = A  or  op(A) = A**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/df/d01/strmm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32}.
-     */
-    public void STRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, float alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRMM(Element.F32(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                        alpha, aID, bID, 0.f, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTRMM performs one of the matrix-matrix operations
-     * B := alpha*op(A)*B   or   B := alpha*B*op(A)
-     * op(A) is one of  op(A) = A  or  op(A) = A**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/dd/d19/dtrmm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64}.
-     */
-    public void DTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, double alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRMM(Element.F64(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                        alpha, aID, bID, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTRMM performs one of the matrix-matrix operations
-     * B := alpha*op(A)*B   or   B := alpha*B*op(A)
-     * op(A) is one of  op(A) = A  or  op(A) = A**T  or  op(A) = A**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d4/d9b/ctrmm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     */
-    public void CTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRMM(Element.F32_2(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                         alpha.x, alpha.y, aID, bID, 0, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTRMM performs one of the matrix-matrix operations
-     * B := alpha*op(A)*B   or   B := alpha*B*op(A)
-     * op(A) is one of  op(A) = A  or  op(A) = A**T  or  op(A) = A**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/de1/ztrmm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     */
-    public void ZTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRMM(Element.F64_2(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                   alpha.x, alpha.y, aID, bID, 0, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateTRSM(Element e, @Side int Side, @Transpose int TransA, Allocation A, Allocation B) {
-        int adim = -1, bM = -1, bN = -1;
-        validateSide(Side);
-        validateTranspose(TransA);
-        if (!A.getType().getElement().isCompatible(e) ||
-            !B.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        adim = A.getType().getX();
-        if (adim != A.getType().getY()) {
-            // this may be unnecessary, the restriction could potentially be relaxed
-            // A needs to contain at least that symmetric matrix but could theoretically be larger
-            // for now we assume adapters are sufficient, will reevaluate in the future
-            throw new RSRuntimeException("Called TRSM with a non-symmetric matrix A");
-        }
-        bM = B.getType().getY();
-        bN = B.getType().getX();
-        if (Side == LEFT) {
-            // A is M*M
-            if (adim != bM) {
-                throw new RSRuntimeException("Called TRSM with invalid matrix dimensions");
-            }
-        } else {
-            // A is N*N
-            if (adim != bN) {
-                throw new RSRuntimeException("Called TRSM with invalid matrix dimensions");
-            }
-        }
-    }
-
-    /**
-     * STRSM solves one of the matrix equations
-     * op(A)*X := alpha*B   or   X*op(A) := alpha*B
-     * op(A) is one of  op(A) = A  or  op(A) = A**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d2/d8b/strsm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32}.
-     */
-    public void STRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, float alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRSM(Element.F32(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Single(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                        alpha, aID, bID, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * DTRSM solves one of the matrix equations
-     * op(A)*X := alpha*B   or   X*op(A) := alpha*B
-     * op(A) is one of  op(A) = A  or  op(A) = A**T
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/da7/dtrsm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64}.
-     */
-    public void DTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, double alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRSM(Element.F64(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                        alpha, aID, bID, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * CTRSM solves one of the matrix equations
-     * op(A)*X := alpha*B   or   X*op(A) := alpha*B
-     * op(A) is one of  op(A) = A  or  op(A) = A**T  or  op(A) = A**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/de/d30/ctrsm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     */
-    public void CTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRSM(Element.F32_2(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                         alpha.x, alpha.y, aID, bID, 0, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZTRSM solves one of the matrix equations
-     * op(A)*X := alpha*B   or   X*op(A) := alpha*B
-     * op(A) is one of  op(A) = A  or  op(A) = A**T  or  op(A) = A**H
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d1/d39/ztrsm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether matrix A is upper or lower triangular.
-     * @param TransA The type of transpose applied to matrix A.
-     * @param Diag Specifies whether or not A is unit triangular.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     */
-    public void ZTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) {
-        validateUplo(Uplo);
-        validateDiag(Diag);
-        validateTRSM(Element.F64_2(mRS), Side, TransA, A, B);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0,
-                                   alpha.x, alpha.y, aID, bID, 0, 0, 0, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateHEMM(Element e, @Side int Side, Allocation A, Allocation B, Allocation C) {
-        validateSide(Side);
-
-        if (!A.getType().getElement().isCompatible(e) ||
-            !B.getType().getElement().isCompatible(e) ||
-            !C.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-
-        // A must be square; can potentially be relaxed similar to TRSM
-        int adim = A.getType().getX();
-        if (adim != A.getType().getY()) {
-            throw new RSRuntimeException("Called HEMM with non-square A");
-        }
-        if ((Side == LEFT && adim != B.getType().getY()) ||
-            (Side == RIGHT && adim != B.getType().getX())) {
-            throw new RSRuntimeException("Called HEMM with invalid B");
-        }
-        if (B.getType().getX() != C.getType().getX() ||
-            B.getType().getY() != C.getType().getY()) {
-            throw new RSRuntimeException("Called HEMM with mismatched B and C");
-        }
-    }
-
-    /**
-     * CHEMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d3/d66/chemm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CHEMM(@Side int Side, @Uplo int Uplo, Float2 alpha, Allocation A, Allocation B, Float2 beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHEMM(Element.F32_2(mRS), Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_chemm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0,
-                                         alpha.x, alpha.y, aID, bID, beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHEMM performs one of the matrix-matrix operations
-     * C := alpha*A*B + beta*C   or   C := alpha*B*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d6/d3e/zhemm_8f.html
-     *
-     * @param Side Specifies whether the symmetric matrix A appears on the left or right.
-     * @param Uplo Specifies whether the upper or lower triangular part is to be referenced.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHEMM(@Side int Side, @Uplo int Uplo, Double2 alpha, Allocation A, Allocation B, Double2 beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHEMM(Element.F64_2(mRS), Side, A, B, C);
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zhemm, 0, 0, Side, Uplo, 0, C.getType().getY(), C.getType().getX(), 0,
-                                   alpha.x, alpha.y, aID, bID, beta.x, beta.y, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateHERK(Element e, @Transpose int Trans, Allocation A, Allocation C) {
-        if (!A.getType().getElement().isCompatible(e) ||
-            !C.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        validateConjTranspose(Trans);
-        int cdim = C.getType().getX();
-        if (cdim != C.getType().getY()) {
-            throw new RSRuntimeException("Called HERK with non-square C");
-        }
-        if (Trans == NO_TRANSPOSE) {
-            if (cdim != A.getType().getY()) {
-                throw new RSRuntimeException("Called HERK with invalid A");
-            }
-        } else {
-            if (cdim != A.getType().getX()) {
-                throw new RSRuntimeException("Called HERK with invalid A");
-            }
-        }
-    }
-
-    /**
-     * CHERK performs one of the hermitian rank k operations
-     * C := alpha*A*A**H + beta*C   or   C := alpha*A**H*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d8/d52/cherk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CHERK(@Uplo int Uplo, @Transpose int Trans, float alpha, Allocation A, float beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHERK(Element.F32_2(mRS), Trans, A, C);
-        int k = 0;
-        if (Trans == CONJ_TRANSPOSE) {
-            k = A.getType().getY();
-        } else {
-            k = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cherk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), k,
-                                         alpha, 0, aID, 0, beta, 0, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHERK performs one of the hermitian rank k operations
-     * C := alpha*A*A**H + beta*C   or   C := alpha*A**H*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d1/db1/zherk_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHERK(@Uplo int Uplo, @Transpose int Trans, double alpha, Allocation A, double beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHERK(Element.F64_2(mRS), Trans, A, C);
-        int k = 0;
-        if (Trans == CONJ_TRANSPOSE) {
-            k = A.getType().getY();
-        } else {
-            k = A.getType().getX();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zherk, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), k,
-                                   alpha, 0, aID, 0, beta, 0, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    static void validateHER2K(Element e, @Transpose int Trans, Allocation A, Allocation B, Allocation C) {
-        if (!A.getType().getElement().isCompatible(e) ||
-            !B.getType().getElement().isCompatible(e) ||
-            !C.getType().getElement().isCompatible(e)) {
-            throw new RSRuntimeException("Called BLAS with wrong Element type");
-        }
-        validateConjTranspose(Trans);
-        int cdim = C.getType().getX();
-        if (cdim != C.getType().getY()) {
-            throw new RSRuntimeException("Called HER2K with non-square C");
-        }
-        if (Trans == NO_TRANSPOSE) {
-            if (A.getType().getY() != cdim) {
-                throw new RSRuntimeException("Called HER2K with invalid matrices");
-            }
-        } else {
-            if (A.getType().getX() != cdim) {
-                throw new RSRuntimeException("Called HER2K with invalid matrices");
-            }
-        }
-        if (A.getType().getX() != B.getType().getX() || A.getType().getY() != B.getType().getY()) {
-            throw new RSRuntimeException("Called HER2K with invalid A and B matrices");
-        }
-    }
-
-    /**
-     * CHER2K performs one of the hermitian rank 2k operations
-     * C := alpha*A*B**H + conjg( alpha )*B*A**H + beta*C   or   C := alpha*A**H*B + conjg( alpha )*B**H*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d1/d82/cher2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F32_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F32_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F32_2}.
-     */
-    public void CHER2K(@Uplo int Uplo, @Transpose int Trans, Float2 alpha, Allocation A, Allocation B, float beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHER2K(Element.F32_2(mRS), Trans, A, B, C);
-        int k = 0;
-        if (Trans == NO_TRANSPOSE) {
-            k = A.getType().getX();
-        } else {
-            k = A.getType().getY();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_cher2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), k, alpha.x, alpha.y,
-                                         A.getID(mRS), bID, beta, 0, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-    /**
-     * ZHER2K performs one of the hermitian rank 2k operations
-     * C := alpha*A*B**H + conjg( alpha )*B*A**H + beta*C   or   C := alpha*A**H*B + conjg( alpha )*B**H*A + beta*C
-     *
-     * Details: http://www.netlib.org/lapack/explore-html/d7/dfa/zher2k_8f.html
-     *
-     * @param Uplo Specifies whether the upper or lower triangular part of C is to be referenced.
-     * @param Trans The type of transpose applied to the operation.
-     * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
-     * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
-     */
-    public void ZHER2K(@Uplo int Uplo, @Transpose int Trans, Double2 alpha, Allocation A, Allocation B, double beta, Allocation C) {
-        validateUplo(Uplo);
-        validateHER2K(Element.F64_2(mRS), Trans, A, B, C);
-        int k = 0;
-        if (Trans == NO_TRANSPOSE) {
-            k = A.getType().getX();
-        } else {
-            k = A.getType().getY();
-        }
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zher2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), k, alpha.x, alpha.y,
-                                   A.getID(mRS), bID, beta, 0, cID, 0, 0, 0, 0, mUseIncSupp);
-    }
-
-
-    /**
-     * 8-bit GEMM-like operation for neural networks: C = A * Transpose(B)
-     * Calculations are done in 1.10.21 fixed-point format for the final output,
-     * just before there's a shift down to drop the fractional parts. The output
-     * values are gated to 0 to 255 to fit in a byte, but the 10-bit format
-     * gives some headroom to avoid wrapping around on small overflows.
-     *
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#U8}.
-     * @param a_offset The offset for all values in matrix A, e.g A[i,j] = A[i,j] - a_offset. Value should be from 0 to 255.
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#U8}.
-     * @param b_offset The offset for all values in matrix B, e.g B[i,j] = B[i,j] - b_offset. Value should be from 0 to 255.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#U8}.
-     * @param c_offset The offset for all values in matrix C.
-     * @param c_mult The multiplier for all values in matrix C, e.g C[i,j] = (C[i,j] + c_offset) * c_mult.
-     **/
-    public void BNNM(Allocation A, int a_offset, Allocation B, int b_offset, Allocation C, int c_offset, int c_mult) {
-        validateL3(Element.U8(mRS), NO_TRANSPOSE, TRANSPOSE, 0, A, B, C);
-
-        if (a_offset < 0 || a_offset > 255) {
-            throw new RSRuntimeException("Invalid a_offset passed to BNNM");
-        }
-        if (b_offset < 0 || b_offset > 255) {
-            throw new RSRuntimeException("Invalid b_offset passed to BNNM");
-        }
-        int M = -1, N = -1, K = -1;
-        M = A.getType().getY();
-        N = B.getType().getY();
-        K = A.getType().getX();
-
-        boolean mUseIncSupp = isIncSupp();
-        long aID = A.getID(mRS);
-        long bID = B.getID(mRS);
-        long cID = C.getID(mRS);
-        if (mUseIncSupp) {
-            aID = getDummyAlloc(A);
-            bID = getDummyAlloc(B);
-            cID = getDummyAlloc(C);
-        }
-        mRS.nScriptIntrinsicBLAS_BNNM(getID(mRS), M, N, K, aID, a_offset, bID, b_offset, cID, c_offset, c_mult, mUseIncSupp);
-
-    }
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java
deleted file mode 100644
index eef581e..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlend.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-
-/**
- * Intrinsic kernels for blending two
- * {@link android.support.v8.renderscript.Allocation} objects.
- **/
-public class ScriptIntrinsicBlend extends ScriptIntrinsic {
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    ScriptIntrinsicBlend(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8_4}
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for inputs and outputs
-     *
-     * @return ScriptIntrinsicBlend
-     */
-    public static ScriptIntrinsicBlend create(RenderScript rs, Element e) {
-        // 7 comes from RS_SCRIPT_INTRINSIC_ID_BLEND in rsDefines.h
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(7, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicBlend si = new ScriptIntrinsicBlend(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-
-    }
-
-    private void blend(int id, Allocation ain, Allocation aout) {
-        if (!ain.getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Input is not of expected format.");
-        }
-        if (!aout.getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Output is not of expected format.");
-        }
-        forEach(id, ain, aout, null);
-    }
-
-    /**
-     * Sets dst = {0, 0, 0, 0}
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachClear(Allocation ain, Allocation aout) {
-        blend(0, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Clear kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDClear() {
-        return createKernelID(0, 3, null, null);
-    }
-
-
-    /**
-     * Sets dst = src
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSrc(Allocation ain, Allocation aout) {
-        blend(1, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Src kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSrc() {
-        return createKernelID(1, 3, null, null);
-    }
-
-    /**
-     * Sets dst = dst
-     *
-     * This is a NOP.
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachDst(Allocation ain, Allocation aout) {
-        // NOP
-    }
-
-    /**
-     * Get a KernelID for the Dst kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDDst() {
-        return createKernelID(2, 3, null, null);
-    }
-
-    /**
-     * Sets dst = src + dst * (1.0 - src.a)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSrcOver(Allocation ain, Allocation aout) {
-        blend(3, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the SrcOver kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSrcOver() {
-        return createKernelID(3, 3, null, null);
-    }
-
-    /**
-     * Sets dst = dst + src * (1.0 - dst.a)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachDstOver(Allocation ain, Allocation aout) {
-        blend(4, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the DstOver kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDDstOver() {
-        return createKernelID(4, 3, null, null);
-    }
-
-    /**
-     * Sets dst = src * dst.a
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSrcIn(Allocation ain, Allocation aout) {
-        blend(5, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the SrcIn kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSrcIn() {
-        return createKernelID(5, 3, null, null);
-    }
-
-    /**
-     * Sets dst = dst * src.a
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachDstIn(Allocation ain, Allocation aout) {
-        blend(6, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the DstIn kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDDstIn() {
-        return createKernelID(6, 3, null, null);
-    }
-
-    /**
-     * Sets dst = src * (1.0 - dst.a)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSrcOut(Allocation ain, Allocation aout) {
-        blend(7, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the SrcOut kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSrcOut() {
-        return createKernelID(7, 3, null, null);
-    }
-
-    /**
-     * Sets dst = dst * (1.0 - src.a)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachDstOut(Allocation ain, Allocation aout) {
-        blend(8, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the DstOut kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDDstOut() {
-        return createKernelID(8, 3, null, null);
-    }
-
-    /**
-     * dst.rgb = src.rgb * dst.a + (1.0 - src.a) * dst.rgb
-     * dst.a = dst.a
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSrcAtop(Allocation ain, Allocation aout) {
-        blend(9, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the SrcAtop kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSrcAtop() {
-        return createKernelID(9, 3, null, null);
-    }
-
-    /**
-     * dst = dst.rgb * src.a + (1.0 - dst.a) * src.rgb
-     * dst.a = src.a
-     * Note: Before API 23, the alpha channel was not correctly set.
-     *       Please use with caution when targeting older APIs.
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachDstAtop(Allocation ain, Allocation aout) {
-        blend(10, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the DstAtop kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDDstAtop() {
-        return createKernelID(10, 3, null, null);
-    }
-
-    /**
-     * Sets dst = {src.r ^ dst.r, src.g ^ dst.g, src.b ^ dst.b, src.a ^ dst.a}
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachXor(Allocation ain, Allocation aout) {
-        blend(11, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Xor kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDXor() {
-        return createKernelID(11, 3, null, null);
-    }
-
-    ////////
-/*
-    public void forEachNormal(Allocation ain, Allocation aout) {
-        blend(12, ain, aout);
-    }
-
-    public void forEachAverage(Allocation ain, Allocation aout) {
-        blend(13, ain, aout);
-    }
-*/
-    /**
-     * Sets dst = src * dst
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachMultiply(Allocation ain, Allocation aout) {
-        blend(14, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Multiply kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDMultiply() {
-        return createKernelID(14, 3, null, null);
-    }
-
-/*
-    public void forEachScreen(Allocation ain, Allocation aout) {
-        blend(15, ain, aout);
-    }
-
-    public void forEachDarken(Allocation ain, Allocation aout) {
-        blend(16, ain, aout);
-    }
-
-    public void forEachLighten(Allocation ain, Allocation aout) {
-        blend(17, ain, aout);
-    }
-
-    public void forEachOverlay(Allocation ain, Allocation aout) {
-        blend(18, ain, aout);
-    }
-
-    public void forEachHardlight(Allocation ain, Allocation aout) {
-        blend(19, ain, aout);
-    }
-
-    public void forEachSoftlight(Allocation ain, Allocation aout) {
-        blend(20, ain, aout);
-    }
-
-    public void forEachDifference(Allocation ain, Allocation aout) {
-        blend(21, ain, aout);
-    }
-
-    public void forEachNegation(Allocation ain, Allocation aout) {
-        blend(22, ain, aout);
-    }
-
-    public void forEachExclusion(Allocation ain, Allocation aout) {
-        blend(23, ain, aout);
-    }
-
-    public void forEachColorDodge(Allocation ain, Allocation aout) {
-        blend(24, ain, aout);
-    }
-
-    public void forEachInverseColorDodge(Allocation ain, Allocation aout) {
-        blend(25, ain, aout);
-    }
-
-    public void forEachSoftDodge(Allocation ain, Allocation aout) {
-        blend(26, ain, aout);
-    }
-
-    public void forEachColorBurn(Allocation ain, Allocation aout) {
-        blend(27, ain, aout);
-    }
-
-    public void forEachInverseColorBurn(Allocation ain, Allocation aout) {
-        blend(28, ain, aout);
-    }
-
-    public void forEachSoftBurn(Allocation ain, Allocation aout) {
-        blend(29, ain, aout);
-    }
-
-    public void forEachReflect(Allocation ain, Allocation aout) {
-        blend(30, ain, aout);
-    }
-
-    public void forEachGlow(Allocation ain, Allocation aout) {
-        blend(31, ain, aout);
-    }
-
-    public void forEachFreeze(Allocation ain, Allocation aout) {
-        blend(32, ain, aout);
-    }
-
-    public void forEachHeat(Allocation ain, Allocation aout) {
-        blend(33, ain, aout);
-    }
-*/
-    /**
-     * Sets dst = min(src + dst, 1.0)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachAdd(Allocation ain, Allocation aout) {
-        blend(34, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Add kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDAdd() {
-        return createKernelID(34, 3, null, null);
-    }
-
-    /**
-     * Sets dst = max(dst - src, 0.0)
-     *
-     * @param ain The source buffer
-     * @param aout The destination buffer
-     */
-    public void forEachSubtract(Allocation ain, Allocation aout) {
-        blend(35, ain, aout);
-    }
-
-    /**
-     * Get a KernelID for the Subtract kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelIDSubtract() {
-        return createKernelID(35, 3, null, null);
-    }
-
-/*
-    public void forEachStamp(Allocation ain, Allocation aout) {
-        blend(36, ain, aout);
-    }
-
-    public void forEachRed(Allocation ain, Allocation aout) {
-        blend(37, ain, aout);
-    }
-
-    public void forEachGreen(Allocation ain, Allocation aout) {
-        blend(38, ain, aout);
-    }
-
-    public void forEachBlue(Allocation ain, Allocation aout) {
-        blend(39, ain, aout);
-    }
-
-    public void forEachHue(Allocation ain, Allocation aout) {
-        blend(40, ain, aout);
-    }
-
-    public void forEachSaturation(Allocation ain, Allocation aout) {
-        blend(41, ain, aout);
-    }
-
-    public void forEachColor(Allocation ain, Allocation aout) {
-        blend(42, ain, aout);
-    }
-
-    public void forEachLuminosity(Allocation ain, Allocation aout) {
-        blend(43, ain, aout);
-    }
-*/
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
deleted file mode 100644
index e1a134a..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-/**
- * Intrinsic Gausian blur filter. Applies a gaussian blur of the
- * specified radius to all elements of an allocation.
- *
- *
- **/
-public class ScriptIntrinsicBlur extends ScriptIntrinsic {
-    private final float[] mValues = new float[9];
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    protected ScriptIntrinsicBlur(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Create an intrinsic for applying a blur to an allocation. The
-     * default radius is 5.0.
-     *
-     * Supported elements types are {@link Element#U8},
-     * {@link Element#U8_4}.
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for inputs and outputs
-     *
-     * @return ScriptIntrinsicBlur
-     */
-    public static ScriptIntrinsicBlur create(RenderScript rs, Element e) {
-        if ((!e.isCompatible(Element.U8_4(rs))) && (!e.isCompatible(Element.U8(rs)))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(5, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicBlur si = new ScriptIntrinsicBlur(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        si.setRadius(5.f);
-
-        return si;
-    }
-
-    /**
-     * Set the input of the blur.
-     * Must match the element type supplied during create.
-     *
-     * @param ain The input allocation
-     */
-    public void setInput(Allocation ain) {
-        mInput = ain;
-        setVar(1, ain);
-    }
-
-    /**
-     * Set the radius of the Blur.
-     *
-     * Supported range 0 < radius <= 25
-     *
-     * @param radius The radius of the blur
-     */
-    public void setRadius(float radius) {
-        if (radius <= 0 || radius > 25) {
-            throw new RSIllegalArgumentException("Radius out of range (0 < r <= 25).");
-        }
-        setVar(0, radius);
-    }
-
-    /**
-     * Apply the filter to the input and save to the specified
-     * allocation.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     */
-    public void forEach(Allocation aout) {
-        forEach(0, (Allocation) null, aout, null);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 2, null, null);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(1, null);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java
deleted file mode 100644
index f03526e..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicColorMatrix.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic for applying a color matrix to allocations.
- *
- * This has the same effect as loading each element and
- * converting it to a {@link Element#F32_4}, multiplying the
- * result by the 4x4 color matrix as performed by
- * rsMatrixMultiply() and writing it to the output after
- * conversion back to {@link Element#U8_4}.
- **/
-public class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
-    private final Matrix4f mMatrix = new Matrix4f();
-    private final Float4 mAdd = new Float4();
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    protected ScriptIntrinsicColorMatrix(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Create an intrinsic for applying a color matrix to an
-     * allocation.
-     *
-     * Supported elements types are {@link Element#U8_4}
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for intputs and outputs
-     *
-     * @return ScriptIntrinsicColorMatrix
-     */
-    public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
-        if (!e.isCompatible(Element.U8_4(rs))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(2, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicColorMatrix si = new ScriptIntrinsicColorMatrix(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-
-    }
-
-    private void setMatrix() {
-        FieldPacker fp = new FieldPacker(16*4);
-        fp.addMatrix(mMatrix);
-        setVar(0, fp);
-    }
-
-    /**
-     * Set the color matrix which will be applied to each cell of
-     * the image.
-     *
-     * @param m The 4x4 matrix to set.
-     */
-    public void setColorMatrix(Matrix4f m) {
-        mMatrix.load(m);
-        setMatrix();
-    }
-
-    /**
-     * Set the color matrix which will be applied to each cell of the image.
-     * This will set the alpha channel to be a copy.
-     *
-     * @param m The 3x3 matrix to set.
-     */
-    public void setColorMatrix(Matrix3f m) {
-        mMatrix.load(m);
-        setMatrix();
-    }
-
-    /**
-     * Set the value to be added after the color matrix has been
-     * applied. The default value is {0, 0, 0, 0}
-     *
-     * @param f The float4 value to be added.
-     */
-    public void setAdd(Float4 f) {
-        mAdd.x = f.x;
-        mAdd.y = f.y;
-        mAdd.z = f.z;
-        mAdd.w = f.w;
-
-        FieldPacker fp = new FieldPacker(4*4);
-        fp.addF32(f.x);
-        fp.addF32(f.y);
-        fp.addF32(f.z);
-        fp.addF32(f.w);
-        setVar(1, fp);
-    }
-
-    /**
-     * Set the value to be added after the color matrix has been
-     * applied. The default value is {0, 0, 0, 0}
-     *
-     * @param r The red add value.
-     * @param g The green add value.
-     * @param b The blue add value.
-     * @param a The alpha add value.
-     */
-    public void setAdd(float r, float g, float b, float a) {
-        mAdd.x = r;
-        mAdd.y = g;
-        mAdd.z = b;
-        mAdd.w = a;
-
-        FieldPacker fp = new FieldPacker(4*4);
-        fp.addF32(mAdd.x);
-        fp.addF32(mAdd.y);
-        fp.addF32(mAdd.z);
-        fp.addF32(mAdd.w);
-        setVar(1, fp);
-    }
-
-    /**
-     * Set a color matrix to convert from RGB to luminance. The alpha channel
-     * will be a copy.
-     *
-     */
-    public void setGreyscale() {
-        mMatrix.loadIdentity();
-        mMatrix.set(0, 0, 0.299f);
-        mMatrix.set(1, 0, 0.587f);
-        mMatrix.set(2, 0, 0.114f);
-        mMatrix.set(0, 1, 0.299f);
-        mMatrix.set(1, 1, 0.587f);
-        mMatrix.set(2, 1, 0.114f);
-        mMatrix.set(0, 2, 0.299f);
-        mMatrix.set(1, 2, 0.587f);
-        mMatrix.set(2, 2, 0.114f);
-        setMatrix();
-    }
-
-    /**
-     * Set the matrix to convert from YUV to RGB with a direct copy of the 4th
-     * channel.
-     *
-     */
-    public void setYUVtoRGB() {
-        mMatrix.loadIdentity();
-        mMatrix.set(0, 0, 1.f);
-        mMatrix.set(1, 0, 0.f);
-        mMatrix.set(2, 0, 1.13983f);
-        mMatrix.set(0, 1, 1.f);
-        mMatrix.set(1, 1, -0.39465f);
-        mMatrix.set(2, 1, -0.5806f);
-        mMatrix.set(0, 2, 1.f);
-        mMatrix.set(1, 2, 2.03211f);
-        mMatrix.set(2, 2, 0.f);
-        setMatrix();
-    }
-
-    /**
-     * Set the matrix to convert from RGB to YUV with a direct copy of the 4th
-     * channel.
-     *
-     */
-    public void setRGBtoYUV() {
-        mMatrix.loadIdentity();
-        mMatrix.set(0, 0, 0.299f);
-        mMatrix.set(1, 0, 0.587f);
-        mMatrix.set(2, 0, 0.114f);
-        mMatrix.set(0, 1, -0.14713f);
-        mMatrix.set(1, 1, -0.28886f);
-        mMatrix.set(2, 1, 0.436f);
-        mMatrix.set(0, 2, 0.615f);
-        mMatrix.set(1, 2, -0.51499f);
-        mMatrix.set(2, 2, -0.10001f);
-        setMatrix();
-    }
-
-
-    /**
-     * Invoke the kernel and apply the matrix to each cell of ain and copy to
-     * aout.
-     *
-     * @param ain Input allocation
-     * @param aout Output allocation
-     */
-    public void forEach(Allocation ain, Allocation aout) {
-        forEach(0, ain, aout, null);
-    }
-
-    /**
-     * Invoke the kernel and apply the matrix to each cell of input
-     * {@link Allocation} and copy to the output {@link Allocation}.
-     *
-     * If the vector size of the input is less than four, the
-     * remaining components are treated as zero for the matrix
-     * multiply.
-     *
-     * If the output vector size is less than four, the unused
-     * vector components are discarded.
-     *
-     *
-     * @param ain Input allocation
-     * @param aout Output allocation
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
-        if (!ain.getElement().isCompatible(Element.U8(mRS)) &&
-            !ain.getElement().isCompatible(Element.U8_2(mRS)) &&
-            !ain.getElement().isCompatible(Element.U8_3(mRS)) &&
-            !ain.getElement().isCompatible(Element.U8_4(mRS)) &&
-            !ain.getElement().isCompatible(Element.F32(mRS)) &&
-            !ain.getElement().isCompatible(Element.F32_2(mRS)) &&
-            !ain.getElement().isCompatible(Element.F32_3(mRS)) &&
-            !ain.getElement().isCompatible(Element.F32_4(mRS))) {
-
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-
-        if (!aout.getElement().isCompatible(Element.U8(mRS)) &&
-            !aout.getElement().isCompatible(Element.U8_2(mRS)) &&
-            !aout.getElement().isCompatible(Element.U8_3(mRS)) &&
-            !aout.getElement().isCompatible(Element.U8_4(mRS)) &&
-            !aout.getElement().isCompatible(Element.F32(mRS)) &&
-            !aout.getElement().isCompatible(Element.F32_2(mRS)) &&
-            !aout.getElement().isCompatible(Element.F32_3(mRS)) &&
-            !aout.getElement().isCompatible(Element.F32_4(mRS))) {
-
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-
-        forEach(0, ain, aout, null, opt);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 3, null, null);
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
deleted file mode 100644
index 17889aa..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic for applying a 3x3 convolve to an allocation.
- *
- **/
-public class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
-    private final float[] mValues = new float[9];
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    ScriptIntrinsicConvolve3x3(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8}, {@link
-     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
-     * {@link Element#F32}, {@link Element#F32_2}, {@link
-     * Element#F32_3}, and {@link Element#F32_4}.
-     *
-     * <p> The default coefficients are:
-     * <code>
-     * <p> [ 0,  0,  0 ]
-     * <p> [ 0,  1,  0 ]
-     * <p> [ 0,  0,  0 ]
-     * </code>
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for intputs and outputs
-     *
-     * @return ScriptIntrinsicConvolve3x3
-     */
-    public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
-        float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0};
-        if (!e.isCompatible(Element.U8(rs)) &&
-            !e.isCompatible(Element.U8_2(rs)) &&
-            !e.isCompatible(Element.U8_3(rs)) &&
-            !e.isCompatible(Element.U8_4(rs)) &&
-            !e.isCompatible(Element.F32(rs)) &&
-            !e.isCompatible(Element.F32_2(rs)) &&
-            !e.isCompatible(Element.F32_3(rs)) &&
-            !e.isCompatible(Element.F32_4(rs))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(1, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicConvolve3x3 si = new ScriptIntrinsicConvolve3x3(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        si.setCoefficients(f);
-        return si;
-    }
-
-    /**
-     * Set the input of the 3x3 convolve.
-     * Must match the element type supplied during create.
-     *
-     * @param ain The input allocation.
-     */
-    public void setInput(Allocation ain) {
-        mInput = ain;
-        setVar(1, ain);
-    }
-
-    /**
-     * Set the coefficients for the convolve.
-     *
-     * <p> The convolve layout is:
-     * <code>
-     * <p> [ 0,  1,  2 ]
-     * <p> [ 3,  4,  5 ]
-     * <p> [ 6,  7,  8 ]
-     * </code>
-     *
-     * @param v The array of coefficients to set
-     */
-    public void setCoefficients(float v[]) {
-        FieldPacker fp = new FieldPacker(9*4);
-        for (int ct=0; ct < mValues.length; ct++) {
-            mValues[ct] = v[ct];
-            fp.addF32(mValues[ct]);
-        }
-        setVar(0, fp);
-    }
-
-    /**
-     * Apply the filter to the input and save to the specified
-     * allocation.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     */
-    public void forEach(Allocation aout) {
-        forEach(0, (Allocation) null, aout, null);
-    }
-
-    /**
-     * Apply the filter to the input and save to the specified
-     * allocation.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach(Allocation aout, Script.LaunchOptions opt) {
-        forEach(0, (Allocation) null, aout, null, opt);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 2, null, null);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(1, null);
-    }
-
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
deleted file mode 100644
index 2c591ba..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic for applying a 5x5 convolve to an allocation.
- *
- **/
-public class ScriptIntrinsicConvolve5x5 extends ScriptIntrinsic {
-    private final float[] mValues = new float[25];
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    ScriptIntrinsicConvolve5x5(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8}, {@link
-     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
-     * {@link Element#F32}, {@link Element#F32_2}, {@link
-     * Element#F32_3}, and {@link Element#F32_4}.
-     *
-     * <p> The default coefficients are:
-     * <code>
-     * <p> [ 0,  0,  0,  0,  0  ]
-     * <p> [ 0,  0,  0,  0,  0  ]
-     * <p> [ 0,  0,  1,  0,  0  ]
-     * <p> [ 0,  0,  0,  0,  0  ]
-     * <p> [ 0,  0,  0,  0,  0  ]
-     * </code>
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for intputs and outputs
-     *
-     * @return ScriptIntrinsicConvolve5x5
-     */
-    public static ScriptIntrinsicConvolve5x5 create(RenderScript rs, Element e) {
-        if (!e.isCompatible(Element.U8(rs)) &&
-            !e.isCompatible(Element.U8_2(rs)) &&
-            !e.isCompatible(Element.U8_3(rs)) &&
-            !e.isCompatible(Element.U8_4(rs)) &&
-            !e.isCompatible(Element.F32(rs)) &&
-            !e.isCompatible(Element.F32_2(rs)) &&
-            !e.isCompatible(Element.F32_3(rs)) &&
-            !e.isCompatible(Element.F32_4(rs))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(4, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicConvolve5x5 si = new ScriptIntrinsicConvolve5x5(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-
-    }
-
-    /**
-     * Set the input of the 5x5 convolve.
-     * Must match the element type supplied during create.
-     *
-     * @param ain The input allocation.
-     */
-    public void setInput(Allocation ain) {
-        mInput = ain;
-        setVar(1, ain);
-    }
-
-    /**
-    * Set the coefficients for the convolve.
-    *
-    * <p> The convolve layout is:
-    * <code>
-    * <p> [ 0,  1,  2,  3,  4  ]
-    * <p> [ 5,  6,  7,  8,  9  ]
-    * <p> [ 10, 11, 12, 13, 14 ]
-    * <p> [ 15, 16, 17, 18, 19 ]
-    * <p> [ 20, 21, 22, 23, 24 ]
-    * </code>
-    *
-    * @param v The array of coefficients to set
-    */
-    public void setCoefficients(float v[]) {
-        FieldPacker fp = new FieldPacker(25*4);
-        for (int ct=0; ct < mValues.length; ct++) {
-            mValues[ct] = v[ct];
-            fp.addF32(mValues[ct]);
-        }
-        setVar(0, fp);
-    }
-
-    /**
-     * Apply the filter to the input and save to the specified
-     * allocation.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     */
-    public void forEach(Allocation aout) {
-        forEach(0, (Allocation) null, aout, null);
-    }
-
-    /**
-     * Apply the filter to the input and save to the specified
-     * allocation.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach(Allocation aout, Script.LaunchOptions opt) {
-        forEach(0, (Allocation) null, aout, null, opt);
-    }
-
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 2, null, null);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(1, null);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java
deleted file mode 100644
index e3e6406..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicHistogram.java
+++ /dev/null
@@ -1,232 +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.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic Histogram filter.
- *
- *
- **/
-public class ScriptIntrinsicHistogram extends ScriptIntrinsic {
-    private Allocation mOut;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    protected ScriptIntrinsicHistogram(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Create an intrinsic for calculating the histogram of an uchar
-     * or uchar4 image.
-     *
-     * Supported elements types are
-     * {@link Element#U8_4}, {@link Element#U8_3},
-     * {@link Element#U8_2}, {@link Element#U8}
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for inputs
-     *
-     * @return ScriptIntrinsicHistogram
-     */
-    public static ScriptIntrinsicHistogram create(RenderScript rs, Element e) {
-        if ((!e.isCompatible(Element.U8_4(rs))) &&
-            (!e.isCompatible(Element.U8_3(rs))) &&
-            (!e.isCompatible(Element.U8_2(rs))) &&
-            (!e.isCompatible(Element.U8(rs)))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(9, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicHistogram si = new ScriptIntrinsicHistogram(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-    }
-
-    /**
-     * Process an input buffer and place the histogram into the
-     * output allocation. The output allocation may be a narrower
-     * vector size than the input. In this case the vector size of
-     * the output is used to determine how many of the input
-     * channels are used in the computation. This is useful if you
-     * have an RGBA input buffer but only want the histogram for
-     * RGB.
-     *
-     * 1D and 2D input allocations are supported.
-     *
-     * @param ain The input image
-     */
-    public void forEach(Allocation ain) {
-        forEach(ain, null);
-    }
-
-    /**
-     * Process an input buffer and place the histogram into the
-     * output allocation. The output allocation may be a narrower
-     * vector size than the input. In this case the vector size of
-     * the output is used to determine how many of the input
-     * channels are used in the computation. This is useful if you
-     * have an RGBA input buffer but only want the histogram for
-     * RGB.
-     *
-     * 1D and 2D input allocations are supported.
-     *
-     * @param ain The input image
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach(Allocation ain, Script.LaunchOptions opt) {
-        if (ain.getType().getElement().getVectorSize() <
-            mOut.getType().getElement().getVectorSize()) {
-
-            throw new RSIllegalArgumentException(
-                "Input vector size must be >= output vector size.");
-        }
-        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_2(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_3(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Input type must be U8, U8_1, U8_2 or U8_4.");
-        }
-
-        forEach(0, ain, null, null, opt);
-    }
-
-
-
-    /**
-     * Set the coefficients used for the RGBA to Luminocity
-     * calculation. The default is {0.299f, 0.587f, 0.114f, 0.f}.
-     *
-     * Coefficients must be >= 0 and sum to 1.0 or less.
-     *
-     * @param r Red coefficient
-     * @param g Green coefficient
-     * @param b Blue coefficient
-     * @param a Alpha coefficient
-     */
-    public void setDotCoefficients(float r, float g, float b, float a) {
-        if ((r < 0.f) || (g < 0.f) || (b < 0.f) || (a < 0.f)) {
-            throw new RSIllegalArgumentException("Coefficient may not be negative.");
-        }
-        if ((r + g + b + a) > 1.f) {
-            throw new RSIllegalArgumentException("Sum of coefficients must be 1.0 or less.");
-        }
-
-        FieldPacker fp = new FieldPacker(16);
-        fp.addF32(r);
-        fp.addF32(g);
-        fp.addF32(b);
-        fp.addF32(a);
-        setVar(0, fp);
-    }
-
-    /**
-     * Set the output of the histogram.  32 bit integer types are
-     * supported.
-     *
-     * @param aout The output allocation
-     */
-    public void setOutput(Allocation aout) {
-        mOut = aout;
-        if (mOut.getType().getElement() != Element.U32(mRS) &&
-            mOut.getType().getElement() != Element.U32_2(mRS) &&
-            mOut.getType().getElement() != Element.U32_3(mRS) &&
-            mOut.getType().getElement() != Element.U32_4(mRS) &&
-            mOut.getType().getElement() != Element.I32(mRS) &&
-            mOut.getType().getElement() != Element.I32_2(mRS) &&
-            mOut.getType().getElement() != Element.I32_3(mRS) &&
-            mOut.getType().getElement() != Element.I32_4(mRS)) {
-
-            throw new RSIllegalArgumentException("Output type must be U32 or I32.");
-        }
-        if ((mOut.getType().getX() != 256) ||
-            (mOut.getType().getY() != 0) ||
-            mOut.getType().hasMipmaps() ||
-            (mOut.getType().getYuv() != 0)) {
-
-            throw new RSIllegalArgumentException("Output must be 1D, 256 elements.");
-        }
-        setVar(1, aout);
-    }
-
-
-    /**
-     * Process an input buffer and place the histogram into the
-     * output allocation. The dot product of the input channel and
-     * the coefficients from 'setDotCoefficients' are used to
-     * calculate the output values.
-     *
-     * 1D and 2D input allocations are supported.
-     *
-     * @param ain The input image
-     */
-    public void forEach_Dot(Allocation ain) {
-        forEach_Dot(ain, null);
-    }
-
-    /**
-     * Process an input buffer and place the histogram into the
-     * output allocation. The dot product of the input channel and
-     * the coefficients from 'setDotCoefficients' are used to
-     * calculate the output values.
-     *
-     * 1D and 2D input allocations are supported.
-     *
-     * @param ain The input image
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach_Dot(Allocation ain, Script.LaunchOptions opt) {
-        if (mOut.getType().getElement().getVectorSize() != 1) {
-            throw new RSIllegalArgumentException("Output vector size must be one.");
-        }
-        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_2(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_3(mRS)) &&
-            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Input type must be U8, U8_1, U8_2 or U8_4.");
-        }
-
-        forEach(1, ain, null, null, opt);
-    }
-
-
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID_Separate() {
-        return createKernelID(0, 3, null, null);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(1, null);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java
deleted file mode 100644
index 0b905ba..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicLUT.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic for applying a per-channel lookup table. Each
- * channel of the input has an independant lookup table. The
- * tables are 256 entries in size and can cover the full value
- * range of {@link Element#U8_4}.
- **/
-public class ScriptIntrinsicLUT extends ScriptIntrinsic {
-    private final Matrix4f mMatrix = new Matrix4f();
-    private Allocation mTables;
-    private final byte mCache[] = new byte[1024];
-    private boolean mDirty = true;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    protected ScriptIntrinsicLUT(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8_4}
-     *
-     * The defaults tables are identity.
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for intputs and outputs
-     *
-     * @return ScriptIntrinsicLUT
-     */
-    public static ScriptIntrinsicLUT create(RenderScript rs, Element e) {
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(3, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicLUT si = new ScriptIntrinsicLUT(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        si.mTables = Allocation.createSized(rs, Element.U8(rs), 1024);
-        for (int ct=0; ct < 256; ct++) {
-            si.mCache[ct] = (byte)ct;
-            si.mCache[ct + 256] = (byte)ct;
-            si.mCache[ct + 512] = (byte)ct;
-            si.mCache[ct + 768] = (byte)ct;
-        }
-        si.setVar(0, si.mTables);
-        return si;
-    }
-
-
-    private void validate(int index, int value) {
-        if (index < 0 || index > 255) {
-            throw new RSIllegalArgumentException("Index out of range (0-255).");
-        }
-        if (value < 0 || value > 255) {
-            throw new RSIllegalArgumentException("Value out of range (0-255).");
-        }
-    }
-
-    /**
-     * Set an entry in the red channel lookup table
-     *
-     * @param index Must be 0-255
-     * @param value Must be 0-255
-     */
-    public void setRed(int index, int value) {
-        validate(index, value);
-        mCache[index] = (byte)value;
-        mDirty = true;
-    }
-
-    /**
-     * Set an entry in the green channel lookup table
-     *
-     * @param index Must be 0-255
-     * @param value Must be 0-255
-     */
-    public void setGreen(int index, int value) {
-        validate(index, value);
-        mCache[index+256] = (byte)value;
-        mDirty = true;
-    }
-
-    /**
-     * Set an entry in the blue channel lookup table
-     *
-     * @param index Must be 0-255
-     * @param value Must be 0-255
-     */
-    public void setBlue(int index, int value) {
-        validate(index, value);
-        mCache[index+512] = (byte)value;
-        mDirty = true;
-    }
-
-    /**
-     * Set an entry in the alpha channel lookup table
-     *
-     * @param index Must be 0-255
-     * @param value Must be 0-255
-     */
-    public void setAlpha(int index, int value) {
-        validate(index, value);
-        mCache[index+768] = (byte)value;
-        mDirty = true;
-    }
-
-
-    /**
-     * Invoke the kernel and apply the lookup to each cell of ain
-     * and copy to aout.
-     *
-     * @param ain Input allocation
-     * @param aout Output allocation
-     */
-    public void forEach(Allocation ain, Allocation aout) {
-        if (mDirty) {
-            mDirty = false;
-            mTables.copyFromUnchecked(mCache);
-        }
-        forEach(0, ain, aout, null);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 3, null, null);
-    }
-}
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java
deleted file mode 100644
index 61c169c..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicResize.java
+++ /dev/null
@@ -1,127 +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.v8.renderscript;
-
-import android.util.Log;
-
-/**
- * Intrinsic for performing a resize of a 2D allocation.
- */
-public class ScriptIntrinsicResize extends ScriptIntrinsic {
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 21;
-
-    protected ScriptIntrinsicResize(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Supported elements types are {@link Element#U8}, {@link
-     * Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4}
-     * {@link Element#F32}, {@link Element#F32_2}, {@link
-     * Element#F32_3}, {@link Element#F32_4}
-     *
-     * @param rs The RenderScript context
-     *
-     * @return ScriptIntrinsicResize
-     */
-    public static ScriptIntrinsicResize create(RenderScript rs) {
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(12, 0, mUseIncSupp);
-
-        ScriptIntrinsicResize si = new ScriptIntrinsicResize(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-
-    }
-
-    /**
-     * Set the input of the resize.
-     * Must match the element type supplied during create.
-     *
-     * @param ain The input allocation.
-     */
-    public void setInput(Allocation ain) {
-        Element e = ain.getElement();
-        if (!e.isCompatible(Element.U8(mRS)) &&
-            !e.isCompatible(Element.U8_2(mRS)) &&
-            !e.isCompatible(Element.U8_3(mRS)) &&
-            !e.isCompatible(Element.U8_4(mRS)) &&
-            !e.isCompatible(Element.F32(mRS)) &&
-            !e.isCompatible(Element.F32_2(mRS)) &&
-            !e.isCompatible(Element.F32_3(mRS)) &&
-            !e.isCompatible(Element.F32_4(mRS))) {
-            throw new RSIllegalArgumentException("Unsupported element type.");
-        }
-
-        mInput = ain;
-        setVar(0, ain);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(0, null);
-    }
-
-
-    /**
-     * Resize copy the input allocation to the output specified. The
-     * Allocation is rescaled if necessary using bi-cubic
-     * interpolation.
-     *
-     * @param aout Output allocation. Element type must match
-     *             current input.  Must not be same as input.
-     */
-    public void forEach_bicubic(Allocation aout) {
-        if (aout == mInput) {
-            throw new RSIllegalArgumentException("Output cannot be same as Input.");
-        }
-        forEach_bicubic(aout, null);
-    }
-
-    /**
-     * Resize copy the input allocation to the output specified. The
-     * Allocation is rescaled if necessary using bi-cubic
-     * interpolation.
-     *
-     * @param aout Output allocation. Element type must match
-     *             current input.
-     * @param opt LaunchOptions for clipping
-     */
-    public void forEach_bicubic(Allocation aout, Script.LaunchOptions opt) {
-        forEach(0, (Allocation) null, aout, null, opt);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID_bicubic() {
-        return createKernelID(0, 2, null, null);
-    }
-
-
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
deleted file mode 100644
index 6c84020..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v8.renderscript;
-
-
-/**
- * Intrinsic for converting an Android YUV buffer to RGB.
- *
- * The input allocation is supplied in NV21 format as a U8
- * element type. The output is RGBA, the alpha channel will be
- * set to 255.
- */
-public class ScriptIntrinsicYuvToRGB extends ScriptIntrinsic {
-    private Allocation mInput;
-    // API level for the intrinsic
-    private static final int INTRINSIC_API_LEVEL = 19;
-
-    ScriptIntrinsicYuvToRGB(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /**
-     * Create an intrinsic for converting YUV to RGB.
-     *
-     * Supported elements types are {@link Element#U8_4}
-     *
-     * @param rs The RenderScript context
-     * @param e Element type for output
-     *
-     * @return ScriptIntrinsicYuvToRGB
-     */
-    public static ScriptIntrinsicYuvToRGB create(RenderScript rs, Element e) {
-        // 6 comes from RS_SCRIPT_INTRINSIC_YUV_TO_RGB in rsDefines.h
-        long id;
-        boolean mUseIncSupp = rs.isUseNative() &&
-                              android.os.Build.VERSION.SDK_INT < INTRINSIC_API_LEVEL;
-
-        id = rs.nScriptIntrinsicCreate(6, e.getID(rs), mUseIncSupp);
-
-        ScriptIntrinsicYuvToRGB si = new ScriptIntrinsicYuvToRGB(id, rs);
-        si.setIncSupp(mUseIncSupp);
-        return si;
-    }
-
-
-    /**
-     * Set the input yuv allocation, must be {@link Element#U8}.
-     *
-     * @param ain The input allocation.
-     */
-    public void setInput(Allocation ain) {
-        mInput = ain;
-        setVar(0, ain);
-    }
-
-    /**
-     * Convert the image to RGB.
-     *
-     * @param aout Output allocation. Must match creation element
-     *             type.
-     */
-    public void forEach(Allocation aout) {
-        forEach(0, (Allocation) null, aout, null);
-    }
-
-    /**
-     * Get a KernelID for this intrinsic kernel.
-     *
-     * @return Script.KernelID The KernelID object.
-     */
-    public Script.KernelID getKernelID() {
-        return createKernelID(0, 2, null, null);
-    }
-
-    /**
-     * Get a FieldID for the input field of this intrinsic.
-     *
-     * @return Script.FieldID The FieldID object.
-     */
-    public Script.FieldID getFieldID_Input() {
-        return createFieldID(0, null);
-    }
-}
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Short2.java b/v8/renderscript/java/src/android/support/v8/renderscript/Short2.java
deleted file mode 100644
index 365b319..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Short2.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript Short2 type back to the Android system.
- *
- **/
-public class Short2 {
-    public Short2() {
-    }
-
-    public Short2(short initX, short initY) {
-        x = initX;
-        y = initY;
-    }
-
-    public short x;
-    public short y;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Short3.java b/v8/renderscript/java/src/android/support/v8/renderscript/Short3.java
deleted file mode 100644
index abf8196..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Short3.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript short3 type back to the Android system.
- *
- **/
-public class Short3 {
-    public Short3() {
-    }
-
-    public Short3(short initX, short initY, short initZ) {
-        x = initX;
-        y = initY;
-        z = initZ;
-    }
-
-    public short x;
-    public short y;
-    public short z;
-}
-
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Short4.java b/v8/renderscript/java/src/android/support/v8/renderscript/Short4.java
deleted file mode 100644
index d2108e4..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Short4.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2009 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.v8.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * Class for exposing the native RenderScript short4 type back to the Android system.
- *
- **/
-public class Short4 {
-    public Short4() {
-    }
-
-    public Short4(short initX, short initY, short initZ, short initW) {
-        x = initX;
-        y = initY;
-        z = initZ;
-        w = initW;
-    }
-
-    public short x;
-    public short y;
-    public short z;
-    public short w;
-}
-
-
-
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java b/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
deleted file mode 100644
index 6aeb9ea..0000000
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Type.java
+++ /dev/null
@@ -1,411 +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.v8.renderscript;
-
-
-import java.lang.reflect.Field;
-
-import android.graphics.ImageFormat;
-import android.util.Log;
-
-/**
- * <p>A Type describes the {@link android.support.v8.renderscript.Element} and
- * dimensions used for an {@link android.support.v8.renderscript.Allocation} or
- * a parallel operation. Types are created through
- * {@link android.support.v8.renderscript.Type.Builder}.</p>
- *
- * <p>A Type always includes an {@link android.support.v8.renderscript.Element}
- * and an X dimension. A Type may be multidimensional, up to three dimensions.
- * A nonzero value in the Y or Z dimensions indicates that the dimension is
- * present. Note that a Type with only a given X dimension and a Type with the
- * same X dimension but Y = 1 are not equivalent.</p>
- *
- * <p>A Type also supports inclusion of level of detail (LOD) or cube map
- * faces. LOD and cube map faces are booleans to indicate present or not
- * present. </p>
- *
- * <p>A Type also supports YUV format information to support an {@link
- * android.support.v8.renderscript.Allocation} in a YUV format. The YUV formats
- * supported are {@link android.graphics.ImageFormat#YV12} and {@link
- * android.graphics.ImageFormat#NV21}.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about creating an application that uses RenderScript,
- * read the
- * <a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a>
- * developer guide.</p>
- * </div>
- **/
-public class Type extends BaseObj {
-    int mDimX;
-    int mDimY;
-    int mDimZ;
-    boolean mDimMipmaps;
-    boolean mDimFaces;
-    int mDimYuv;
-    int mElementCount;
-    Element mElement;
-
-    public enum CubemapFace {
-        POSITIVE_X (0),
-        NEGATIVE_X (1),
-        POSITIVE_Y (2),
-        NEGATIVE_Y (3),
-        POSITIVE_Z (4),
-        NEGATIVE_Z (5);
-
-        int mID;
-        CubemapFace(int id) {
-            mID = id;
-        }
-    }
-
-    /**
-     * Return the element associated with this Type.
-     *
-     * @return Element
-     */
-    public Element getElement() {
-        return mElement;
-    }
-
-    /**
-     * Return the value of the X dimension.
-     *
-     * @return int
-     */
-    public int getX() {
-        return mDimX;
-    }
-
-    /**
-     * Return the value of the Y dimension or 0 for a 1D allocation.
-     *
-     * @return int
-     */
-    public int getY() {
-        return mDimY;
-    }
-
-    /**
-     * Return the value of the Z dimension or 0 for a 1D or 2D allocation.
-     *
-     * @return int
-     */
-    public int getZ() {
-        return mDimZ;
-    }
-
-    /**
-     * Get the YUV format
-     *
-     * @return int
-     */
-    public int getYuv() {
-        return mDimYuv;
-    }
-
-    /**
-     * Return if the Type has a mipmap chain.
-     *
-     * @return boolean
-     */
-    public boolean hasMipmaps() {
-        return mDimMipmaps;
-    }
-
-    /**
-     * Return if the Type is a cube map.
-     *
-     * @return boolean
-     */
-    public boolean hasFaces() {
-        return mDimFaces;
-    }
-
-    /**
-     * Return the total number of accessable cells in the Type.
-     *
-     * @return int
-     */
-    public int getCount() {
-        return mElementCount;
-    }
-
-    void calcElementCount() {
-        boolean hasLod = hasMipmaps();
-        int x = getX();
-        int y = getY();
-        int z = getZ();
-        int faces = 1;
-        if (hasFaces()) {
-            faces = 6;
-        }
-        if (x == 0) {
-            x = 1;
-        }
-        if (y == 0) {
-            y = 1;
-        }
-        if (z == 0) {
-            z = 1;
-        }
-
-        int count = x * y * z * faces;
-
-        while (hasLod && ((x > 1) || (y > 1) || (z > 1))) {
-            if(x > 1) {
-                x >>= 1;
-            }
-            if(y > 1) {
-                y >>= 1;
-            }
-            if(z > 1) {
-                z >>= 1;
-            }
-
-            count += x * y * z * faces;
-        }
-        mElementCount = count;
-    }
-
-
-    Type(long id, RenderScript rs) {
-        super(id, rs);
-    }
-
-    /*
-     * Get an identical dummy Type for Compat Context
-     *
-     */
-    public long getDummyType(RenderScript mRS, long eid) {
-        return mRS.nIncTypeCreate(eid, mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mDimYuv);
-    }
-
-    /**
-     * Utility function for creating basic 1D types. The type is
-     * created without mipmaps enabled.
-     *
-     * @param rs The RenderScript context
-     * @param e The Element for the Type
-     * @param dimX The X dimension, must be > 0
-     *
-     * @return Type
-     */
-    static public Type createX(RenderScript rs, Element e, int dimX) {
-        if (dimX < 1) {
-            throw new RSInvalidStateException("Dimension must be >= 1.");
-        }
-
-        long id = rs.nTypeCreate(e.getID(rs), dimX, 0, 0, false, false, 0);
-        Type t = new Type(id, rs);
-        t.mElement = e;
-        t.mDimX = dimX;
-        t.calcElementCount();
-        return t;
-    }
-
-    /**
-     * Utility function for creating basic 2D types. The type is
-     * created without mipmaps or cubemaps.
-     *
-     * @param rs The RenderScript context
-     * @param e The Element for the Type
-     * @param dimX The X dimension, must be > 0
-     * @param dimY The Y dimension, must be > 0
-     *
-     * @return Type
-     */
-    static public Type createXY(RenderScript rs, Element e, int dimX, int dimY) {
-        if ((dimX < 1) || (dimY < 1)) {
-            throw new RSInvalidStateException("Dimension must be >= 1.");
-        }
-
-        long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, 0, false, false, 0);
-        Type t = new Type(id, rs);
-        t.mElement = e;
-        t.mDimX = dimX;
-        t.mDimY = dimY;
-        t.calcElementCount();
-        return t;
-    }
-
-    /**
-     * Utility function for creating basic 3D types. The type is
-     * created without mipmaps.
-     *
-     * @param rs The RenderScript context
-     * @param e The Element for the Type
-     * @param dimX The X dimension, must be > 0
-     * @param dimY The Y dimension, must be > 0
-     * @param dimZ The Z dimension, must be > 0
-     *
-     * @return Type
-     */
-    static public Type createXYZ(RenderScript rs, Element e, int dimX, int dimY, int dimZ) {
-        if ((dimX < 1) || (dimY < 1) || (dimZ < 1)) {
-            throw new RSInvalidStateException("Dimension must be >= 1.");
-        }
-
-        long id = rs.nTypeCreate(e.getID(rs), dimX, dimY, dimZ, false, false, 0);
-        Type t = new Type(id, rs);
-        t.mElement = e;
-        t.mDimX = dimX;
-        t.mDimY = dimY;
-        t.mDimZ = dimZ;
-        t.calcElementCount();
-        return t;
-    }
-
-    /**
-     * Builder class for Type.
-     *
-     */
-    public static class Builder {
-        RenderScript mRS;
-        int mDimX = 1;
-        int mDimY;
-        int mDimZ;
-        boolean mDimMipmaps;
-        boolean mDimFaces;
-        int mYuv;
-
-        Element mElement;
-
-        /**
-         * Create a new builder object.
-         *
-         * @param rs
-         * @param e The element for the type to be created.
-         */
-        public Builder(RenderScript rs, Element e) {
-            e.checkValid();
-            mRS = rs;
-            mElement = e;
-        }
-
-        /**
-         * Add a dimension to the Type.
-         *
-         *
-         * @param value
-         */
-        public Builder setX(int value) {
-            if(value < 1) {
-                throw new RSIllegalArgumentException("Values of less than 1 for Dimension X are not valid.");
-            }
-            mDimX = value;
-            return this;
-        }
-
-        public Builder setY(int value) {
-            if(value < 1) {
-                throw new RSIllegalArgumentException("Values of less than 1 for Dimension Y are not valid.");
-            }
-            mDimY = value;
-            return this;
-        }
-
-        public Builder setZ(int value) {
-            if(value < 1) {
-                throw new RSIllegalArgumentException("Values of less than 1 for Dimension Z are not valid.");
-            }
-            mDimZ = value;
-            return this;
-        }
-
-        public Builder setMipmaps(boolean value) {
-            mDimMipmaps = value;
-            return this;
-        }
-
-        public Builder setFaces(boolean value) {
-            mDimFaces = value;
-            return this;
-        }
-
-        /**
-         * Set the YUV layout for a Type.
-         *
-         * @param yuvFormat {@link android.graphics.ImageFormat#YV12} or {@link android.graphics.ImageFormat#NV21}
-         */
-        public Builder setYuvFormat(int yuvFormat) {
-            switch (yuvFormat) {
-            case android.graphics.ImageFormat.NV21:
-            case android.graphics.ImageFormat.YV12:
-                break;
-
-            default:
-                throw new RSIllegalArgumentException("Only NV21 and YV12 are supported..");
-            }
-
-            mYuv = yuvFormat;
-            return this;
-        }
-
-
-        /**
-         * Validate structure and create a new Type.
-         *
-         * @return Type
-         */
-        public Type create() {
-            if (mDimZ > 0) {
-                if ((mDimX < 1) || (mDimY < 1)) {
-                    throw new RSInvalidStateException("Both X and Y dimension required when Z is present.");
-                }
-                if (mDimFaces) {
-                    throw new RSInvalidStateException("Cube maps not supported with 3D types.");
-                }
-            }
-            if (mDimY > 0) {
-                if (mDimX < 1) {
-                    throw new RSInvalidStateException("X dimension required when Y is present.");
-                }
-            }
-            if (mDimFaces) {
-                if (mDimY < 1) {
-                    throw new RSInvalidStateException("Cube maps require 2D Types.");
-                }
-            }
-
-            if (mYuv != 0) {
-                if ((mDimZ != 0) || mDimFaces || mDimMipmaps) {
-                    throw new RSInvalidStateException("YUV only supports basic 2D.");
-                }
-            }
-
-            Type t;
-            long id = mRS.nTypeCreate(mElement.getID(mRS),
-                                     mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mYuv);
-            t = new Type(id, mRS);
-
-            t.mElement = mElement;
-            t.mDimX = mDimX;
-            t.mDimY = mDimY;
-            t.mDimZ = mDimZ;
-            t.mDimMipmaps = mDimMipmaps;
-            t.mDimFaces = mDimFaces;
-            t.mDimYuv = mYuv;
-
-            t.calcElementCount();
-            return t;
-        }
-    }
-
-}
diff --git a/v8/renderscript/jni/Android.mk b/v8/renderscript/jni/Android.mk
deleted file mode 100644
index d6a800d..0000000
--- a/v8/renderscript/jni/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_SDK_VERSION := 14
-
-LOCAL_SRC_FILES:= \
-    android_rscompat_usage_io.cpp \
-    android_rscompat_usage_io_driver.cpp
-
-LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE) \
-	frameworks/rs \
-	frameworks/rs/cpp \
-	frameworks/rs/driver
-
-LOCAL_CFLAGS += -Wno-unused-parameter -Werror
-LOCAL_CFLAGS += -DRS_COMPATIBILITY_LIB -std=c++11
-
-LOCAL_MODULE:= libRSSupportIO
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_LDLIBS += -landroid
-LOCAL_LDFLAGS += -ldl -Wl,--exclude-libs,libc++_static.a
-LOCAL_NDK_STL_VARIANT := c++_static
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_SDK_VERSION := 9
-
-LOCAL_SRC_FILES:= \
-    android_renderscript_RenderScript.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-        libjnigraphics
-
-LOCAL_STATIC_LIBRARIES := \
-        libRSDispatch
-
-LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE) \
-	frameworks/rs \
-	frameworks/rs/cpp
-
-LOCAL_CFLAGS += -Wno-unused-parameter -Werror -std=c++11
-
-LOCAL_MODULE:= librsjni
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := libRSSupport
-
-LOCAL_LDFLAGS += -ldl -llog -Wl,--exclude-libs,libc++_static.a
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
deleted file mode 100644
index 5642835..0000000
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ /dev/null
@@ -1,2431 +0,0 @@
-/*
- * Copyright (C) 2011-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libRS_jni"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <math.h>
-#include <android/bitmap.h>
-#include <android/log.h>
-
-#include <rsEnv.h>
-#include "rsDispatch.h"
-#include <dlfcn.h>
-
-//#define LOG_API ALOG
-#define LOG_API(...)
-#define LOG_ERR(...) __android_log_print(ANDROID_LOG_ERROR, "RenderScript JNI", __VA_ARGS__);
-#define RS_JNI_VERSION 2301
-
-#define NELEM(m) (sizeof(m) / sizeof((m)[0]))
-
-template <typename... T>
-void UNUSED(T... t) {}
-#define PER_ARRAY_TYPE(flag, fnc, readonly, ...) {                                      \
-    jint len = 0;                                                                       \
-    void *ptr = nullptr;                                                                \
-    void *srcPtr = nullptr;                                                             \
-    size_t typeBytes = 0;                                                               \
-    jint relFlag = 0;                                                                   \
-    if (readonly) {                                                                     \
-        /* The on-release mode should only be JNI_ABORT for read-only accesses. */      \
-        /* readonly = true, also indicates we are copying to the allocation   . */      \
-        relFlag = JNI_ABORT;                                                            \
-    }                                                                                   \
-    switch(dataType) {                                                                  \
-    case RS_TYPE_FLOAT_32:                                                              \
-        len = _env->GetArrayLength((jfloatArray)data);                                  \
-        ptr = _env->GetFloatArrayElements((jfloatArray)data, flag);                     \
-        typeBytes = 4;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseFloatArrayElements((jfloatArray)data, (jfloat *)ptr, relFlag);     \
-        return;                                                                         \
-    case RS_TYPE_FLOAT_64:                                                              \
-        len = _env->GetArrayLength((jdoubleArray)data);                                 \
-        ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag);                   \
-        typeBytes = 8;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseDoubleArrayElements((jdoubleArray)data, (jdouble *)ptr, relFlag);  \
-        return;                                                                         \
-    case RS_TYPE_SIGNED_8:                                                              \
-    case RS_TYPE_UNSIGNED_8:                                                            \
-        len = _env->GetArrayLength((jbyteArray)data);                                   \
-        ptr = _env->GetByteArrayElements((jbyteArray)data, flag);                       \
-        typeBytes = 1;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseByteArrayElements((jbyteArray)data, (jbyte*)ptr, relFlag);         \
-        return;                                                                         \
-    case RS_TYPE_SIGNED_16:                                                             \
-    case RS_TYPE_UNSIGNED_16:                                                           \
-        len = _env->GetArrayLength((jshortArray)data);                                  \
-        ptr = _env->GetShortArrayElements((jshortArray)data, flag);                     \
-        typeBytes = 2;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseShortArrayElements((jshortArray)data, (jshort *)ptr, relFlag);     \
-        return;                                                                         \
-    case RS_TYPE_SIGNED_32:                                                             \
-    case RS_TYPE_UNSIGNED_32:                                                           \
-        len = _env->GetArrayLength((jintArray)data);                                    \
-        ptr = _env->GetIntArrayElements((jintArray)data, flag);                         \
-        typeBytes = 4;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseIntArrayElements((jintArray)data, (jint *)ptr, relFlag);           \
-        return;                                                                         \
-    case RS_TYPE_SIGNED_64:                                                             \
-    case RS_TYPE_UNSIGNED_64:                                                           \
-        len = _env->GetArrayLength((jlongArray)data);                                   \
-        ptr = _env->GetLongArrayElements((jlongArray)data, flag);                       \
-        typeBytes = 8;                                                                  \
-        if (usePadding) {                                                               \
-            srcPtr = ptr;                                                               \
-            len = len / 3 * 4;                                                          \
-            if (count == 0) {                                                           \
-                count = len / 4;                                                        \
-            }                                                                           \
-            ptr = malloc (len * typeBytes);                                             \
-            if (readonly) {                                                             \
-                copyWithPadding(ptr, srcPtr, mSize, count);                             \
-                fnc(__VA_ARGS__);                                                       \
-            } else {                                                                    \
-                fnc(__VA_ARGS__);                                                       \
-                copyWithUnPadding(srcPtr, ptr, mSize, count);                           \
-            }                                                                           \
-            free(ptr);                                                                  \
-            ptr = srcPtr;                                                               \
-        } else {                                                                        \
-            fnc(__VA_ARGS__);                                                           \
-        }                                                                               \
-        _env->ReleaseLongArrayElements((jlongArray)data, (jlong *)ptr, relFlag);        \
-        return;                                                                         \
-    default:                                                                            \
-        break;                                                                          \
-    }                                                                                   \
-    UNUSED(len, ptr, srcPtr, typeBytes, relFlag);                                       \
-}
-
-
-class AutoJavaStringToUTF8 {
-public:
-    AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) {
-        fCStr = env->GetStringUTFChars(str, NULL);
-        fLength = env->GetStringUTFLength(str);
-    }
-    ~AutoJavaStringToUTF8() {
-        fEnv->ReleaseStringUTFChars(fJStr, fCStr);
-    }
-    const char* c_str() const { return fCStr; }
-    jsize length() const { return fLength; }
-
-private:
-    JNIEnv*     fEnv;
-    jstring     fJStr;
-    const char* fCStr;
-    jsize       fLength;
-};
-
-class AutoJavaStringArrayToUTF8 {
-public:
-    AutoJavaStringArrayToUTF8(JNIEnv* env, jobjectArray strings, jsize stringsLength)
-    : mEnv(env), mStrings(strings), mStringsLength(stringsLength) {
-        mCStrings = NULL;
-        mSizeArray = NULL;
-        if (stringsLength > 0) {
-            mCStrings = (const char **)calloc(stringsLength, sizeof(char *));
-            mSizeArray = (size_t*)calloc(stringsLength, sizeof(size_t));
-            for (jsize ct = 0; ct < stringsLength; ct ++) {
-                jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct);
-                mCStrings[ct] = mEnv->GetStringUTFChars(s, NULL);
-                mSizeArray[ct] = mEnv->GetStringUTFLength(s);
-            }
-        }
-    }
-    ~AutoJavaStringArrayToUTF8() {
-        for (jsize ct=0; ct < mStringsLength; ct++) {
-            jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct);
-            mEnv->ReleaseStringUTFChars(s, mCStrings[ct]);
-        }
-        free(mCStrings);
-        free(mSizeArray);
-    }
-    const char **c_str() const { return mCStrings; }
-    size_t *c_str_len() const { return mSizeArray; }
-    jsize length() const { return mStringsLength; }
-
-private:
-    JNIEnv      *mEnv;
-    jobjectArray mStrings;
-    const char **mCStrings;
-    size_t      *mSizeArray;
-    jsize        mStringsLength;
-};
-
-
-// ---------------------------------------------------------------------------
-static dispatchTable dispatchTab;
-// Incremental Support lib
-static dispatchTable dispatchTabInc;
-
-static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative, jint targetApi, jstring libPath) {
-    void* handle = NULL;
-    if (useNative) {
-        handle = dlopen("libRS.so", RTLD_LAZY | RTLD_LOCAL);
-    } else {
-        // For API 9+, dlopen the full path of libRSSupport.
-        if (libPath != NULL) {
-            const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
-            handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
-            _env->ReleaseStringUTFChars(libPath, libPathJni);
-        } else {
-            handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
-        }
-    }
-    if (handle == NULL) {
-        LOG_ERR("couldn't dlopen %s; librsjni version: %d", dlerror(), RS_JNI_VERSION);
-        return false;
-    }
-
-    if (loadSymbols(handle, dispatchTab, targetApi) == false) {
-        LOG_ERR("Dispatch table init failed! librsjni version: %d", RS_JNI_VERSION);
-        dlclose(handle);
-        return false;
-    }
-    LOG_API("Successfully loaded runtime");
-    return true;
-}
-
-static ioSuppDT ioDispatch;
-static jboolean nLoadIOSO(JNIEnv *_env, jobject _this) {
-    void* handleIO = NULL;
-    handleIO = dlopen("libRSSupportIO.so", RTLD_LAZY | RTLD_LOCAL);
-    if (handleIO == NULL) {
-        LOG_ERR("Couldn't load libRSSupportIO.so, librsjni version: %d", RS_JNI_VERSION);
-        return false;
-    }
-    if (loadIOSuppSyms(handleIO, ioDispatch) == false) {
-        LOG_ERR("libRSSupportIO init failed! librsjni version: %d", RS_JNI_VERSION);
-        return false;
-    }
-    return true;
-}
-
-// ---------------------------------------------------------------------------
-
-static void copyWithPadding(void* ptr, void* srcPtr, int mSize, int count) {
-    int sizeBytesPad = mSize * 4;
-    int sizeBytes = mSize * 3;
-    uint8_t *dst = static_cast<uint8_t *>(ptr);
-    uint8_t *src = static_cast<uint8_t *>(srcPtr);
-    for (int i = 0; i < count; i++) {
-        memcpy(dst, src, sizeBytes);
-        dst += sizeBytesPad;
-        src += sizeBytes;
-    }
-}
-
-static void copyWithUnPadding(void* ptr, void* srcPtr, int mSize, int count) {
-    int sizeBytesPad = mSize * 4;
-    int sizeBytes = mSize * 3;
-    uint8_t *dst = static_cast<uint8_t *>(ptr);
-    uint8_t *src = static_cast<uint8_t *>(srcPtr);
-    for (int i = 0; i < count; i++) {
-        memcpy(dst, src, sizeBytes);
-        dst += sizeBytes;
-        src += sizeBytesPad;
-    }
-}
-
-
-// ---------------------------------------------------------------------------
-
-static void
-nContextFinish(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextFinish, con(%p)", (RsContext)con);
-    dispatchTab.ContextFinish((RsContext)con);
-}
-
-static jlong
-nClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong kernelID,
-               jlong returnValue, jlongArray fieldIDArray,
-               jlongArray valueArray, jintArray sizeArray,
-               jlongArray depClosureArray, jlongArray depFieldIDArray) {
-  jlong ret = 0;
-
-  jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
-  jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
-  jsize values_length = _env->GetArrayLength(valueArray);
-  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
-  jsize sizes_length = _env->GetArrayLength(sizeArray);
-  jlong* jDepClosures =
-      _env->GetLongArrayElements(depClosureArray, nullptr);
-  jsize depClosures_length = _env->GetArrayLength(depClosureArray);
-  jlong* jDepFieldIDs =
-      _env->GetLongArrayElements(depFieldIDArray, nullptr);
-  jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
-
-  size_t numValues, numDependencies;
-  RsScriptFieldID* fieldIDs;
-  RsClosure* depClosures;
-  RsScriptFieldID* depFieldIDs;
-
-  if (fieldIDs_length != values_length || values_length != sizes_length) {
-      LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
-      goto exit;
-  }
-
-  numValues = (size_t)fieldIDs_length;
-
-  if (depClosures_length != depFieldIDs_length) {
-      LOG_ERR("Unmatched closures and field IDs for dependencies in closure creation.");
-      goto exit;
-  }
-
-  numDependencies = (size_t)depClosures_length;
-
-  if (numDependencies > numValues) {
-      LOG_ERR("Unexpected number of dependencies in closure creation");
-      goto exit;
-  }
-
-  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
-      LOG_ERR("Too many arguments or globals in closure creation");
-      goto exit;
-  }
-
-  if (numValues > 0) {
-      fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
-      if (fieldIDs == nullptr) {
-          goto exit;
-      }
-  } else {
-      // numValues == 0
-      // alloca(0) implementation is platform dependent
-      fieldIDs = nullptr;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
-  }
-
-  if (numDependencies > 0) {
-      depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
-      if (depClosures == nullptr) {
-          goto exit;
-      }
-
-      for (size_t i = 0; i < numDependencies; i++) {
-          depClosures[i] = (RsClosure)jDepClosures[i];
-      }
-
-      depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies);
-      if (depFieldIDs == nullptr) {
-          goto exit;
-      }
-
-      for (size_t i = 0; i < numDependencies; i++) {
-          depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
-      }
-  } else {
-      // numDependencies == 0
-      // alloca(0) implementation is platform dependent
-      depClosures = nullptr;
-      depFieldIDs = nullptr;
-  }
-
-  ret = (jlong)(uintptr_t)dispatchTab.ClosureCreate(
-      (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
-      fieldIDs, numValues, jValues, numValues,
-      (int*)jSizes, numValues,
-      depClosures, numDependencies,
-      depFieldIDs, numDependencies);
-
-exit:
-
-  _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT);
-  _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT);
-  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
-  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
-  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
-
-  return ret;
-}
-
-static jlong
-nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
-                     jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
-                     jintArray sizeArray) {
-  jlong ret = 0;
-
-  jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
-  jsize jParamLength = _env->GetArrayLength(paramArray);
-  jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
-  jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
-  jsize values_length = _env->GetArrayLength(valueArray);
-  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
-  jsize sizes_length = _env->GetArrayLength(sizeArray);
-
-  size_t numValues;
-  RsScriptFieldID* fieldIDs;
-
-  if (fieldIDs_length != values_length || values_length != sizes_length) {
-      LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
-      goto exit;
-  }
-
-  numValues = (size_t) fieldIDs_length;
-
-  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
-      LOG_ERR("Too many arguments or globals in closure creation");
-      goto exit;
-  }
-
-  fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
-  if (fieldIDs == nullptr) {
-      goto exit;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
-  }
-
-  ret = (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
-      (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
-      fieldIDs, numValues, jValues, numValues,
-      (int*)jSizes, numValues);
-
-exit:
-
-  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
-  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
-  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
-  _env->ReleaseByteArrayElements(paramArray,      jParams,      JNI_ABORT);
-
-  return ret;
-}
-
-static void
-nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
-               jint index, jlong value, jint size) {
-  // Size is signed with -1 indicating the values is an Allocation
-  dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
-                  (uintptr_t)value, size);
-}
-
-static void
-nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
-                  jlong fieldID, jlong value, jint size) {
-  // Size is signed with -1 indicating the values is an Allocation
-  dispatchTab.ClosureSetGlobal((RsContext)con, (RsClosure)closureID,
-                     (RsScriptFieldID)fieldID, (int64_t)value, size);
-}
-
-static long
-nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
-                    jstring cacheDir, jlongArray closureArray) {
-  jlong ret = 0;
-
-  AutoJavaStringToUTF8 nameUTF(_env, name);
-  AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
-
-  jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
-  jsize numClosures = _env->GetArrayLength(closureArray);
-
-  RsClosure* closures;
-
-  if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
-    LOG_ERR("Too many closures in script group");
-    goto exit;
-  }
-
-  closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
-  if (closures == nullptr) {
-      goto exit;
-  }
-
-  for (int i = 0; i < numClosures; i++) {
-    closures[i] = (RsClosure)jClosures[i];
-  }
-
-  ret = (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
-      (RsContext)con, nameUTF.c_str(), nameUTF.length(),
-      cacheDirUTF.c_str(), cacheDirUTF.length(),
-      closures, numClosures);
-
-exit:
-
-  _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT);
-
-  return ret;
-}
-
-static void
-nScriptGroup2Execute(JNIEnv *_env, jobject _this, jlong con, jlong groupID) {
-  dispatchTab.ScriptGroupExecute((RsContext)con, (RsScriptGroup2)groupID);
-}
-
-static void
-nObjDestroy(JNIEnv *_env, jobject _this, jlong con, jlong obj)
-{
-    LOG_API("nObjDestroy, con(%p) obj(%p)", (RsContext)con, (void *)obj);
-    dispatchTab.ObjDestroy((RsContext)con, (void *)obj);
-}
-
-
-static void
-nScriptIntrinsicBLAS_Single(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong id, jint func, jint TransA,
-                            jint TransB, jint Side, jint Uplo, jint Diag, jint M, jint N, jint K,
-                            jfloat alpha, jlong A, jlong B, jfloat beta, jlong C, jint incX, jint incY,
-                            jint KL, jint KU, jboolean mUseInc) {
-    RsBlasCall call;
-    memset(&call, 0, sizeof(call));
-    call.func = (RsBlasFunction)func;
-    call.transA = (RsBlasTranspose)TransA;
-    call.transB = (RsBlasTranspose)TransB;
-    call.side = (RsBlasSide)Side;
-    call.uplo = (RsBlasUplo)Uplo;
-    call.diag = (RsBlasDiag)Diag;
-    call.M = M;
-    call.N = N;
-    call.K = K;
-    call.alpha.f = alpha;
-    call.beta.f = beta;
-    call.incX = incX;
-    call.incY = incY;
-    call.KL = KL;
-    call.KU = KU;
-
-    RsAllocation in_allocs[3];
-    in_allocs[0] = (RsAllocation)A;
-    in_allocs[1] = (RsAllocation)B;
-    in_allocs[2] = (RsAllocation)C;
-
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEachMulti((RsContext)incCon, (RsScript)id, 0,
-                                          in_allocs, NELEM(in_allocs), nullptr,
-                                          &call, sizeof(call), nullptr, 0);
-    } else {
-        dispatchTab.ScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                                       in_allocs, NELEM(in_allocs), nullptr,
-                                       &call, sizeof(call), nullptr, 0);
-    }
-}
-
-static void
-nScriptIntrinsicBLAS_Double(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong id, jint func, jint TransA,
-                            jint TransB, jint Side, jint Uplo, jint Diag, jint M, jint N, jint K,
-                            jdouble alpha, jlong A, jlong B, jdouble beta, jlong C, jint incX, jint incY,
-                            jint KL, jint KU, jboolean mUseInc) {
-    RsBlasCall call;
-    memset(&call, 0, sizeof(call));
-    call.func = (RsBlasFunction)func;
-    call.transA = (RsBlasTranspose)TransA;
-    call.transB = (RsBlasTranspose)TransB;
-    call.side = (RsBlasSide)Side;
-    call.uplo = (RsBlasUplo)Uplo;
-    call.diag = (RsBlasDiag)Diag;
-    call.M = M;
-    call.N = N;
-    call.K = K;
-    call.alpha.d = alpha;
-    call.beta.d = beta;
-    call.incX = incX;
-    call.incY = incY;
-    call.KL = KL;
-    call.KU = KU;
-
-    RsAllocation in_allocs[3];
-    in_allocs[0] = (RsAllocation)A;
-    in_allocs[1] = (RsAllocation)B;
-    in_allocs[2] = (RsAllocation)C;
-
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEachMulti((RsContext)incCon, (RsScript)id, 0,
-                                          in_allocs, NELEM(in_allocs), nullptr,
-                                          &call, sizeof(call), nullptr, 0);
-    } else {
-        dispatchTab.ScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                                        in_allocs, NELEM(in_allocs), nullptr,
-                                        &call, sizeof(call), nullptr, 0);
-    }
-}
-
-static void
-nScriptIntrinsicBLAS_Complex(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong id, jint func, jint TransA,
-                             jint TransB, jint Side, jint Uplo, jint Diag, jint M, jint N, jint K,
-                             jfloat alphaX, jfloat alphaY, jlong A, jlong B, jfloat betaX,
-                             jfloat betaY, jlong C, jint incX, jint incY, jint KL, jint KU, jboolean mUseInc) {
-    RsBlasCall call;
-    memset(&call, 0, sizeof(call));
-    call.func = (RsBlasFunction)func;
-    call.transA = (RsBlasTranspose)TransA;
-    call.transB = (RsBlasTranspose)TransB;
-    call.side = (RsBlasSide)Side;
-    call.uplo = (RsBlasUplo)Uplo;
-    call.diag = (RsBlasDiag)Diag;
-    call.M = M;
-    call.N = N;
-    call.K = K;
-    call.alpha.c.r = alphaX;
-    call.alpha.c.i = alphaY;
-    call.beta.c.r = betaX;
-    call.beta.c.i = betaY;
-    call.incX = incX;
-    call.incY = incY;
-    call.KL = KL;
-    call.KU = KU;
-
-    RsAllocation in_allocs[3];
-    in_allocs[0] = (RsAllocation)A;
-    in_allocs[1] = (RsAllocation)B;
-    in_allocs[2] = (RsAllocation)C;
-
-
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEachMulti((RsContext)incCon, (RsScript)id, 0,
-                                          in_allocs, NELEM(in_allocs), nullptr,
-                                          &call, sizeof(call), nullptr, 0);
-    } else {
-        dispatchTab.ScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                                       in_allocs, NELEM(in_allocs), nullptr,
-                                       &call, sizeof(call), nullptr, 0);
-    }
-}
-
-static void
-nScriptIntrinsicBLAS_Z(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong id, jint func, jint TransA,
-                       jint TransB, jint Side, jint Uplo, jint Diag, jint M, jint N, jint K,
-                       jdouble alphaX, jdouble alphaY, jlong A, jlong B, jdouble betaX,
-                       jdouble betaY, jlong C, jint incX, jint incY, jint KL, jint KU, jboolean mUseInc) {
-    RsBlasCall call;
-    memset(&call, 0, sizeof(call));
-    call.func = (RsBlasFunction)func;
-    call.transA = (RsBlasTranspose)TransA;
-    call.transB = (RsBlasTranspose)TransB;
-    call.side = (RsBlasSide)Side;
-    call.uplo = (RsBlasUplo)Uplo;
-    call.diag = (RsBlasDiag)Diag;
-    call.M = M;
-    call.N = N;
-    call.K = K;
-    call.alpha.z.r = alphaX;
-    call.alpha.z.i = alphaY;
-    call.beta.z.r = betaX;
-    call.beta.z.i = betaY;
-    call.incX = incX;
-    call.incY = incY;
-    call.KL = KL;
-    call.KU = KU;
-
-    RsAllocation in_allocs[3];
-    in_allocs[0] = (RsAllocation)A;
-    in_allocs[1] = (RsAllocation)B;
-    in_allocs[2] = (RsAllocation)C;
-
-
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEachMulti((RsContext)incCon, (RsScript)id, 0,
-                                          in_allocs, NELEM(in_allocs), nullptr,
-                                          &call, sizeof(call), nullptr, 0);
-    } else {
-        dispatchTab.ScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                                        in_allocs, NELEM(in_allocs), nullptr,
-                                        &call, sizeof(call), nullptr, 0);
-    }
-}
-
-
-static void
-nScriptIntrinsicBLAS_BNNM(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong id, jint M, jint N, jint K,
-                          jlong A, jint a_offset, jlong B, jint b_offset, jlong C, jint c_offset,
-                          jint c_mult_int, jboolean mUseInc) {
-    RsBlasCall call;
-    memset(&call, 0, sizeof(call));
-    call.func = RsBlas_bnnm;
-    call.M = M;
-    call.N = N;
-    call.K = K;
-    call.a_offset = a_offset & 0xFF;
-    call.b_offset = b_offset & 0xFF;
-    call.c_offset = c_offset;
-    call.c_mult_int = c_mult_int;
-
-    RsAllocation in_allocs[3];
-    in_allocs[0] = (RsAllocation)A;
-    in_allocs[1] = (RsAllocation)B;
-    in_allocs[2] = (RsAllocation)C;
-
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEachMulti((RsContext)incCon, (RsScript)id, 0,
-                                          in_allocs, NELEM(in_allocs), nullptr,
-                                          &call, sizeof(call), nullptr, 0);
-    } else {
-        dispatchTab.ScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                                        in_allocs, NELEM(in_allocs), nullptr,
-                                        &call, sizeof(call), nullptr, 0);
-    }
-}
-// ---------------------------------------------------------------------------
-static jlong
-nDeviceCreate(JNIEnv *_env, jobject _this)
-{
-    LOG_API("nDeviceCreate");
-    return (jlong)(uintptr_t)dispatchTab.DeviceCreate();
-}
-
-static void
-nDeviceDestroy(JNIEnv *_env, jobject _this, jlong dev)
-{
-    LOG_API("nDeviceDestroy");
-    return dispatchTab.DeviceDestroy((RsDevice)dev);
-}
-
-static void
-nDeviceSetConfig(JNIEnv *_env, jobject _this, jlong dev, jint p, jint value)
-{
-    LOG_API("nDeviceSetConfig  dev(%p), param(%i), value(%i)", (void *)dev, p, value);
-    return dispatchTab.DeviceSetConfig((RsDevice)dev, (RsDeviceParam)p, value);
-}
-
-static jlong
-nContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint ver, jint sdkVer,
-               jint ct, jstring nativeLibDirJava)
-{
-    LOG_API("nContextCreate");
-    // Access the NativeLibDir in the Java Context.
-    const char * nativeLibDir = _env->GetStringUTFChars(nativeLibDirJava, JNI_FALSE);
-    size_t length = (size_t)_env->GetStringUTFLength(nativeLibDirJava);
-
-    jlong id = (jlong)(uintptr_t)dispatchTab.ContextCreate((RsDevice)dev, ver,
-                                                           sdkVer,
-                                                           (RsContextType)ct, 0);
-    if (dispatchTab.SetNativeLibDir) {
-        dispatchTab.SetNativeLibDir((RsContext)id, nativeLibDir, length);
-    }
-
-    _env->ReleaseStringUTFChars(nativeLibDirJava, nativeLibDir);
-    return id;
-}
-
-
-static void
-nContextSetPriority(JNIEnv *_env, jobject _this, jlong con, jint p)
-{
-    LOG_API("ContextSetPriority, con(%p), priority(%i)", (RsContext)con, p);
-    dispatchTab.ContextSetPriority((RsContext)con, p);
-}
-
-
-
-static void
-nContextDestroy(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
-    dispatchTab.ContextDestroy((RsContext)con);
-}
-
-static void
-nContextDump(JNIEnv *_env, jobject _this, jlong con, jint bits)
-{
-    LOG_API("nContextDump, con(%p)  bits(%i)", (RsContext)con, bits);
-    dispatchTab.ContextDump((RsContext)con, bits);
-}
-
-
-static jstring
-nContextGetErrorMessage(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextGetErrorMessage, con(%p)", (RsContext)con);
-    char buf[1024];
-
-    size_t receiveLen;
-    uint32_t subID;
-    int id = dispatchTab.ContextGetMessage((RsContext)con,
-                                           buf, sizeof(buf),
-                                           &receiveLen, sizeof(receiveLen),
-                                           &subID, sizeof(subID));
-    if (!id && receiveLen) {
-        //        __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,
-        //            "message receive buffer too small.  %zu", receiveLen);
-    }
-    return _env->NewStringUTF(buf);
-}
-
-static jint
-nContextGetUserMessage(JNIEnv *_env, jobject _this, jlong con, jintArray data)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nContextGetMessage, con(%p), len(%i)", (RsContext)con, len);
-    jint *ptr = _env->GetIntArrayElements(data, NULL);
-    size_t receiveLen;
-    uint32_t subID;
-    int id = dispatchTab.ContextGetMessage((RsContext)con,
-                                           ptr, len * 4,
-                                           &receiveLen, sizeof(receiveLen),
-                                           &subID, sizeof(subID));
-    if (!id && receiveLen) {
-        //        __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,
-        //            "message receive buffer too small.  %zu", receiveLen);
-    }
-    _env->ReleaseIntArrayElements(data, ptr, 0);
-    return (jint)id;
-}
-
-static jint
-nContextPeekMessage(JNIEnv *_env, jobject _this, jlong con, jintArray auxData)
-{
-    LOG_API("nContextPeekMessage, con(%p)", (RsContext)con);
-    jint *auxDataPtr = _env->GetIntArrayElements(auxData, NULL);
-    size_t receiveLen;
-    uint32_t subID;
-    int id = dispatchTab.ContextPeekMessage((RsContext)con, &receiveLen, sizeof(receiveLen),
-                                  &subID, sizeof(subID));
-    auxDataPtr[0] = (jint)subID;
-    auxDataPtr[1] = (jint)receiveLen;
-    _env->ReleaseIntArrayElements(auxData, auxDataPtr, 0);
-    return (jint)id;
-}
-
-static void nContextInitToClient(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextInitToClient, con(%p)", (RsContext)con);
-    dispatchTab.ContextInitToClient((RsContext)con);
-}
-
-static void nContextDeinitToClient(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextDeinitToClient, con(%p)", (RsContext)con);
-    dispatchTab.ContextDeinitToClient((RsContext)con);
-}
-
-static void
-nContextSendMessage(JNIEnv *_env, jobject _this, jlong con, jint id, jintArray data)
-{
-    jint *ptr = NULL;
-    jint len = 0;
-    if (data) {
-        len = _env->GetArrayLength(data);
-        ptr = _env->GetIntArrayElements(data, NULL);
-    }
-    LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
-    dispatchTab.ContextSendMessage((RsContext)con, id, (const uint8_t *)ptr, len * sizeof(int));
-    if (data) {
-        _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
-    }
-}
-
-
-
-static jlong
-nElementCreate(JNIEnv *_env, jobject _this, jlong con, jlong type, jint kind,
-               jboolean norm, jint size)
-{
-    LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", (RsContext)con,
-            type, kind, norm, size);
-    return (jlong)(uintptr_t)dispatchTab.ElementCreate((RsContext)con,
-                                                       (RsDataType)type,
-                                                       (RsDataKind)kind,
-                                                       norm, size);
-}
-
-static jlong
-nElementCreate2(JNIEnv *_env, jobject _this, jlong con,
-                jlongArray _ids, jobjectArray _names, jintArray _arraySizes)
-{
-    int fieldCount = _env->GetArrayLength(_ids);
-    LOG_API("nElementCreate2, con(%p)", (RsContext)con);
-
-    jlong *jIds = _env->GetLongArrayElements(_ids, NULL);
-    jint *jArraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
-
-    RsElement *ids = (RsElement*)malloc(fieldCount * sizeof(RsElement));
-    uint32_t *arraySizes = (uint32_t *)malloc(fieldCount * sizeof(uint32_t));
-
-    for(int i = 0; i < fieldCount; i ++) {
-        ids[i] = (RsElement)jIds[i];
-        arraySizes[i] = (uint32_t)jArraySizes[i];
-    }
-
-    AutoJavaStringArrayToUTF8 names(_env, _names, fieldCount);
-
-    const char **nameArray = names.c_str();
-    size_t *sizeArray = names.c_str_len();
-
-    jlong id = (jlong)(uintptr_t)dispatchTab.ElementCreate2((RsContext)con, (RsElement *)ids,
-                                                            fieldCount, nameArray,
-                                                            fieldCount * sizeof(size_t),  sizeArray,
-                                                            (const uint32_t *)arraySizes, fieldCount);
-
-    free(ids);
-    free(arraySizes);
-    _env->ReleaseLongArrayElements(_ids, jIds, JNI_ABORT);
-    _env->ReleaseIntArrayElements(_arraySizes, jArraySizes, JNI_ABORT);
-    return id;
-}
-
-
-
-
-static void
-nElementGetSubElements(JNIEnv *_env, jobject _this, jlong con, jlong id,
-                       jlongArray _IDs,
-                       jobjectArray _names,
-                       jintArray _arraySizes)
-{
-    uint32_t dataSize = _env->GetArrayLength(_IDs);
-    LOG_API("nElementGetSubElements, con(%p)", (RsContext)con);
-
-    uintptr_t *ids = (uintptr_t *)malloc(dataSize * sizeof(uintptr_t));
-    const char **names = (const char **)malloc((uint32_t)dataSize * sizeof(const char *));
-    uint32_t *arraySizes = (uint32_t *)malloc((uint32_t)dataSize * sizeof(uint32_t));
-
-    dispatchTab.ElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes,
-                                      (uint32_t)dataSize);
-
-    for(uint32_t i = 0; i < dataSize; i++) {
-        const jlong id = (jlong)(uintptr_t)ids[i];
-        const jint arraySize = (jint)arraySizes[i];
-        _env->SetObjectArrayElement(_names, i, _env->NewStringUTF(names[i]));
-        _env->SetLongArrayRegion(_IDs, i, 1, &id);
-        _env->SetIntArrayRegion(_arraySizes, i, 1, &arraySize);
-    }
-
-    free(ids);
-    free(names);
-    free(arraySizes);
-}
-
-// -----------------------------------
-
-static jlong
-nTypeCreate(JNIEnv *_env, jobject _this, jlong con, jlong eid,
-            jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces, jint yuv)
-{
-    LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i), yuv(%i)",
-            (RsContext)con, eid, dimx, dimy, dimz, mips, faces, yuv);
-
-    return (jlong)(uintptr_t)dispatchTab.TypeCreate((RsContext)con, (RsElement)eid, dimx, dimy,
-                                                    dimz, mips, faces, yuv);
-}
-
-// -----------------------------------
-
-static jlong
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mips, jint usage,
-                       jlong pointer)
-{
-    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
-            (RsContext)con, (RsElement)type, mips, usage, (void *)pointer);
-    return (jlong)(uintptr_t) dispatchTab.AllocationCreateTyped((RsContext)con, (RsType)type,
-                                                                (RsAllocationMipmapControl)mips,
-                                                                (uint32_t)usage, (uintptr_t)pointer);
-}
-
-static void
-nAllocationSyncAll(JNIEnv *_env, jobject _this, jlong con, jlong a, jint bits)
-{
-    LOG_API("nAllocationSyncAll, con(%p), a(%p), bits(0x%08x)", (RsContext)con, (RsAllocation)a, bits);
-    dispatchTab.AllocationSyncAll((RsContext)con, (RsAllocation)a, (RsAllocationUsageType)bits);
-}
-
-static void
-nAllocationSetSurface(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject sur)
-{
-    ioDispatch.sAllocationSetSurface(_env, _this, (RsContext)con, (RsAllocation)alloc, sur, dispatchTab);
-}
-
-static void
-nAllocationIoSend(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
-{
-    dispatchTab.AllocationIoSend((RsContext)con, (RsAllocation)alloc);
-}
-
-static void
-nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
-{
-    LOG_API("nAllocationGenerateMipmaps, con(%p), a(%p)", (RsContext)con, (RsAllocation)alloc);
-    dispatchTab.AllocationGenerateMipmaps((RsContext)con, (RsAllocation)alloc);
-}
-
-static size_t GetBitmapSize(JNIEnv *env, jobject jbitmap) {
-    AndroidBitmapInfo info;
-    memset(&info, 0, sizeof(info));
-    AndroidBitmap_getInfo(env, jbitmap, &info);
-    size_t s = info.width * info.height;
-    switch (info.format) {
-        case ANDROID_BITMAP_FORMAT_RGBA_8888: s *= 4; break;
-        case ANDROID_BITMAP_FORMAT_RGB_565: s *= 2; break;
-        case ANDROID_BITMAP_FORMAT_RGBA_4444: s *= 2; break;
-    }
-    return s;
-}
-
-static jlong
-nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, jint mip,
-                            jobject jbitmap, jint usage)
-{
-    jlong id = 0;
-    void *pixels = NULL;
-    AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
-
-    if (pixels != NULL) {
-        id = (jlong)(uintptr_t)dispatchTab.AllocationCreateFromBitmap((RsContext)con,
-                                                                      (RsType)type,
-                                                                      (RsAllocationMipmapControl)mip,
-                                                                      pixels,
-                                                                      GetBitmapSize(_env, jbitmap),
-                                                                      usage);
-        AndroidBitmap_unlockPixels(_env, jbitmap);
-    }
-    return id;
-}
-
-static jlong
-nAllocationCreateBitmapBackedAllocation(JNIEnv *_env, jobject _this, jlong con, jlong type,
-                                        jint mip, jobject jbitmap, jint usage)
-{
-    jlong id = 0;
-    void *pixels = NULL;
-    AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
-
-    if (pixels != NULL) {
-        id = (jlong)(uintptr_t)dispatchTab.AllocationCreateTyped((RsContext)con,
-                                                                 (RsType)type,
-                                                                 (RsAllocationMipmapControl)mip,
-                                                                 (uint32_t)usage,
-                                                                 (uintptr_t)pixels);
-        AndroidBitmap_unlockPixels(_env, jbitmap);
-    }
-    return id;
-}
-
-static jlong
-nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type,
-                                jint mip, jobject jbitmap, jint usage)
-{
-    void *pixels = NULL;
-    AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
-
-    jlong id = 0;
-    if (pixels != NULL) {
-        id = (jlong)(uintptr_t)dispatchTab.AllocationCubeCreateFromBitmap((RsContext)con,
-                                                                          (RsType)type,
-                                                                          (RsAllocationMipmapControl)mip,
-                                                                          pixels,
-                                                                          GetBitmapSize(_env, jbitmap),
-                                                                          usage);
-        AndroidBitmap_unlockPixels(_env, jbitmap);
-    }
-    return id;
-}
-
-static void
-nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
-{
-    AndroidBitmapInfo info;
-    memset(&info, 0, sizeof(info));
-    AndroidBitmap_getInfo(_env, jbitmap, &info);
-
-    void *pixels = NULL;
-    AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
-
-    if (pixels != NULL) {
-        dispatchTab.Allocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0, 0,
-                                     RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, info.width,
-                                     info.height, pixels, GetBitmapSize(_env, jbitmap), 0);
-        AndroidBitmap_unlockPixels(_env, jbitmap);
-    }
-}
-
-static void
-nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jobject jbitmap)
-{
-    AndroidBitmapInfo info;
-    memset(&info, 0, sizeof(info));
-    AndroidBitmap_getInfo(_env, jbitmap, &info);
-
-    void *pixels = NULL;
-    AndroidBitmap_lockPixels(_env, jbitmap, &pixels);
-
-    if (pixels != NULL) {
-        dispatchTab.AllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, pixels,
-                                           GetBitmapSize(_env, jbitmap));
-        AndroidBitmap_unlockPixels(_env, jbitmap);
-    }
-    //bitmap.notifyPixelsChanged();
-}
-
-// Copies from the Java object data into the Allocation pointed to by _alloc.
-static void
-nAllocationData1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
-                  jint count, jobject data, jint sizeBytes, jint dataType, jint mSize,
-                  jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    LOG_API("nAllocation1DData, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), "
-            "dataType(%i)", (RsContext)con, (RsAllocation)alloc, offset, count, sizeBytes,
-            dataType);
-    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation1DData, true,
-                   (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
-}
-
-
-static void
-nAllocationElementData1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint xoff,
-                         jint lod, jint compIdx, jbyteArray data, jint sizeBytes)
-{
-    LOG_API("nAllocationElementData1D, con(%p), alloc(%p), xoff(%i), comp(%i), len(%i), "
-            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, compIdx,
-            _env->GetArrayLength(data),
-            sizeBytes);
-    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
-    dispatchTab.Allocation1DElementData((RsContext)con, (RsAllocation)alloc, xoff,
-                                        lod, ptr, sizeBytes, compIdx);
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-
-/*
-static void
-nAllocationElementData(JNIEnv *_env, jobject _this, jlong con, jlong alloc,
-                       jint xoff, jint yoff, jint zoff,
-                       jint lod, jint compIdx, jbyteArray data, jint sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationElementData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), comp(%i), len(%i), "
-            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, compIdx, len,
-            sizeBytes);
-    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
-    dispatchTab.AllocationElementData((RsContext)con, (RsAllocation)alloc,
-                                      xoff, yoff, zoff,
-                                      lod, ptr, sizeBytes, compIdx);
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-*/
-
-// Copies from the Java object data into the Allocation pointed to by _alloc.
-static void
-nAllocationData2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
-                  jint w, jint h, jobject data, jint sizeBytes, jint dataType, jint mSize,
-                  jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
-    LOG_API("nAllocation2DData, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) "
-            "type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
-    int count = w * h;
-    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation2DData, true,
-                   (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
-}
-
-static void
-nAllocationData2D_alloc(JNIEnv *_env, jobject _this, jlong con,
-                        jlong dstAlloc, jint dstXoff, jint dstYoff,
-                        jint dstMip, jint dstFace,
-                        jint width, jint height,
-                        jlong srcAlloc, jint srcXoff, jint srcYoff,
-                        jint srcMip, jint srcFace)
-{
-    LOG_API("nAllocation2DData_s, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
-            " dstMip(%i), dstFace(%i), width(%i), height(%i),"
-            " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i), srcFace(%i)",
-            (RsContext)con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
-            width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
-
-    dispatchTab.AllocationCopy2DRange((RsContext)con,
-                                      (RsAllocation)dstAlloc,
-                                      dstXoff, dstYoff,
-                                      dstMip, dstFace,
-                                      width, height,
-                                      (RsAllocation)srcAlloc,
-                                      srcXoff, srcYoff,
-                                      srcMip, srcFace);
-}
-
-// Copies from the Java object data into the Allocation pointed to by _alloc.
-static void
-nAllocationData3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                  jint w, jint h, jint d, jobject data, jint sizeBytes, jint dataType,
-                  jint mSize, jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    LOG_API("nAllocation3DData, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i),"
-            " h(%i), d(%i), sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff,
-            lod, w, h, d, sizeBytes);
-    int count = w * h * d;
-    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation3DData, true,
-                   (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-}
-
-static void
-nAllocationData3D_alloc(JNIEnv *_env, jobject _this, jlong con,
-                        jlong dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
-                        jint dstMip,
-                        jint width, jint height, jint depth,
-                        jlong srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
-                        jint srcMip)
-{
-    LOG_API("nAllocationData3D_alloc, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
-            " dstMip(%i), width(%i), height(%i),"
-            " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i)",
-            (RsContext)con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
-            width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
-
-    dispatchTab.AllocationCopy3DRange((RsContext)con,
-                                      (RsAllocation)dstAlloc,
-                                      dstXoff, dstYoff, dstZoff, dstMip,
-                                      width, height, depth,
-                                      (RsAllocation)srcAlloc,
-                                      srcXoff, srcYoff, srcZoff, srcMip);
-}
-
-// Copies from the Allocation pointed to by _alloc into the Java object data.
-static void
-nAllocationRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jobject data, jint dataType,
-                jint mSize, jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    LOG_API("nAllocationRead, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
-    int count = 0;
-    PER_ARRAY_TYPE(0, dispatchTab.AllocationRead, false,
-                   (RsContext)con, alloc, ptr, len * typeBytes);
-}
-
-// Copies from the Allocation pointed to by _alloc into the Java object data.
-static void
-nAllocationRead1D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint offset, jint lod,
-                  jint count, jobject data, jint sizeBytes, jint dataType,
-                  jint mSize, jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    LOG_API("nAllocation1DRead, con(%p), adapter(%p), offset(%i), count(%i), sizeBytes(%i), "
-              "dataType(%i)", (RsContext)con, alloc, offset, count, sizeBytes, dataType);
-    PER_ARRAY_TYPE(0, dispatchTab.Allocation1DRead, false,
-                   (RsContext)con, alloc, offset, lod, count, ptr, sizeBytes);
-}
-
-// Copies from the Element in the Allocation pointed to by _alloc into the Java array data.
-/*
-static void
-nAllocationElementRead(JNIEnv *_env, jobject _this, jlong con, jlong _alloc,
-                       jint xoff, jint yoff, jint zoff,
-                       jint lod, jint compIdx, jobject data, jint sizeBytes)
-{
-    jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationElementRead, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), comp(%i), len(%i), "
-            "sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff, compIdx, len,
-            sizeBytes);
-    jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
-    dispatchTab.AllocationElementRead((RsContext)con, (RsAllocation)alloc,
-                                      xoff, yoff, zoff,
-                                      lod, ptr, sizeBytes, compIdx);
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-*/
-
-// Copies from the Allocation pointed to by _alloc into the Java object data.
-static void
-nAllocationRead2D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint lod, jint _face,
-                  jint w, jint h, jobject data, jint sizeBytes, jint dataType,
-                  jint mSize, jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    RsAllocationCubemapFace face = (RsAllocationCubemapFace)_face;
-    LOG_API("nAllocation2DRead, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i) "
-              "type(%i)", (RsContext)con, alloc, xoff, yoff, w, h, sizeBytes, dataType);
-    int count = w * h;
-    PER_ARRAY_TYPE(0, dispatchTab.Allocation2DRead, false,
-                   (RsContext)con, alloc, xoff, yoff, lod, face, w, h, ptr, sizeBytes, 0);
-}
-
-// Copies from the Allocation pointed to by _alloc into the Java object data.
-/*
-static void
-nAllocationRead3D(JNIEnv *_env, jobject _this, jlong con, jlong _alloc, jint xoff, jint yoff, jint zoff, jint lod,
-                  jint w, jint h, jint d, jobject data, int sizeBytes, int dataType,
-                  jint mSize, jboolean usePadding)
-{
-    RsAllocation *alloc = (RsAllocation *)_alloc;
-    LOG_API("nAllocation3DRead, con(%p), alloc(%p), xoff(%i), yoff(%i), zoff(%i), lod(%i), w(%i),"
-            " h(%i), d(%i), sizeBytes(%i)", (RsContext)con, (RsAllocation)alloc, xoff, yoff, zoff,
-            lod, w, h, d, sizeBytes);
-    int count = w * h * d;
-    PER_ARRAY_TYPE(nullptr, dispatchTab.Allocation3DRead, false,
-                   (RsContext)con, alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
-}
-*/
-
-static jlong
-nAllocationGetType(JNIEnv *_env, jobject _this, jlong con, jlong a)
-{
-    LOG_API("nAllocationGetType, con(%p), a(%p)", (RsContext)con, (RsAllocation)a);
-    return (jlong)(uintptr_t) dispatchTab.AllocationGetType((RsContext)con, (RsAllocation)a);
-}
-
-static void
-nAllocationResize1D(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint dimX)
-{
-    LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i)", (RsContext)con,
-            (RsAllocation)alloc, dimX);
-    dispatchTab.AllocationResize1D((RsContext)con, (RsAllocation)alloc, dimX);
-}
-
-// -----------------------------------
-
-static void
-nScriptBindAllocation(JNIEnv *_env, jobject _this, jlong con, jlong script, jlong alloc, jint slot, jboolean mUseInc)
-{
-    LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)",
-            (RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
-    if (mUseInc) {
-        dispatchTabInc.ScriptBindAllocation((RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
-    } else {
-        dispatchTab.ScriptBindAllocation((RsContext)con, (RsScript)script, (RsAllocation)alloc, slot);
-    }
-}
-
-static void
-nScriptSetVarI(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jint val, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i)", (RsContext)con,
-            (void *)script, slot, val);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarI((RsContext)con, (RsScript)script, slot, val);
-    } else {
-        dispatchTab.ScriptSetVarI((RsContext)con, (RsScript)script, slot, val);
-    }
-}
-
-static void
-nScriptSetVarObj(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jlong val, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarObj, con(%p), s(%p), slot(%i), val(%i)", (RsContext)con,
-            (void *)script, slot, val);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarObj((RsContext)con, (RsScript)script, slot, (RsObjectBase)val);
-    } else {
-        dispatchTab.ScriptSetVarObj((RsContext)con, (RsScript)script, slot, (RsObjectBase)val);
-    }
-}
-
-static void
-nScriptSetVarJ(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jlong val, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarJ, con(%p), s(%p), slot(%i), val(%lli)", (RsContext)con,
-            (void *)script, slot, val);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarJ((RsContext)con, (RsScript)script, slot, val);
-    } else {
-        dispatchTab.ScriptSetVarJ((RsContext)con, (RsScript)script, slot, val);
-    }
-}
-
-static void
-nScriptSetVarF(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, float val, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarF, con(%p), s(%p), slot(%i), val(%f)", (RsContext)con,
-            (void *)script, slot, val);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarF((RsContext)con, (RsScript)script, slot, val);
-    } else {
-        dispatchTab.ScriptSetVarF((RsContext)con, (RsScript)script, slot, val);
-    }
-}
-
-static void
-nScriptSetVarD(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, double val, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarD, con(%p), s(%p), slot(%i), val(%lf)", (RsContext)con,
-            (void *)script, slot, val);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
-    } else {
-        dispatchTab.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
-    }
-}
-
-static void
-nScriptSetVarV(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    jint len = _env->GetArrayLength(data);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
-    } else {
-        dispatchTab.ScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
-    }
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nScriptSetVarVE(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data,
-                jlong elem, jintArray dims, jboolean mUseInc)
-{
-    LOG_API("nScriptSetVarVE, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    jint len = _env->GetArrayLength(data);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    jint dimsLen = _env->GetArrayLength(dims) * sizeof(int);
-    jint *dimsPtr = _env->GetIntArrayElements(dims, NULL);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
-                                      (const uint32_t *)dimsPtr, dimsLen);
-    } else {
-        dispatchTab.ScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
-                                   (const uint32_t *)dimsPtr, dimsLen);
-    }
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-    _env->ReleaseIntArrayElements(dims, dimsPtr, JNI_ABORT);
-}
-
-
-static void
-nScriptSetTimeZone(JNIEnv *_env, jobject _this, jlong con, jlong script, jbyteArray timeZone, jboolean mUseInc)
-{
-    LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", (RsContext)con,
-            (void *)script, (const char *)timeZone);
-
-    jint length = _env->GetArrayLength(timeZone);
-    jbyte* timeZone_ptr;
-    timeZone_ptr = (jbyte *) _env->GetPrimitiveArrayCritical(timeZone, (jboolean *)0);
-    if (mUseInc) {
-        dispatchTabInc.ScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
-    } else {
-        dispatchTab.ScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
-    }
-
-    if (timeZone_ptr) {
-        _env->ReleasePrimitiveArrayCritical(timeZone, timeZone_ptr, 0);
-    }
-}
-
-static void
-nScriptInvoke(JNIEnv *_env, jobject _this, jlong con, jlong obj, jint slot, jboolean mUseInc)
-{
-    LOG_API("nScriptInvoke, con(%p), script(%p)", (RsContext)con, (void *)obj);
-    if (mUseInc) {
-        dispatchTabInc.ScriptInvoke((RsContext)con, (RsScript)obj, slot);
-    } else {
-        dispatchTab.ScriptInvoke((RsContext)con, (RsScript)obj, slot);
-    }
-}
-
-static void
-nScriptInvokeV(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot, jbyteArray data, jboolean mUseInc)
-{
-    LOG_API("nScriptInvokeV, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    jint len = _env->GetArrayLength(data);
-    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
-    if (mUseInc) {
-        dispatchTabInc.ScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
-    } else {
-        dispatchTab.ScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
-    }
-    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
-}
-
-static void
-nScriptForEach(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
-               jlong script, jint slot, jlong ain, jlong aout, jboolean mUseInc)
-{
-    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
-                                     (RsAllocation)ain, (RsAllocation)aout,
-                                     NULL, 0, NULL, 0);
-    } else {
-        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
-                                  (RsAllocation)ain, (RsAllocation)aout,
-                                  NULL, 0, NULL, 0);
-    }
-}
-
-static void
-nScriptForEachV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
-                jlong script, jint slot, jlong ain, jlong aout, jbyteArray params, jboolean mUseInc)
-{
-    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    jint len = _env->GetArrayLength(params);
-    jbyte *ptr = _env->GetByteArrayElements(params, NULL);
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
-                                     (RsAllocation)ain, (RsAllocation)aout,
-                                     ptr, len, NULL, 0);
-    } else {
-        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
-                                  (RsAllocation)ain, (RsAllocation)aout,
-                                  ptr, len, NULL, 0);
-    }
-    _env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
-}
-
-static void
-nScriptForEachClipped(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
-                      jlong script, jint slot, jlong ain, jlong aout,
-                      jint xstart, jint xend,
-                      jint ystart, jint yend, jint zstart, jint zend, jboolean mUseInc)
-{
-    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    RsScriptCall sc;
-    sc.xStart = xstart;
-    sc.xEnd = xend;
-    sc.yStart = ystart;
-    sc.yEnd = yend;
-    sc.zStart = zstart;
-    sc.zEnd = zend;
-    sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
-    sc.arrayStart = 0;
-    sc.arrayEnd = 0;
-    sc.array2Start = 0;
-    sc.array2End = 0;
-    sc.array3Start = 0;
-    sc.array3End = 0;
-    sc.array4Start = 0;
-    sc.array4End = 0;
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
-                                     (RsAllocation)ain, (RsAllocation)aout,
-                                     NULL, 0, &sc, sizeof(sc));
-    } else {
-        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
-                                  (RsAllocation)ain, (RsAllocation)aout,
-                                  NULL, 0, &sc, sizeof(sc));
-    }
-}
-
-static void
-nScriptForEachClippedV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
-                       jlong script, jint slot, jlong ain, jlong aout,
-                       jbyteArray params, jint xstart, jint xend,
-                       jint ystart, jint yend, jint zstart, jint zend, jboolean mUseInc)
-{
-    LOG_API("nScriptForEachClipped, con(%p), s(%p), slot(%i)", (RsContext)con, (void *)script, slot);
-    jint len = _env->GetArrayLength(params);
-    jbyte *ptr = _env->GetByteArrayElements(params, NULL);
-    RsScriptCall sc;
-    sc.xStart = xstart;
-    sc.xEnd = xend;
-    sc.yStart = ystart;
-    sc.yEnd = yend;
-    sc.zStart = zstart;
-    sc.zEnd = zend;
-    sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
-    sc.arrayStart = 0;
-    sc.arrayEnd = 0;
-    sc.array2Start = 0;
-    sc.array2End = 0;
-    sc.array3Start = 0;
-    sc.array3End = 0;
-    sc.array4Start = 0;
-    sc.array4End = 0;
-    if (mUseInc) {
-        dispatchTab.ContextFinish((RsContext)con);
-        dispatchTabInc.ScriptForEach((RsContext)incCon, (RsScript)script, slot,
-                                     (RsAllocation)ain, (RsAllocation)aout,
-                                     ptr, len, &sc, sizeof(sc));
-    } else {
-        dispatchTab.ScriptForEach((RsContext)con, (RsScript)script, slot,
-                                  (RsAllocation)ain, (RsAllocation)aout,
-                                  ptr, len, &sc, sizeof(sc));
-    }
-    _env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
-}
-
-static void
-nScriptForEachMulti(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
-                    jlongArray ains, jlong aout, jbyteArray params,
-                    jintArray limits)
-{
-    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
-
-    jint   in_len = 0;
-    jlong *in_ptr = nullptr;
-
-    RsAllocation *in_allocs = nullptr;
-
-    if (ains != nullptr) {
-        in_len = _env->GetArrayLength(ains);
-        if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) {
-            LOG_ERR("Too many arguments in kernel launch.");
-            // TODO (b/20758983): Report back to Java and throw an exception
-            return;
-        }
-
-        // TODO (b/20760800): Check in_ptr is not null
-        in_ptr = _env->GetLongArrayElements(ains, nullptr);
-        if (sizeof(RsAllocation) == sizeof(jlong)) {
-            in_allocs = (RsAllocation*)in_ptr;
-
-        } else {
-            // Convert from 64-bit jlong types to the native pointer type.
-
-            in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
-            if (in_allocs == nullptr) {
-                LOG_ERR("Failed launching kernel for lack of memory.");
-                _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-                return;
-            }
-
-            for (int index = in_len; --index >= 0;) {
-                in_allocs[index] = (RsAllocation)in_ptr[index];
-            }
-        }
-    }
-
-    jint   param_len = 0;
-    jbyte *param_ptr = nullptr;
-
-    if (params != nullptr) {
-        param_len = _env->GetArrayLength(params);
-        param_ptr = _env->GetByteArrayElements(params, nullptr);
-    }
-
-    RsScriptCall sc, *sca = nullptr;
-    uint32_t sc_size = 0;
-
-    jint  limit_len = 0;
-    jint *limit_ptr = nullptr;
-
-    if (limits != nullptr) {
-        limit_len = _env->GetArrayLength(limits);
-        limit_ptr = _env->GetIntArrayElements(limits, nullptr);
-
-        if (limit_len != 6) {
-            LOG_ERR("LaunchOptions cannot be recognized.");
-            goto exit;
-        }
-
-        sc.xStart     = limit_ptr[0];
-        sc.xEnd       = limit_ptr[1];
-        sc.yStart     = limit_ptr[2];
-        sc.yEnd       = limit_ptr[3];
-        sc.zStart     = limit_ptr[4];
-        sc.zEnd       = limit_ptr[5];
-        sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
-        sc.arrayStart = 0;
-        sc.arrayEnd = 0;
-        sc.array2Start = 0;
-        sc.array2End = 0;
-        sc.array3Start = 0;
-        sc.array3End = 0;
-        sc.array4Start = 0;
-        sc.array4End = 0;
-
-        sca = &sc;
-    }
-
-    dispatchTabInc.ScriptForEachMulti((RsContext)con, (RsScript)script, slot,
-                                      in_allocs, in_len, (RsAllocation)aout,
-                                      param_ptr, param_len, sca, sc_size);
-
-exit:
-
-    if (ains != nullptr) {
-        _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-    }
-
-    if (params != nullptr) {
-        _env->ReleaseByteArrayElements(params, param_ptr, JNI_ABORT);
-    }
-
-    if (limits != nullptr) {
-        _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
-    }
-}
-
-static void
-nScriptReduce(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
-              jlongArray ains, jlong aout, jintArray limits)
-{
-    LOG_API("nScriptReduce, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
-
-    if (ains == nullptr) {
-        LOG_ERR("At least one input required.");
-        // TODO (b/20758983): Report back to Java and throw an exception
-        return;
-    }
-    jint in_len = _env->GetArrayLength(ains);
-    if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) {
-        LOG_ERR("Too many arguments in kernel launch.");
-        // TODO (b/20758983): Report back to Java and throw an exception
-        return;
-    }
-
-    jlong *in_ptr = _env->GetLongArrayElements(ains, nullptr);
-    if (in_ptr == nullptr) {
-        LOG_ERR("Failed to get Java array elements");
-        // TODO (b/20758983): Report back to Java and throw an exception
-        return;
-    }
-
-    RsAllocation *in_allocs = nullptr;
-    if (sizeof(RsAllocation) == sizeof(jlong)) {
-        in_allocs = (RsAllocation*)in_ptr;
-    } else {
-        // Convert from 64-bit jlong types to the native pointer type.
-
-        in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
-        if (in_allocs == nullptr) {
-            LOG_ERR("Failed launching kernel for lack of memory.");
-            // TODO (b/20758983): Report back to Java and throw an exception
-            _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-            return;
-        }
-
-        for (int index = in_len; --index >= 0;) {
-            in_allocs[index] = (RsAllocation)in_ptr[index];
-        }
-    }
-
-    RsScriptCall sc, *sca = nullptr;
-    uint32_t sc_size = 0;
-
-    jint  limit_len = 0;
-    jint *limit_ptr = nullptr;
-
-    if (limits != nullptr) {
-        limit_len = _env->GetArrayLength(limits);
-        limit_ptr = _env->GetIntArrayElements(limits, nullptr);
-        if (limit_ptr == nullptr) {
-            LOG_ERR("Failed to get Java array elements");
-            // TODO (b/20758983): Report back to Java and throw an exception
-            _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-            return;
-        }
-
-        if (limit_len != 6) {
-            LOG_ERR("LaunchOptions cannot be recognized");
-            // TODO (b/20758983): Report back to Java and throw an exception
-            _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-            return;
-        }
-
-        sc.xStart     = limit_ptr[0];
-        sc.xEnd       = limit_ptr[1];
-        sc.yStart     = limit_ptr[2];
-        sc.yEnd       = limit_ptr[3];
-        sc.zStart     = limit_ptr[4];
-        sc.zEnd       = limit_ptr[5];
-        sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
-        sc.arrayStart = 0;
-        sc.arrayEnd = 0;
-        sc.array2Start = 0;
-        sc.array2End = 0;
-        sc.array3Start = 0;
-        sc.array3End = 0;
-        sc.array4Start = 0;
-        sc.array4End = 0;
-
-        sca = &sc;
-        sc_size = sizeof(sc);
-    }
-
-    dispatchTab.ScriptReduce((RsContext)con, (RsScript)script, slot,
-                             in_allocs, in_len, (RsAllocation)aout,
-                             sca, sc_size);
-
-    _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
-
-    if (limits != nullptr) {
-        _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
-    }
-}
-
-// -----------------------------------
-
-static jlong
-nScriptCCreate(JNIEnv *_env, jobject _this, jlong con,
-               jstring resName, jstring cacheDir,
-               jbyteArray scriptRef, jint length)
-{
-    LOG_API("nScriptCCreate, con(%p)", (RsContext)con);
-
-    AutoJavaStringToUTF8 resNameUTF(_env, resName);
-    AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
-    jlong ret = 0;
-    jbyte* script_ptr = NULL;
-    jint _exception = 0;
-    jint remaining;
-    if (!scriptRef) {
-        _exception = 1;
-        //jniThrowException(_env, "java/lang/IllegalArgumentException", "script == null");
-        goto exit;
-    }
-    if (length < 0) {
-        _exception = 1;
-        //jniThrowException(_env, "java/lang/IllegalArgumentException", "length < 0");
-        goto exit;
-    }
-    remaining = _env->GetArrayLength(scriptRef);
-    if (remaining < length) {
-        _exception = 1;
-        //jniThrowException(_env, "java/lang/IllegalArgumentException",
-        //        "length > script.length - offset");
-        goto exit;
-    }
-    script_ptr = (jbyte *)
-        _env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
-
-    //rsScriptCSetText(con, (const char *)script_ptr, length);
-
-    ret = (jlong)(uintptr_t)dispatchTab.ScriptCCreate((RsContext)con,
-                                                      resNameUTF.c_str(), resNameUTF.length(),
-                                                      cacheDirUTF.c_str(), cacheDirUTF.length(),
-                                                      (const char *)script_ptr, length);
-
-exit:
-    if (script_ptr) {
-        _env->ReleasePrimitiveArrayCritical(scriptRef, script_ptr,
-                _exception ? JNI_ABORT: 0);
-    }
-
-    return (jlong)(uintptr_t)ret;
-}
-
-static jlong
-nScriptIntrinsicCreate(JNIEnv *_env, jobject _this, jlong con, jint id, jlong eid, jboolean mUseInc)
-{
-    LOG_API("nScriptIntrinsicCreate, con(%p) id(%i) element(%p)", (RsContext)con, id, (void *)eid);
-    if (mUseInc) {
-        return (jlong)(uintptr_t)dispatchTabInc.ScriptIntrinsicCreate((RsContext)con, id, (RsElement)eid);
-    } else {
-        return (jlong)(uintptr_t)dispatchTab.ScriptIntrinsicCreate((RsContext)con, id, (RsElement)eid);
-    }
-}
-
-static jlong
-nScriptKernelIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot, jint sig, jboolean mUseInc)
-{
-    LOG_API("nScriptKernelIDCreate, con(%p) script(%p), slot(%i), sig(%i)", (RsContext)con,
-            (void *)sid, slot, sig);
-    if (mUseInc) {
-        return (jlong)(uintptr_t)dispatchTabInc.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
-                                                                     slot, sig);
-    } else {
-        return (jlong)(uintptr_t)dispatchTab.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
-                                                                  slot, sig);
-    }
-}
-
-static jlong
-nScriptInvokeIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot)
-{
-    LOG_API("nScriptInvokeIDCreate, con(%p) script(%p), slot(%i), sig(%i)", con,
-            (void *)sid, slot);
-    return (jlong)dispatchTab.ScriptInvokeIDCreate((RsContext)con, (RsScript)sid, slot);
-}
-
-static jlong
-nScriptFieldIDCreate(JNIEnv *_env, jobject _this, jlong con, jlong sid, jint slot, jboolean mUseInc)
-{
-    LOG_API("nScriptFieldIDCreate, con(%p) script(%p), slot(%i)", (RsContext)con, (void *)sid, slot);
-    if (mUseInc) {
-        return (jlong)(uintptr_t)dispatchTabInc.ScriptFieldIDCreate((RsContext)con, (RsScript)sid, slot);
-    } else {
-        return (jlong)(uintptr_t)dispatchTab.ScriptFieldIDCreate((RsContext)con, (RsScript)sid, slot);
-    }
-}
-
-static jlong
-nScriptGroupCreate(JNIEnv *_env, jobject _this, jlong con, jlongArray _kernels, jlongArray _src,
-    jlongArray _dstk, jlongArray _dstf, jlongArray _types)
-{
-    LOG_API("nScriptGroupCreate, con(%p)", (RsContext)con);
-
-    jlong id = 0;
-
-    RsScriptKernelID* kernelsPtr;
-    jint kernelsLen = _env->GetArrayLength(_kernels);
-    jlong *jKernelsPtr = _env->GetLongArrayElements(_kernels, nullptr);
-
-    RsScriptKernelID* srcPtr;
-    jint srcLen = _env->GetArrayLength(_src);
-    jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
-
-    RsScriptKernelID* dstkPtr;
-    jint dstkLen = _env->GetArrayLength(_dstk);
-    jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
-
-    RsScriptKernelID* dstfPtr;
-    jint dstfLen = _env->GetArrayLength(_dstf);
-    jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
-
-    RsType* typesPtr;
-    jint typesLen = _env->GetArrayLength(_types);
-    jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
-
-    if (jKernelsPtr == nullptr) {
-        LOG_ERR("Failed to get Java array elements: kernels");
-        goto cleanup;
-    }
-    if (jSrcPtr == nullptr) {
-        LOG_ERR("Failed to get Java array elements: src");
-        goto cleanup;
-    }
-    if (jDstkPtr == nullptr) {
-        LOG_ERR("Failed to get Java array elements: dstk");
-        goto cleanup;
-    }
-    if (jDstfPtr == nullptr) {
-        LOG_ERR("Failed to get Java array elements: dstf");
-        goto cleanup;
-    }
-    if (jTypesPtr == nullptr) {
-        LOG_ERR("Failed to get Java array elements: types");
-        goto cleanup;
-    }
-
-    kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
-    for(int i = 0; i < kernelsLen; ++i) {
-        kernelsPtr[i] = (RsScriptKernelID)jKernelsPtr[i];
-    }
-
-    srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
-    for(int i = 0; i < srcLen; ++i) {
-        srcPtr[i] = (RsScriptKernelID)jSrcPtr[i];
-    }
-
-    dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
-    for(int i = 0; i < dstkLen; ++i) {
-        dstkPtr[i] = (RsScriptKernelID)jDstkPtr[i];
-    }
-
-    dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
-    for(int i = 0; i < dstfLen; ++i) {
-        dstfPtr[i] = (RsScriptKernelID)jDstfPtr[i];
-    }
-
-    typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
-    for(int i = 0; i < typesLen; ++i) {
-        typesPtr[i] = (RsType)jTypesPtr[i];
-    }
-
-    id = (jlong)(uintptr_t) dispatchTab.ScriptGroupCreate((RsContext)con,
-                               (RsScriptKernelID *)kernelsPtr, kernelsLen * sizeof(RsScriptKernelID),
-                               (RsScriptKernelID *)srcPtr, srcLen * sizeof(RsScriptKernelID),
-                               (RsScriptKernelID *)dstkPtr, dstkLen * sizeof(RsScriptKernelID),
-                               (RsScriptFieldID *)dstfPtr, dstfLen * sizeof(RsScriptKernelID),
-                               (RsType *)typesPtr, typesLen * sizeof(RsType));
-
-    free(kernelsPtr);
-    free(srcPtr);
-    free(dstkPtr);
-    free(dstfPtr);
-    free(typesPtr);
-
-cleanup:
-    if (jKernelsPtr != nullptr) {
-        _env->ReleaseLongArrayElements(_kernels, jKernelsPtr, 0);
-    }
-    if (jSrcPtr != nullptr) {
-        _env->ReleaseLongArrayElements(_src, jSrcPtr, 0);
-    }
-    if (jDstkPtr != nullptr) {
-        _env->ReleaseLongArrayElements(_dstk, jDstkPtr, 0);
-    }
-    if (jDstfPtr != nullptr) {
-        _env->ReleaseLongArrayElements(_dstf, jDstfPtr, 0);
-    }
-    if (jTypesPtr != nullptr) {
-        _env->ReleaseLongArrayElements(_types, jTypesPtr, 0);
-    }
-
-    return id;
-}
-
-static void
-nScriptGroupSetInput(JNIEnv *_env, jobject _this, jlong con, jlong gid, jlong kid, jlong alloc)
-{
-    LOG_API("nScriptGroupSetInput, con(%p) group(%p), kernelId(%p), alloc(%p)", (RsContext)con,
-            (void *)gid, (void *)kid, (void *)alloc);
-    dispatchTab.ScriptGroupSetInput((RsContext)con, (RsScriptGroup)gid, (RsScriptKernelID)kid,
-                                    (RsAllocation)alloc);
-}
-
-static void
-nScriptGroupSetOutput(JNIEnv *_env, jobject _this, jlong con, jlong gid, jlong kid, jlong alloc)
-{
-    LOG_API("nScriptGroupSetOutput, con(%p) group(%p), kernelId(%p), alloc(%p)", (RsContext)con,
-            (void *)gid, (void *)kid, (void *)alloc);
-    dispatchTab.ScriptGroupSetOutput((RsContext)con, (RsScriptGroup)gid, (RsScriptKernelID)kid,
-                                     (RsAllocation)alloc);
-}
-
-static void
-nScriptGroupExecute(JNIEnv *_env, jobject _this, jlong con, jlong gid)
-{
-    LOG_API("nScriptGroupSetOutput, con(%p) group(%p)", (RsContext)con, (void *)gid);
-    dispatchTab.ScriptGroupExecute((RsContext)con, (RsScriptGroup)gid);
-}
-
-// ---------------------------------------------------------------------------
-
-static jlong
-nSamplerCreate(JNIEnv *_env, jobject _this, jlong con, jint magFilter, jint minFilter,
-               jint wrapS, jint wrapT, jint wrapR, jfloat aniso)
-{
-    LOG_API("nSamplerCreate, con(%p)", (RsContext)con);
-    return (jlong)(uintptr_t)dispatchTab.SamplerCreate((RsContext)con,
-                                                       (RsSamplerValue)magFilter,
-                                                       (RsSamplerValue)minFilter,
-                                                       (RsSamplerValue)wrapS,
-                                                       (RsSamplerValue)wrapT,
-                                                       (RsSamplerValue)wrapR,
-                                                       aniso);
-}
-
-static jint
-nSystemGetPointerSize(JNIEnv *_env, jobject _this) {
-    return (jint)sizeof(void*);
-}
-
-// ---------------------------------------------------------------------------
-// For Incremental Intrinsic Support
-static jboolean nIncLoadSO(JNIEnv *_env, jobject _this, jint deviceApi, jstring libPath) {
-    void* handle = NULL;
-    // For API 9+, dlopen the full path of libRSSupport.
-    if (libPath != NULL) {
-        const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
-        handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
-        _env->ReleaseStringUTFChars(libPath, libPathJni);
-    } else {
-        handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
-    }
-
-    if (handle == NULL) {
-        LOG_ERR("couldn't dlopen %s;  librsjni version: %d", dlerror(), RS_JNI_VERSION);
-        return false;
-    }
-
-    if (loadSymbols(handle, dispatchTabInc, deviceApi) == false) {
-        LOG_ERR("Dispatch Table init failed! librsjni version: %d", RS_JNI_VERSION);
-        dlclose(handle);
-        return false;
-    }
-    dispatchTabInc.AllocationCreateStrided = (AllocationCreateStridedFnPtr)dlsym(handle, "rsAllocationCreateStrided");
-    if (dispatchTabInc.AllocationCreateStrided == NULL) {
-        LOG_ERR("Couldn't initialize dispatchTabInc.AllocationCreateStrided");
-        dlclose(handle);
-        return false;
-    }
-    LOG_API("Successfully loaded compat runtime");
-    return true;
-}
-
-// -----------------------------------
-// To create/destroy a dummy context
-static void
-nIncObjDestroy(JNIEnv *_env, jobject _this, jlong con, jlong obj)
-{
-    LOG_API("nObjDestroy, con(%p) obj(%p)", (RsContext)con, (void *)obj);
-    dispatchTabInc.ObjDestroy((RsContext)con, (void *)obj);
-}
-
-
-static jlong
-nIncDeviceCreate(JNIEnv *_env, jobject _this)
-{
-    LOG_API("nDeviceCreate");
-    return (jlong)(uintptr_t)dispatchTabInc.DeviceCreate();
-}
-
-static void
-nIncDeviceDestroy(JNIEnv *_env, jobject _this, jlong dev)
-{
-    LOG_API("nDeviceDestroy");
-    return dispatchTabInc.DeviceDestroy((RsDevice)dev);
-}
-
-static jlong
-nIncContextCreate(JNIEnv *_env, jobject _this, jlong dev, jint ver, jint sdkVer, jint ct)
-{
-    LOG_API("nContextCreate");
-    //The compat context for incremental support will be synchronous.
-    return (jlong)(uintptr_t)dispatchTabInc.ContextCreate((RsDevice)dev, ver, sdkVer,
-                                                          (RsContextType)ct,
-                                                          RS_CONTEXT_SYNCHRONOUS);
-}
-
-static void
-nIncContextFinish(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextFinish, con(%p)", (RsContext)con);
-    dispatchTabInc.ContextFinish((RsContext)con);
-}
-
-static void
-nIncContextDestroy(JNIEnv *_env, jobject _this, jlong con)
-{
-    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
-    dispatchTabInc.ContextDestroy((RsContext)con);
-}
-
-// -----------------------------------
-// Create dummy Element
-static jlong
-nIncElementCreate(JNIEnv *_env, jobject _this, jlong con, jlong type, jint kind, jboolean norm, jint size)
-{
-    LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", (RsContext)con,
-            type, kind, norm, size);
-    return (jlong)(uintptr_t)dispatchTabInc.ElementCreate((RsContext)con, (RsDataType)type,
-                                                          (RsDataKind)kind, norm, size);
-}
-// -----------------------------------
-// Create dummy Type
-static jlong
-nIncTypeCreate(JNIEnv *_env, jobject _this, jlong con, jlong eid,
-            jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces, jint yuv)
-{
-    LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i), yuv(%i)",
-            incCon, eid, dimx, dimy, dimz, mips, faces, yuv);
-
-    return (jlong)(uintptr_t)dispatchTabInc.TypeCreate((RsContext)con, (RsElement)eid, dimx, dimy,
-                                                       dimz, mips, faces, yuv);
-}
-
-// -----------------------------------
-// Create Allocation from pointer
-static jlong
-nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type, jint xBytesSize)
-{
-    LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
-            incCon, (RsElement)type, mips, usage, (void *)pointer);
-    size_t strideIn;
-    void* pIn = NULL;
-    RsAllocation ainI = NULL;
-    if (alloc != 0) {
-        pIn = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
-                                               RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
-                                               &strideIn, sizeof(size_t));
-        /*
-         * By definition stride is a roundup of xBytesSize with requiredAlignment, so requiredAlignment must
-         * be strictly larger than the difference of (stride - xBytesSize).
-         *
-         * We can prove that as long as requiredAlignment satisfies the following two conditions, the
-         * memory layout will be identical :
-         * 1. Smaller or equal than stride;
-         * 2. Larger than minRequiredAlignment.
-         *
-         * In this case we can simply choose the first power of 2 that satisfies both conditions.
-         */
-        size_t requiredAlignment = 16;
-        size_t minRequiredAlignment = strideIn - xBytesSize;
-        while (requiredAlignment <= minRequiredAlignment) {
-            requiredAlignment <<= 1;
-        }
-        ainI = dispatchTabInc.AllocationCreateStrided((RsContext)incCon, (RsType)type,
-                                                      RS_ALLOCATION_MIPMAP_NONE,
-                                                      RS_ALLOCATION_USAGE_INCREMENTAL_SUPPORT | RS_ALLOCATION_USAGE_SHARED,
-                                                      (uintptr_t)pIn, requiredAlignment);
-    }
-    return (jlong)(uintptr_t) ainI;
-}
-
-static jobject
-nAllocationGetByteBuffer(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint xBytesSize, jint dimY, jint dimZ)
-{
-    LOG_API("nAllocationGetByteBuffer, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
-    size_t strideIn = xBytesSize;
-    void* ptr = NULL;
-    if (alloc != 0 && dispatchTab.AllocationGetPointer != nullptr) {
-        ptr = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
-                                               RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, dimZ, 0,
-                                               &strideIn, sizeof(size_t));
-    }
-    if (ptr != NULL) {
-        size_t bufferSize = strideIn;
-        if (dimY > 0) {
-            bufferSize *= dimY;
-        }
-        if (dimZ > 0) {
-            bufferSize *= dimZ;
-        }
-        jobject byteBuffer = _env->NewDirectByteBuffer(ptr, (jlong) bufferSize);
-        return byteBuffer;
-    } else {
-        return NULL;
-    }
-}
-
-static jlong
-nAllocationGetStride(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
-{
-    LOG_API("nAllocationGetStride, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
-    size_t strideIn = 0;
-    if (alloc != 0 && dispatchTab.AllocationGetPointer != nullptr) {
-        dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
-                                         RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
-                                         &strideIn, sizeof(size_t));
-    }
-    return (jlong)strideIn;
-}
-
-// ---------------------------------------------------------------------------
-
-
-static const char *classPathName = "android/support/v8/renderscript/RenderScript";
-
-static JNINativeMethod methods[] = {
-{"nLoadSO",                        "(ZILjava/lang/String;)Z",                 (bool*)nLoadSO },
-{"nLoadIOSO",                      "()Z",                                     (bool*)nLoadIOSO },
-{"nDeviceCreate",                  "()J",                                     (void*)nDeviceCreate },
-{"nDeviceDestroy",                 "(J)V",                                    (void*)nDeviceDestroy },
-{"nDeviceSetConfig",               "(JII)V",                                  (void*)nDeviceSetConfig },
-{"nContextGetUserMessage",         "(J[I)I",                                  (void*)nContextGetUserMessage },
-{"nContextGetErrorMessage",        "(J)Ljava/lang/String;",                   (void*)nContextGetErrorMessage },
-{"nContextPeekMessage",            "(J[I)I",                                  (void*)nContextPeekMessage },
-{"nContextInitToClient",           "(J)V",                                    (void*)nContextInitToClient },
-{"nContextDeinitToClient",         "(J)V",                                    (void*)nContextDeinitToClient },
-
-
-// All methods below are thread protected in java.
-{"rsnContextCreate",                 "(JIIILjava/lang/String;)J",             (void*)nContextCreate },
-{"rsnContextFinish",                 "(J)V",                                  (void*)nContextFinish },
-{"rsnContextSetPriority",            "(JI)V",                                 (void*)nContextSetPriority },
-{"rsnContextDestroy",                "(J)V",                                  (void*)nContextDestroy },
-{"rsnContextDump",                   "(JI)V",                                 (void*)nContextDump },
-{"rsnContextSendMessage",            "(JI[I)V",                               (void*)nContextSendMessage },
-{"rsnClosureCreate",                 "(JJJ[J[J[I[J[J)J",                      (void*)nClosureCreate },
-{"rsnInvokeClosureCreate",           "(JJ[B[J[J[I)J",                         (void*)nInvokeClosureCreate },
-{"rsnClosureSetArg",                 "(JJIJI)V",                              (void*)nClosureSetArg },
-{"rsnClosureSetGlobal",              "(JJJJI)V",                              (void*)nClosureSetGlobal },
-{"rsnObjDestroy",                    "(JJ)V",                                 (void*)nObjDestroy },
-
-{"rsnElementCreate",                 "(JJIZI)J",                              (void*)nElementCreate },
-{"rsnElementCreate2",                "(J[J[Ljava/lang/String;[I)J",           (void*)nElementCreate2 },
-{"rsnElementGetSubElements",         "(JJ[J[Ljava/lang/String;[I)V",          (void*)nElementGetSubElements },
-
-{"rsnTypeCreate",                    "(JJIIIZZI)J",                           (void*)nTypeCreate },
-
-{"rsnAllocationCreateTyped",         "(JJIIJ)J",                              (void*)nAllocationCreateTyped },
-{"rsnAllocationCreateFromBitmap",    "(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCreateFromBitmap },
-{"rsnAllocationCreateBitmapBackedAllocation",    "(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCreateBitmapBackedAllocation },
-{"rsnAllocationCubeCreateFromBitmap","(JJILandroid/graphics/Bitmap;I)J",      (void*)nAllocationCubeCreateFromBitmap },
-
-{"rsnAllocationCopyFromBitmap",      "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyFromBitmap },
-{"rsnAllocationCopyToBitmap",        "(JJLandroid/graphics/Bitmap;)V",        (void*)nAllocationCopyToBitmap },
-
-{"rsnAllocationSyncAll",             "(JJI)V",                                (void*)nAllocationSyncAll },
-{"rsnAllocationSetSurface",          "(JJLandroid/view/Surface;)V",           (void*)nAllocationSetSurface },
-{"rsnAllocationIoSend",              "(JJ)V",                                 (void*)nAllocationIoSend },
-{"rsnAllocationData1D",              "(JJIIILjava/lang/Object;IIIZ)V",        (void*)nAllocationData1D },
-{"rsnAllocationElementData1D",       "(JJIII[BI)V",                           (void*)nAllocationElementData1D },
-//{"rsnAllocationElementData",         "(JJIIIII[BI)V",                         (void*)nAllocationElementData },
-{"rsnAllocationData2D",              "(JJIIIIIILjava/lang/Object;IIIZ)V",     (void*)nAllocationData2D },
-{"rsnAllocationData2D",              "(JJIIIIIIJIIII)V",                      (void*)nAllocationData2D_alloc },
-{"rsnAllocationData3D",              "(JJIIIIIIILjava/lang/Object;IIIZ)V",    (void*)nAllocationData3D },
-{"rsnAllocationData3D",              "(JJIIIIIIIJIIII)V",                     (void*)nAllocationData3D_alloc },
-{"rsnAllocationRead",                "(JJLjava/lang/Object;IIZ)V",            (void*)nAllocationRead },
-{"rsnAllocationRead1D",              "(JJIIILjava/lang/Object;IIIZ)V",        (void*)nAllocationRead1D },
-//{"rsnAllocationElementRead",         "(JJIIIII[BI)V",                         (void*)nAllocationElementRead },
-{"rsnAllocationRead2D",              "(JJIIIIIILjava/lang/Object;IIIZ)V",     (void*)nAllocationRead2D },
-//{"rsnAllocationRead3D",              "(JJIIIIIIILjava/lang/Object;IIIZ)V",  (void*)nAllocationRead3D },
-{"rsnAllocationGetType",             "(JJ)J",                                 (void*)nAllocationGetType},
-{"rsnAllocationResize1D",            "(JJI)V",                                (void*)nAllocationResize1D },
-{"rsnAllocationGenerateMipmaps",     "(JJ)V",                                 (void*)nAllocationGenerateMipmaps },
-
-{"rsnScriptBindAllocation",          "(JJJIZ)V",                              (void*)nScriptBindAllocation },
-{"rsnScriptSetTimeZone",             "(JJ[BZ)V",                              (void*)nScriptSetTimeZone },
-{"rsnScriptInvoke",                  "(JJIZ)V",                               (void*)nScriptInvoke },
-{"rsnScriptInvokeV",                 "(JJI[BZ)V",                             (void*)nScriptInvokeV },
-{"rsnScriptForEach",                 "(JJJIJJZ)V",                            (void*)nScriptForEach },
-{"rsnScriptForEach",                 "(JJJIJJ[BZ)V",                          (void*)nScriptForEachV },
-{"rsnScriptForEach",                 "(JJI[JJ[B[I)V",                         (void*)nScriptForEachMulti },
-{"rsnScriptForEachClipped",          "(JJJIJJIIIIIIZ)V",                      (void*)nScriptForEachClipped },
-{"rsnScriptForEachClipped",          "(JJJIJJ[BIIIIIIZ)V",                    (void*)nScriptForEachClippedV },
-{"rsnScriptReduce",                  "(JJI[JJ[I)V",                           (void*)nScriptReduce },
-{"rsnScriptSetVarI",                 "(JJIIZ)V",                              (void*)nScriptSetVarI },
-{"rsnScriptSetVarJ",                 "(JJIJZ)V",                              (void*)nScriptSetVarJ },
-{"rsnScriptSetVarF",                 "(JJIFZ)V",                              (void*)nScriptSetVarF },
-{"rsnScriptSetVarD",                 "(JJIDZ)V",                              (void*)nScriptSetVarD },
-{"rsnScriptSetVarV",                 "(JJI[BZ)V",                             (void*)nScriptSetVarV },
-{"rsnScriptSetVarVE",                "(JJI[BJ[IZ)V",                          (void*)nScriptSetVarVE },
-{"rsnScriptSetVarObj",               "(JJIJZ)V",                              (void*)nScriptSetVarObj },
-
-{"rsnScriptCCreate",                 "(JLjava/lang/String;Ljava/lang/String;[BI)J",  (void*)nScriptCCreate },
-{"rsnScriptIntrinsicCreate",         "(JIJZ)J",                               (void*)nScriptIntrinsicCreate },
-{"rsnScriptKernelIDCreate",          "(JJIIZ)J",                              (void*)nScriptKernelIDCreate },
-{"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
-{"rsnScriptFieldIDCreate",           "(JJIZ)J",                               (void*)nScriptFieldIDCreate },
-{"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create",            "(JLjava/lang/String;Ljava/lang/String;[J)J", (void*)nScriptGroup2Create },
-{"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
-{"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
-{"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },
-{"rsnScriptGroup2Execute",           "(JJ)V",                                 (void*)nScriptGroup2Execute },
-
-{"rsnScriptIntrinsicBLAS_Single",    "(JJJIIIIIIIIIFJJFJIIIIZ)V",             (void*)nScriptIntrinsicBLAS_Single },
-{"rsnScriptIntrinsicBLAS_Double",    "(JJJIIIIIIIIIDJJDJIIIIZ)V",             (void*)nScriptIntrinsicBLAS_Double },
-{"rsnScriptIntrinsicBLAS_Complex",   "(JJJIIIIIIIIIFFJJFFJIIIIZ)V",           (void*)nScriptIntrinsicBLAS_Complex },
-{"rsnScriptIntrinsicBLAS_Z",         "(JJJIIIIIIIIIDDJJDDJIIIIZ)V",           (void*)nScriptIntrinsicBLAS_Z },
-
-{"rsnScriptIntrinsicBLAS_BNNM",      "(JJJIIIJIJIJIIZ)V",                     (void*)nScriptIntrinsicBLAS_BNNM },
-
-{"rsnSamplerCreate",                 "(JIIIIIF)J",                            (void*)nSamplerCreate },
-
-{"rsnSystemGetPointerSize",          "()I",                                   (void*)nSystemGetPointerSize },
-
-// Entry points for Inc libRSSupport
-{"nIncLoadSO",                       "(ILjava/lang/String;)Z",                (bool*)nIncLoadSO },
-{"nIncDeviceCreate",                 "()J",                                   (void*)nIncDeviceCreate },
-{"nIncDeviceDestroy",                "(J)V",                                  (void*)nIncDeviceDestroy },
-{"rsnIncContextCreate",              "(JIII)J",                               (void*)nIncContextCreate },
-{"rsnIncContextFinish",              "(J)V",                                  (void*)nIncContextFinish },
-{"rsnIncContextDestroy",             "(J)V",                                  (void*)nIncContextDestroy },
-{"rsnIncObjDestroy",                 "(JJ)V",                                 (void*)nIncObjDestroy },
-{"rsnIncElementCreate",              "(JJIZI)J",                              (void*)nIncElementCreate },
-{"rsnIncTypeCreate",                 "(JJIIIZZI)J",                           (void*)nIncTypeCreate },
-{"rsnIncAllocationCreateTyped",      "(JJJJI)J",                              (void*)nIncAllocationCreateTyped },
-{"rsnAllocationGetByteBuffer",       "(JJIII)Ljava/nio/ByteBuffer;",          (void*)nAllocationGetByteBuffer },
-{"rsnAllocationGetStride",           "(JJ)J",                                 (void*)nAllocationGetStride },
-};
-
-// ---------------------------------------------------------------------------
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
-    JNIEnv* env = NULL;
-    jclass clazz = NULL;
-    jint result = -1;
-
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        //        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
-        //            "ERROR: GetEnv failed\n");
-        goto bail;
-    }
-    if (env == NULL) {
-        //        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: env == NULL");
-        goto bail;
-    }
-
-    clazz = env->FindClass(classPathName);
-    if (clazz == NULL) {
-        goto bail;
-    }
-
-    if (env->RegisterNatives(clazz, methods, NELEM(methods)) < 0) {
-        //        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
-        //            "ERROR: MediaPlayer native registration failed\n");
-        goto bail;
-    }
-
-    /* success -- return valid version number */
-    result = JNI_VERSION_1_4;
-
-bail:
-    return result;
-}
diff --git a/v8/renderscript/jni/android_rscompat_usage_io.cpp b/v8/renderscript/jni/android_rscompat_usage_io.cpp
deleted file mode 100644
index e29be1a..0000000
--- a/v8/renderscript/jni/android_rscompat_usage_io.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <android/log.h>
-#include <android/native_window.h>
-#include <android/native_window_jni.h>
-
-#include <rsEnv.h>
-#include "rsDispatch.h"
-#define LOG_API(...)
-
-extern "C" void AllocationSetSurface(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc, jobject sur, dispatchTable dispatchTab)
-{
-    LOG_API("nAllocationSetSurface, con(%p), alloc(%p), surface(%p)",
-            con, alloc, sur);
-
-    ANativeWindow* s = NULL;
-    if (sur != 0) {
-        s = ANativeWindow_fromSurface(_env, sur);
-    }
-    dispatchTab.AllocationSetSurface(con, alloc, s);
-}
-
diff --git a/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp b/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp
deleted file mode 100644
index 96eb19a..0000000
--- a/v8/renderscript/jni/android_rscompat_usage_io_driver.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <android/native_window.h>
-#include <android/log.h>
-
-#include "rsCompatibilityLib.h"
-
-#include "rsdCore.h"
-#include "rsdAllocation.h"
-#include "rsAllocation.h"
-
-#define LOG_API(...)
-
-using namespace android;
-using namespace android::renderscript;
-
-static bool IoGetBuffer(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
-    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
-    // Must lock the whole surface
-    if(drv->wndBuffer == NULL) {
-        drv->wndBuffer = new ANativeWindow_Buffer;
-    }
-    int32_t r = ANativeWindow_lock(nw, drv->wndBuffer, NULL);
-    if (r) {
-        LOG_API("Error Locking IO output buffer.");
-        return false;
-    }
-
-    void *dst = drv->wndBuffer->bits;
-    alloc->mHal.drvState.lod[0].mallocPtr = dst;
-    alloc->mHal.drvState.lod[0].stride = drv->wndBuffer->stride * alloc->mHal.state.elementSizeBytes;
-    return true;
-}
-
-extern "C" void rscAllocationSetSurface(RsContext rscR, RsAllocation allocR, ANativeWindow *nw) {
-    Context *rsc = (Context *)rscR;
-    Allocation *alloc = (Allocation *)allocR;
-    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
-
-    // Cleanup old surface if there is one.
-    if (drv->wndSurface) {
-        ANativeWindow *old = drv->wndSurface;
-        ANativeWindow_unlockAndPost(old);
-        drv->wndSurface = NULL;
-        ANativeWindow_release(old);
-        old = NULL;
-    }
-
-    if (nw != NULL) {
-        int32_t r;
-        r = ANativeWindow_setBuffersGeometry(nw, alloc->mHal.drvState.lod[0].dimX,
-                                                 alloc->mHal.drvState.lod[0].dimY,
-                                                 WINDOW_FORMAT_RGBA_8888);
-        if (r) {
-            LOG_API("Error setting IO output buffer geometry.");
-            goto errorcmp;
-        }
-
-        IoGetBuffer(rsc, alloc, nw);
-        drv->wndSurface = nw;
-    }
-
-    return;
-
- errorcmp:
-
-    if (nw) {
-        nw = NULL;
-    }
-
-}
-
-extern "C" void rscAllocationDestroy(const Context *rsc, Allocation *alloc) {
-    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
-    if (alloc->mHal.drvState.lod[0].mallocPtr) {
-        // don't free user-allocated ptrs or IO_OUTPUT buffers
-        if (!(drv->useUserProvidedPtr) &&
-            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_INPUT) &&
-            !(alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT)) {
-                free(alloc->mHal.drvState.lod[0].mallocPtr);
-        }
-        alloc->mHal.drvState.lod[0].mallocPtr = NULL;
-    }
-
-    if ((alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) &&
-        (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT)) {
-        ANativeWindow *nw = drv->wndSurface;
-        if (nw) {
-            //If we have an attached surface, need to release it.
-            ANativeWindow_unlockAndPost(nw);
-            drv->wndSurface = NULL;
-            ANativeWindow_release(nw);
-            nw = NULL;
-        }
-    }
-}
-
-extern "C" void rscAllocationIoSend(const Context *rsc, Allocation *alloc) {
-    DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
-    ANativeWindow *nw = drv->wndSurface;
-    if (nw) {
-        if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_SCRIPT) {
-            int32_t r = ANativeWindow_unlockAndPost(nw);
-            if (r) {
-                LOG_API("Error sending IO output buffer.");
-                return;
-            }
-            IoGetBuffer(rsc, alloc, nw);
-        }
-    } else {
-        LOG_API("Sent IO buffer with no attached surface.");
-        return;
-    }
-}
-
diff --git a/v8/renderscript/rs_support/Android.mk b/v8/renderscript/rs_support/Android.mk
deleted file mode 100644
index de8fae0..0000000
--- a/v8/renderscript/rs_support/Android.mk
+++ /dev/null
@@ -1,189 +0,0 @@
-
-LOCAL_PATH:=frameworks/rs
-rs_base_CFLAGS := -Werror -Wall -Wno-unused-parameter -Wno-unused-variable \
-		  -Wno-overloaded-virtual -DRS_COMPATIBILITY_LIB -std=c++11
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-rs_base_CFLAGS += -DARCH_ARM_HAVE_NEON
-endif
-
-ifeq ($(TARGET_BUILD_PDK), true)
-  rs_base_CFLAGS += -D__RS_PDK__
-endif
-
-# Build rsg-generator ====================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := rsg-generator_support
-
-# These symbols are normally defined by BUILD_XXX, but we need to define them
-# here so that local-intermediates-dir works.
-
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-intermediates := $(local-intermediates-dir)
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES:= \
-    spec.l \
-    rsg_generator.c
-
-LOCAL_CXX_STL := none
-LOCAL_SANITIZE := never
-
-include $(BUILD_HOST_EXECUTABLE)
-
-# TODO: This should go into build/core/config.mk
-RSG_GENERATOR_SUPPORT:=$(LOCAL_BUILT_MODULE)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := libRSSupport
-LOCAL_SDK_VERSION := 9
-
-
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-generated_sources_dir := $(call local-generated-sources-dir)
-
-# Generate custom headers
-
-GEN := $(addprefix $(generated_sources_dir)/, \
-            rsgApiStructs.h \
-            rsgApiFuncDecl.h \
-        )
-
-$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
-$(GEN): $(generated_sources_dir)/%.h : $(LOCAL_PATH)/%.h.rsg
-	$(transform-generated-source)
-
-# used in jni/Android.mk
-rs_generated_source += $(GEN)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-# Generate custom source files
-
-GEN := $(addprefix $(generated_sources_dir)/, \
-            rsgApi.cpp \
-            rsgApiReplay.cpp \
-        )
-
-$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
-$(GEN): $(generated_sources_dir)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
-	$(transform-generated-source)
-
-# used in jni/Android.mk
-rs_generated_source += $(GEN)
-
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-LOCAL_SRC_FILES:= \
-	rsAllocation.cpp \
-	rsApiAllocation.cpp \
-	rsApiContext.cpp \
-	rsApiDevice.cpp \
-	rsApiElement.cpp \
-	rsApiType.cpp \
-	rsClosure.cpp \
-	rsCompatibilityLib.cpp \
-	rsComponent.cpp \
-	rsContext.cpp \
-	rsCppUtils.cpp \
-	rsDevice.cpp \
-	rsDriverLoader.cpp \
-	rsElement.cpp \
-	rsFifoSocket.cpp \
-	rsObjectBase.cpp \
-	rsMatrix2x2.cpp \
-	rsMatrix3x3.cpp \
-	rsMatrix4x4.cpp \
-	rsMutex.cpp \
-	rsSampler.cpp \
-	rsScript.cpp \
-	rsScriptC.cpp \
-	rsScriptC_Lib.cpp \
-	rsScriptGroup.cpp \
-	rsScriptGroup2.cpp \
-	rsScriptIntrinsic.cpp \
-	rsSignal.cpp \
-	rsStream.cpp \
-	rsThreadIO.cpp \
-	rsType.cpp \
-	driver/rsdAllocation.cpp \
-	driver/rsdBcc.cpp \
-	driver/rsdCore.cpp \
-	driver/rsdElement.cpp \
-	driver/rsdRuntimeStubs.cpp \
-	driver/rsdSampler.cpp \
-	driver/rsdScriptGroup.cpp \
-	driver/rsdType.cpp \
-	cpu_ref/rsCpuCore.cpp \
-	cpu_ref/rsCpuExecutable.cpp \
-	cpu_ref/rsCpuScript.cpp \
-	cpu_ref/rsCpuRuntimeMath.cpp \
-	cpu_ref/rsCpuScriptGroup.cpp \
-	cpu_ref/rsCpuScriptGroup2.cpp \
-	cpu_ref/rsCpuIntrinsic.cpp \
-	cpu_ref/rsCpuIntrinsic3DLUT.cpp \
-	cpu_ref/rsCpuIntrinsicBlend.cpp \
-	cpu_ref/rsCpuIntrinsicBlur.cpp \
-	cpu_ref/rsCpuIntrinsicBLAS.cpp \
-	cpu_ref/rsCpuIntrinsicColorMatrix.cpp \
-	cpu_ref/rsCpuIntrinsicConvolve3x3.cpp \
-	cpu_ref/rsCpuIntrinsicConvolve5x5.cpp \
-	cpu_ref/rsCpuIntrinsicHistogram.cpp \
-	cpu_ref/rsCpuIntrinsicLUT.cpp \
-	cpu_ref/rsCpuIntrinsicResize.cpp \
-	cpu_ref/rsCpuIntrinsicYuvToRGB.cpp
-
-ifeq ($(ARCH_ARM_HAVE_ARMV7A),true)
-LOCAL_CFLAGS_arm := -DARCH_ARM_HAVE_VFP -DARCH_ARM_USE_INTRINSICS
-LOCAL_ASFLAGS_arm := -mfpu=neon
-LOCAL_SRC_FILES_arm := \
-    cpu_ref/rsCpuIntrinsics_neon_3DLUT.S \
-    cpu_ref/rsCpuIntrinsics_neon_Blend.S \
-    cpu_ref/rsCpuIntrinsics_neon_Blur.S \
-    cpu_ref/rsCpuIntrinsics_neon_ColorMatrix.S \
-    cpu_ref/rsCpuIntrinsics_neon_Convolve.S \
-    cpu_ref/rsCpuIntrinsics_neon_Resize.S \
-    cpu_ref/rsCpuIntrinsics_neon_YuvToRGB.S
-endif
-
-LOCAL_CFLAGS_arm64 += \
-    -DARCH_ARM_USE_INTRINSICS \
-    -DARCH_ARM64_USE_INTRINSICS \
-    -DARCH_ARM64_HAVE_NEON
-LOCAL_SRC_FILES_arm64 += \
-    cpu_ref/rsCpuIntrinsics_advsimd_3DLUT.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_Blend.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_Blur.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_ColorMatrix.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_Convolve.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_Resize.S \
-    cpu_ref/rsCpuIntrinsics_advsimd_YuvToRGB.S
-
-LOCAL_CFLAGS_x86 += -DARCH_X86_HAVE_SSSE3
-LOCAL_SRC_FILES_x86 += cpu_ref/rsCpuIntrinsics_x86.cpp
-LOCAL_CFLAGS_x86_64 += -DARCH_X86_HAVE_SSSE3
-LOCAL_SRC_FILES_x86_64 += cpu_ref/rsCpuIntrinsics_x86.cpp
-
-LOCAL_REQUIRED_MODULES := libblasV8
-LOCAL_STATIC_LIBRARIES := libbnnmlowpV8
-LOCAL_LDFLAGS += -llog -ldl -Wl,--exclude-libs,libc++_static.a
-LOCAL_NDK_STL_VARIANT := c++_static
-
-LOCAL_C_INCLUDES += external/cblas/include
-LOCAL_C_INCLUDES += external/gemmlowp/eight_bit_int_gemm
-
-LOCAL_CFLAGS += $(rs_base_CFLAGS) -DGEMMLOWP_USE_STLPORT
-
-LOCAL_MODULE:= libRSSupport
-LOCAL_MODULE_TAGS := optional
-
-# TODO: why isn't this picked up from the host GLOBAL_CFLAGS?
-LOCAL_CFLAGS += -D__STDC_FORMAT_MACROS
-
-include $(BUILD_SHARED_LIBRARY)