Merge "Support design - fix FloatingActionButton elevation"
diff --git a/api/current.txt b/api/current.txt
index 02f26d7..4ea0b76 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4266,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);
@@ -4291,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);
   }
 
@@ -4415,8 +4417,10 @@
     method public abstract boolean popBackStackImmediate(java.lang.String, int);
     method public abstract boolean popBackStackImmediate(int, int);
     method public abstract void putFragment(android.os.Bundle, java.lang.String, android.support.v4.app.Fragment);
+    method public abstract void registerFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks, boolean);
     method public abstract void removeOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
     method public abstract android.support.v4.app.Fragment.SavedState saveFragmentInstanceState(android.support.v4.app.Fragment);
+    method public abstract void unregisterFragmentLifecycleCallbacks(android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks);
     field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
   }
 
@@ -4429,6 +4433,23 @@
     method public abstract java.lang.String getName();
   }
 
+  public abstract class FragmentManager.FragmentLifecycleCallbacks {
+    ctor public FragmentManager.FragmentLifecycleCallbacks();
+    method public void onFragmentActivityCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentDetached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPaused(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentPreAttached(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.content.Context);
+    method public void onFragmentResumed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentSaveInstanceState(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.os.Bundle);
+    method public void onFragmentStarted(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentStopped(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+    method public void onFragmentViewCreated(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment, android.view.View, android.os.Bundle);
+    method public void onFragmentViewDestroyed(android.support.v4.app.FragmentManager, android.support.v4.app.Fragment);
+  }
+
   public static abstract interface FragmentManager.OnBackStackChangedListener {
     method public abstract void onBackStackChanged();
   }
@@ -4478,6 +4499,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);
@@ -8762,6 +8784,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();
@@ -8835,8 +8858,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);
diff --git a/build.gradle b/build.gradle
index 15f52bf..e47de23 100644
--- a/build.gradle
+++ b/build.gradle
@@ -182,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 {
@@ -281,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.
@@ -369,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/compat/Android.mk b/compat/Android.mk
index 3e1a2a6..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 := 24
-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/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/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/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/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-utils/Android.mk b/core-utils/Android.mk
index add72db..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 := 24
-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/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 10bcf25..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,9 +24,12 @@
 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";
 
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/design/Android.mk b/design/Android.mk
index 3217915..b3bc846 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -14,158 +14,38 @@
 
 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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment \
-    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-compat \
-#       android-support-core-utils \
-#       android-support-core-ui \
-#       android-support-fragment
+#       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-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_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 := \
-    $(resource_libs) \
-    android-support-compat \
-    android-support-core-utils \
-    android-support-core-ui \
-    android-support-fragment
-LOCAL_JAR_EXCLUDE_FILES := none
+    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 577b047..1f35a60 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -3,10 +3,7 @@
 archivesBaseName = 'design'
 
 dependencies {
-    compile project(':support-compat')
-    compile project(':support-core-utils')
-    compile project(':support-core-ui')
-    compile project(':support-fragment')
+    compile project(':support-v4')
     compile project(':support-appcompat-v7')
     compile project(':support-recyclerview-v7')
     compile project(':support-transition')
@@ -38,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/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 506f459..c61dc2a 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -27,11 +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;
 
-@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/values/attrs.xml b/design/res/values/attrs.xml
index 545a82e..41e0c0f 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -469,6 +469,7 @@
         <attr name="itemIconTint"/>
         <attr name="itemTextColor"/>
         <attr name="itemBackground"/>
+        <attr name="elevation"/>
     </declare-styleable>
 
 </resources>
diff --git a/design/res/values/colors.xml b/design/res/values/colors.xml
index ebf6412..eb18f05 100644
--- a/design/res/values/colors.xml
+++ b/design/res/values/colors.xml
@@ -38,4 +38,6 @@
 
     <color name="design_snackbar_background_color">#323232</color>
 
+    <color name="design_bottom_navigation_shadow_color">#14000000</color>
+
 </resources>
\ No newline at end of file
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index 63e98b8..de715ce 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -59,6 +59,8 @@
     <dimen name="design_bottom_sheet_peek_height_min">64dp</dimen>
 
     <dimen name="design_bottom_navigation_height">56dp</dimen>
+    <dimen name="design_bottom_navigation_elevation">8dp</dimen>
+    <dimen name="design_bottom_navigation_shadow_height">1dp</dimen>
     <dimen name="design_bottom_navigation_text_size">12sp</dimen>
     <dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
     <dimen name="design_bottom_navigation_margin">8dp</dimen>
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index c8b9cd5..820742f 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -44,6 +44,7 @@
 
     <style name="Widget.Design.BottomNavigationView" parent="">
         <item name="itemBackground">?attr/selectableItemBackgroundBorderless</item>
+        <item name="elevation">@dimen/design_bottom_navigation_elevation</item>
     </style>
 
     <style name="Base.Widget.Design.TabLayout" parent="android:Widget">
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index df361e6..e6ae08f 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -86,8 +86,9 @@
             public void onClick(View v) {
                 final BottomNavigationItemView itemView = (BottomNavigationItemView) v;
                 final int itemPosition = itemView.getItemPosition();
-                activateNewButton(itemPosition);
-                mMenu.performItemAction(itemView.getItemData(), mPresenter, 0);
+                if (!mMenu.performItemAction(itemView.getItemData(), mPresenter, 0)) {
+                    activateNewButton(itemPosition);
+                }
             }
         };
         mTempChildWidths = new int[BottomNavigationMenu.MAX_ITEM_COUNT];
@@ -96,10 +97,6 @@
     @Override
     public void initialize(MenuBuilder menu) {
         mMenu = menu;
-        if (mMenu == null) return;
-        if (mMenu.size() > mActiveButton) {
-            mMenu.getItem(mActiveButton).setChecked(true);
-        }
     }
 
     @Override
@@ -124,7 +121,7 @@
                 }
             }
         } else {
-            final int maxAvailable = width / count;
+            final int maxAvailable = width / (count == 0 ? 1 : count);
             final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
             int extra = width - childWidth * count;
             for (int i = 0; i < count; i++) {
@@ -180,9 +177,9 @@
     }
 
     /**
-     * Set the tint which is applied to the menu items' icons.
+     * Sets the tint which is applied to the menu items' icons.
      *
-     * @param tint the tint to apply.
+     * @param tint the tint to apply
      */
     public void setIconTintList(ColorStateList tint) {
         mItemIconTint = tint;
@@ -195,7 +192,7 @@
     /**
      * Returns the tint which is applied to menu items' icons.
      *
-     * @return The ColorStateList that is used to tint menu items' icons.
+     * @return the ColorStateList that is used to tint menu items' icons
      */
     @Nullable
     public ColorStateList getIconTintList() {
@@ -203,7 +200,7 @@
     }
 
     /**
-     * Set the text color to be used on menu items.
+     * Sets the text color to be used on menu items.
      *
      * @param color the ColorStateList used for menu items' text.
      */
@@ -218,15 +215,16 @@
     /**
      * Returns the text color used on menu items.
      *
-     * @return the ColorStateList used for menu items' text.
+     * @return the ColorStateList used for menu items' text
      */
     public ColorStateList getItemTextColor() {
         return mItemTextColor;
     }
 
     /**
-     * Sets the resource id to be used for item background.
-     * @param background the resource id of the background.
+     * Sets the resource ID to be used for item background.
+     *
+     * @param background the resource ID of the background
      */
     public void setItemBackgroundRes(int background) {
         mItemBackgroundRes = background;
@@ -237,9 +235,9 @@
     }
 
     /**
-     * Returns the background resource of the menu items.
+     * Returns the resource ID for the background of the menu items.
      *
-     * @return the resource id of the background.
+     * @return the resource ID for the background
      */
     public int getItemBackgroundRes() {
         return mItemBackgroundRes;
@@ -256,6 +254,9 @@
             }
         }
         removeAllViews();
+        if (mMenu.size() == 0) {
+            return;
+        }
         mButtons = new BottomNavigationItemView[mMenu.size()];
         mShiftingMode = mMenu.size() > 3;
         for (int i = 0; i < mMenu.size(); i++) {
@@ -273,6 +274,8 @@
             child.setOnClickListener(mOnClickListener);
             addView(child);
         }
+        mActiveButton = Math.min(mMenu.size() - 1, mActiveButton);
+        mMenu.getItem(mActiveButton).setChecked(true);
     }
 
     public void updateMenuView() {
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/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/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index e82f1fb..e3a81b3 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.os.Build;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -25,6 +26,8 @@
 import android.support.design.internal.BottomNavigationMenu;
 import android.support.design.internal.BottomNavigationMenuView;
 import android.support.design.internal.BottomNavigationPresenter;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.view.SupportMenuInflater;
 import android.support.v7.view.menu.MenuBuilder;
@@ -35,6 +38,7 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -117,7 +121,7 @@
         mPresenter.setBottomNavigationMenuView(mMenuView);
         mMenuView.setPresenter(mPresenter);
         mMenu.addMenuPresenter(mPresenter);
-
+        mPresenter.initForMenu(getContext(), mMenu);
 
         // Custom attributes
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
@@ -138,6 +142,10 @@
             mMenuView.setItemTextColor(
                     createDefaultColorStateList(android.R.attr.textColorSecondary));
         }
+        if (a.hasValue(R.styleable.BottomNavigationView_elevation)) {
+            ViewCompat.setElevation(this, a.getDimensionPixelSize(
+                    R.styleable.BottomNavigationView_elevation, 0));
+        }
 
         int itemBackground = a.getResourceId(R.styleable.BottomNavigationView_itemBackground, 0);
         mMenuView.setItemBackgroundRes(itemBackground);
@@ -148,11 +156,14 @@
         a.recycle();
 
         addView(mMenuView, params);
+        if (Build.VERSION.SDK_INT < 21) {
+            addCompatibilityTopDivider(context);
+        }
 
         mMenu.setCallback(new MenuBuilder.Callback() {
             @Override
             public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-                return mListener != null && mListener.onNavigationItemSelected(item);
+                return mListener != null && !mListener.onNavigationItemSelected(item);
             }
 
             @Override
@@ -188,7 +199,6 @@
     public void inflateMenu(int resId) {
         mPresenter.setUpdateSuspended(true);
         getMenuInflater().inflate(resId, mMenu);
-        mPresenter.initForMenu(getContext(), mMenu);
         mPresenter.setUpdateSuspended(false);
         mPresenter.updateMenuView(true);
     }
@@ -224,10 +234,13 @@
     }
 
     /**
-     * Returns the text color used on menu items.
+     * Returns colors used for the different states (normal, selected, focused, etc.) of the menu
+     * item text.
      *
      * @see #setItemTextColor(ColorStateList)
      *
+     * @return the ColorStateList of colors used for the different states of the menu items text.
+     *
      * @attr ref R.styleable#BottomNavigationView_itemTextColor
      */
     @Nullable
@@ -236,7 +249,8 @@
     }
 
     /**
-     * Set the text color to be used on menu items.
+     * Set the colors to use for the different states (normal, selected, focused, etc.) of the menu
+     * item text.
      *
      * @see #getItemTextColor()
      *
@@ -279,11 +293,25 @@
          *
          * @param item The selected item
          *
-         * @return true to display the item as the selected item
+         * @return true to display the item as the selected item and false if the item should not
+         *         be selected. Consider setting non-selectable items as disabled preemptively to
+         *         make them appear non-interactive.
          */
         boolean onNavigationItemSelected(@NonNull MenuItem item);
     }
 
+    private void addCompatibilityTopDivider(Context context) {
+        View divider = new View(context);
+        divider.setBackgroundColor(
+                ContextCompat.getColor(context, R.color.design_bottom_navigation_shadow_color));
+        FrameLayout.LayoutParams dividerParams = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                getResources().getDimensionPixelSize(
+                        R.dimen.design_bottom_navigation_shadow_height));
+        divider.setLayoutParams(dividerParams);
+        addView(divider);
+    }
+
     private MenuInflater getMenuInflater() {
         if (mMenuInflater == null) {
             mMenuInflater = new SupportMenuInflater(getContext());
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/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 6aafb4f..94b4be3 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -78,6 +78,16 @@
  * {@link #setError(CharSequence)}, and a character counter via
  * {@link #setCounterEnabled(boolean)}.</p>
  *
+ * <p>Password visibility toggling is also supported via the
+ * {@link #setPasswordVisibilityToggleEnabled(boolean)} API and related attribute.
+ * If enabled, a button is displayed to toggle between the password being displayed as plain-text
+ * or disguised, when your EditText is set to display a password.</p>
+ *
+ * <p><strong>Note:</strong> When using the password toggle functionality, the 'end' compound
+ * drawable of the EditText will be overridden while the toggle is enabled. To ensure that any
+ * existing drawables are restored correctly, you should set those compound drawables relatively
+ * (start/end), opposed to absolutely (left/right).</p>
+ *
  * The {@link TextInputEditText} class is provided to be used as a child of this layout. Using
  * TextInputEditText allows TextInputLayout greater control over the visual aspects of any
  * text input. An example usage is as so:
@@ -222,7 +232,7 @@
                 R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
 
         mPasswordToggleEnabled = a.getBoolean(
-                R.styleable.TextInputLayout_passwordToggleEnabled, true);
+                R.styleable.TextInputLayout_passwordToggleEnabled, false);
         mPasswordToggleDrawable = a.getDrawable(R.styleable.TextInputLayout_passwordToggleDrawable);
         mPasswordToggleContentDesc = a.getText(
                 R.styleable.TextInputLayout_passwordToggleContentDescription);
@@ -1056,11 +1066,15 @@
                 mPasswordToggleView.setVisibility(View.GONE);
             }
 
-            // Make sure that we remove the dummy end compound drawable
-            final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
-            if (compounds[2] == mPasswordToggleDummyDrawable) {
-                TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
-                        mOriginalEditTextEndDrawable, compounds[3]);
+            if (mPasswordToggleDummyDrawable != null) {
+                // Make sure that we remove the dummy end compound drawable if it exists, and then
+                // clear it
+                final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
+                if (compounds[2] == mPasswordToggleDummyDrawable) {
+                    TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0],
+                            compounds[1], mOriginalEditTextEndDrawable, compounds[3]);
+                    mPasswordToggleDummyDrawable = null;
+                }
             }
         }
     }
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index 2c9a2a7..9c1c6ca 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -27,21 +27,27 @@
 
 import static org.hamcrest.core.AllOf.allOf;
 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.mock;
 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.content.res.Resources;
 import android.support.annotation.ColorInt;
 import android.support.design.test.R;
 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.view.Menu;
 import android.view.MenuItem;
+import android.view.ViewGroup;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -73,6 +79,20 @@
         mMenuStringContent.put(R.id.destination_people, res.getString(R.string.navigate_people));
     }
 
+    @UiThreadTest
+    @Test
+    @SmallTest
+    public void testAddItemsWithoutMenuInflation() {
+        BottomNavigationView navigation = new BottomNavigationView(mActivityTestRule.getActivity());
+        mActivityTestRule.getActivity().setContentView(navigation);
+        navigation.getMenu().add("Item1");
+        navigation.getMenu().add("Item2");
+        assertEquals(2, navigation.getMenu().size());
+        navigation.getMenu().removeItem(0);
+        navigation.getMenu().removeItem(0);
+        assertEquals(0, navigation.getMenu().size());
+    }
+
     @Test
     @SmallTest
     public void testBasics() {
@@ -95,22 +115,38 @@
                 mock(BottomNavigationView.OnNavigationItemSelectedListener.class);
         mBottomNavigation.setOnNavigationItemSelectedListener(mockedListener);
 
-        // Click one of our items
+        // Make the listener return true to allow selecting the item.
+        when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(true);
         onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
                 isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
-        // And that our listener has been notified of the click
+        // Verify our listener has been notified of the click
         verify(mockedListener, times(1)).onNavigationItemSelected(
                 mBottomNavigation.getMenu().findItem(R.id.destination_profile));
+        // Verify the item is now selected
+        assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
+
+        // Make the listener return false to disallow selecting the item.
+        when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(false);
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
+        // Verify our listener has been notified of the click
+        verify(mockedListener, times(1)).onNavigationItemSelected(
+                mBottomNavigation.getMenu().findItem(R.id.destination_people));
+        // Verify the previous item is still selected
+        assertFalse(mBottomNavigation.getMenu().findItem(R.id.destination_people).isChecked());
+        assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
 
         // Set null listener to test that the next click is not going to notify the
-        // previously set listener
+        // previously set listener and will allow selecting items.
         mBottomNavigation.setOnNavigationItemSelectedListener(null);
 
         // Click one of our items
-        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
                 isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
         // And that our previous listener has not been notified of the click
         verifyNoMoreInteractions(mockedListener);
+        // Verify the correct item is now selected.
+        assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_home).isChecked());
     }
 
     @Test
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
index 9a5d7ed..f38a03f 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
@@ -46,6 +46,7 @@
 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.view.LayoutInflater;
@@ -362,6 +363,7 @@
     }
 
     @Test
+    @MediumTest
     public void testSkipCollapsed() throws Throwable {
         getBehavior().setSkipCollapsed(true);
         checkSetState(BottomSheetBehavior.STATE_EXPANDED, ViewMatchers.isDisplayed());
@@ -630,7 +632,10 @@
                 });
     }
 
+    // Test disabled because it is consistently failing.
+    @Suppress
     @Test
+    @MediumTest
     public void testFabVisibility() {
         withFabVisibilityChange(false, new Runnable() {
             @Override
@@ -655,6 +660,7 @@
     }
 
     @Test
+    @MediumTest
     public void testAutoPeekHeight() throws Throwable {
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -688,6 +694,7 @@
     }
 
     @Test
+    @MediumTest
     public void testDynamicContent() throws Throwable {
         registerIdlingResourceCallback();
         try {
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 5125f5d..a0fec58 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -37,6 +37,7 @@
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
@@ -49,7 +50,8 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
-import android.support.test.filters.SmallTest;
+import android.support.v4.widget.TextViewCompat;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.View;
@@ -307,6 +309,68 @@
                 1, layout.animateToExpansionFractionCount);
     }
 
+    @Test
+    public void testMaintainsLeftRightCompoundDrawables() throws Throwable {
+        final Activity activity = mActivityTestRule.getActivity();
+
+        // Set a known set of test compound drawables on the EditText
+        final Drawable left = new ColorDrawable(Color.RED);
+        final Drawable top = new ColorDrawable(Color.GREEN);
+        final Drawable right = new ColorDrawable(Color.BLUE);
+        final Drawable bottom = new ColorDrawable(Color.BLACK);
+
+        final TextInputEditText editText = new TextInputEditText(activity);
+        editText.setCompoundDrawables(left, top, right, bottom);
+
+        // Now add the EditText to a TextInputLayout
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextInputLayout til = (TextInputLayout)
+                        activity.findViewById(R.id.textinput_noedittext);
+                til.addView(editText);
+            }
+        });
+
+        // Finally assert that all of the drawables are untouched
+        final Drawable[] compoundDrawables = editText.getCompoundDrawables();
+        assertSame(left, compoundDrawables[0]);
+        assertSame(top, compoundDrawables[1]);
+        assertSame(right, compoundDrawables[2]);
+        assertSame(bottom, compoundDrawables[3]);
+    }
+
+    @Test
+    public void testMaintainsStartEndCompoundDrawables() throws Throwable {
+        final Activity activity = mActivityTestRule.getActivity();
+
+        // Set a known set of test compound drawables on the EditText
+        final Drawable start = new ColorDrawable(Color.RED);
+        final Drawable top = new ColorDrawable(Color.GREEN);
+        final Drawable end = new ColorDrawable(Color.BLUE);
+        final Drawable bottom = new ColorDrawable(Color.BLACK);
+
+        final TextInputEditText editText = new TextInputEditText(activity);
+        TextViewCompat.setCompoundDrawablesRelative(editText, start, top, end, bottom);
+
+        // Now add the EditText to a TextInputLayout
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TextInputLayout til = (TextInputLayout)
+                        activity.findViewById(R.id.textinput_noedittext);
+                til.addView(editText);
+            }
+        });
+
+        // Finally assert that all of the drawables are untouched
+        final Drawable[] compoundDrawables = TextViewCompat.getCompoundDrawablesRelative(editText);
+        assertSame(start, compoundDrawables[0]);
+        assertSame(top, compoundDrawables[1]);
+        assertSame(end, compoundDrawables[2]);
+        assertSame(bottom, compoundDrawables[3]);
+    }
+
     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..a8323dc 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,409 @@
         }
     }
 
+    /**
+     * 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;
+    /**
+     * After the transition completes, the fragment's view is set to GONE and the exiting
+     * views are set to VISIBLE.
+     */
+    public static void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
+            final ArrayList<View> exitingViews) {
+        Transition exitTransition = (Transition) exitTransitionObj;
+        exitTransition.addListener(new Transition.TransitionListener() {
+            @Override
+            public void onTransitionStart(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+                transition.removeListener(this);
+                fragmentView.setVisibility(View.GONE);
+                final int numViews = exitingViews.size();
+                for (int i = 0; i < numViews; i++) {
+                    exitingViews.get(i).setVisibility(View.VISIBLE);
+                }
+            }
+
+            @Override
+            public void onTransitionCancel(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionPause(Transition transition) {
+            }
+
+            @Override
+            public void onTransitionResume(Transition transition) {
+            }
+        });
+    }
+
+    /**
+     * 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..0adde93 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
@@ -721,6 +711,14 @@
     }
 
     /**
+     * Return this fragment's child FragmentManager one has been previously created,
+     * otherwise null.
+     */
+    FragmentManager peekChildFragmentManager() {
+        return mChildFragmentManager;
+    }
+
+    /**
      * Returns the parent Fragment containing this Fragment.  If this Fragment
      * is attached directly to an Activity, returns null.
      */
@@ -1702,7 +1700,7 @@
      *                 when added not as a pop from the back stack.
      */
     public void setEnterSharedElementCallback(SharedElementCallback callback) {
-        mEnterTransitionCallback = callback;
+        ensureAnimationInfo().mEnterTransitionCallback = callback;
     }
 
     /**
@@ -1713,7 +1711,7 @@
      *                 when added as a pop from the back stack.
      */
     public void setExitSharedElementCallback(SharedElementCallback callback) {
-        mExitTransitionCallback = callback;
+        ensureAnimationInfo().mExitTransitionCallback = callback;
     }
 
     /**
@@ -1727,7 +1725,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 +1738,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 +1759,7 @@
      *                   android.transition.Transition.
      */
     public void setReturnTransition(Object transition) {
-        mReturnTransition = transition;
+        ensureAnimationInfo().mReturnTransition = transition;
     }
 
     /**
@@ -1774,8 +1775,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 +1796,7 @@
      *                   must be an android.transition.Transition.
      */
     public void setExitTransition(Object transition) {
-        mExitTransition = transition;
+        ensureAnimationInfo().mExitTransition = transition;
     }
 
     /**
@@ -1808,7 +1812,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 +1832,7 @@
      *                   must be an android.transition.Transition.
      */
     public void setReenterTransition(Object transition) {
-        mReenterTransition = transition;
+        ensureAnimationInfo().mReenterTransition = transition;
     }
 
     /**
@@ -1841,8 +1848,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 +1865,7 @@
      *                   Scene.  <code>transition</code> must be an android.transition.Transition.
      */
     public void setSharedElementEnterTransition(Object transition) {
-        mSharedElementEnterTransition = transition;
+        ensureAnimationInfo().mSharedElementEnterTransition = transition;
     }
 
     /**
@@ -1868,7 +1878,10 @@
      *                   Scene.
      */
     public Object getSharedElementEnterTransition() {
-        return mSharedElementEnterTransition;
+        if (mAnimationInfo == null) {
+            return null;
+        }
+        return mAnimationInfo.mSharedElementEnterTransition;
     }
 
     /**
@@ -1884,7 +1897,7 @@
      *                   Scene. <code>transition</code> must be an android.transition.Transition.
      */
     public void setSharedElementReturnTransition(Object transition) {
-        mSharedElementReturnTransition = transition;
+        ensureAnimationInfo().mSharedElementReturnTransition = transition;
     }
 
     /**
@@ -1900,8 +1913,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 +1930,7 @@
      *              wait until the exiting transition completes.
      */
     public void setAllowEnterTransitionOverlap(boolean allow) {
-        mAllowEnterTransitionOverlap = allow;
+        ensureAnimationInfo().mAllowEnterTransitionOverlap = allow;
     }
 
     /**
@@ -1925,7 +1942,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 +1955,7 @@
      *              return transition completes.
      */
     public void setAllowReturnTransitionOverlap(boolean allow) {
-        mAllowReturnTransitionOverlap = allow;
+        ensureAnimationInfo().mAllowReturnTransitionOverlap = allow;
     }
 
     /**
@@ -1949,7 +1967,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 +2104,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 +2116,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 +2452,175 @@
         }
     }
 
+    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;
+    }
+
+    boolean isHideReplaced() {
+        if (mAnimationInfo == null) {
+            return false;
+        }
+        return mAnimationInfo.mIsHideReplaced;
+    }
+
+    void setHideReplaced(boolean replaced) {
+        ensureAnimationInfo().mIsHideReplaced = replaced;
+    }
+
+    /**
+     * 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 hidden, but the transition is handling the hide
+        boolean mIsHideReplaced;
+    }
 }
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..6ef6ed1 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -22,7 +22,6 @@
 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;
@@ -33,25 +32,27 @@
 import android.support.v4.os.BuildCompat;
 import android.support.v4.util.DebugUtils;
 import android.support.v4.util.LogWriter;
+import android.support.v4.util.Pair;
 import android.support.v4.view.LayoutInflaterFactory;
 import android.support.v4.view.ViewCompat;
 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.LayoutInflater;
 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;
@@ -59,6 +60,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
 
@@ -176,6 +178,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 +228,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 +251,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 +275,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);
@@ -360,6 +365,26 @@
     public abstract boolean isDestroyed();
 
     /**
+     * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events
+     * happening in this FragmentManager. All registered callbacks will be automatically
+     * unregistered when this FragmentManager is destroyed.
+     *
+     * @param cb Callbacks to register
+     * @param recursive true to automatically register this callback for all child FragmentManagers
+     */
+    public abstract void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
+            boolean recursive);
+
+    /**
+     * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback
+     * was not previously registered this call has no effect. All registered callbacks will be
+     * automatically unregistered when this FragmentManager is destroyed.
+     *
+     * @param cb Callbacks to unregister
+     */
+    public abstract void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb);
+
+    /**
      * Print the FragmentManager's state into the given stream.
      *
      * @param prefix Text to print at the front of each line.
@@ -377,6 +402,141 @@
     public static void enableDebugLogging(boolean enabled) {
         FragmentManagerImpl.DEBUG = enabled;
     }
+
+    /**
+     * Callback interface for listening to fragment state changes that happen
+     * within a given FragmentManager.
+     */
+    public abstract class FragmentLifecycleCallbacks {
+        /**
+         * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
+         * This is a good time to inject any required dependencies for the fragment before any of
+         * the fragment's lifecycle methods are invoked.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param context Context that the Fragment is being attached to
+         */
+        public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
+
+        /**
+         * Called after the fragment has been attached to its host. Its host will have had
+         * <code>onAttachFragment</code> called before this call happens.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param context Context that the Fragment was attached to
+         */
+        public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given
+         * fragment instance, though the fragment may be attached and detached multiple times.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param savedInstanceState Saved instance bundle from a previous instance
+         */
+        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given
+         * fragment instance, though the fragment may be attached and detached multiple times.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param savedInstanceState Saved instance bundle from a previous instance
+         */
+        public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
+                Bundle savedInstanceState) {}
+
+        /**
+         * Called after the fragment has returned a non-null view from the FragmentManager's
+         * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment that created and owns the view
+         * @param v View returned by the fragment
+         * @param savedInstanceState Saved instance bundle from a previous instance
+         */
+        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
+                Bundle savedInstanceState) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onStart()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentStarted(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onResume()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentResumed(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onPause()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentPaused(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onStop()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentStopped(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onSaveInstanceState(Bundle)}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         * @param outState Saved state bundle for the fragment
+         */
+        public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onDestroyView()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onDestroy()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
+
+        /**
+         * Called after the fragment has returned from the FragmentManager's call to
+         * {@link Fragment#onDetach()}.
+         *
+         * @param fm Host FragmentManager
+         * @param f Fragment changing state
+         */
+        public void onFragmentDetached(FragmentManager fm, Fragment f) {}
+    }
 }
 
 final class FragmentManagerState implements Parcelable {
@@ -501,7 +661,7 @@
         }
     }
 
-    ArrayList<Runnable> mPendingActions;
+    ArrayList<OpGenerator> mPendingActions;
     Runnable[] mTmpActions;
     boolean mExecutingActions;
 
@@ -516,10 +676,10 @@
     ArrayList<Integer> mAvailBackStackIndices;
 
     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
+    private CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks;
 
     int mCurState = Fragment.INITIALIZING;
     FragmentHostCallback mHost;
-    FragmentController mController;
     FragmentContainer mContainer;
     Fragment mParent;
 
@@ -531,10 +691,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 +759,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 +791,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 +1002,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 +1063,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 +1191,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:
@@ -1044,6 +1225,7 @@
                     f.mParentFragment = mParent;
                     f.mFragmentManager = mParent != null
                             ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
+                    dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                     f.mCalled = false;
                     f.onAttach(mHost.getContext());
                     if (!f.mCalled) {
@@ -1055,9 +1237,11 @@
                     } else {
                         f.mParentFragment.onAttachFragment(f);
                     }
+                    dispatchOnFragmentAttached(f, mHost.getContext(), false);
 
                     if (!f.mRetaining) {
                         f.performCreate(f.mSavedFragmentState);
+                        dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                     } else {
                         f.restoreChildFragmentState(f.mSavedFragmentState);
                         f.mState = Fragment.CREATED;
@@ -1078,6 +1262,7 @@
                             }
                             if (f.mHidden) f.mView.setVisibility(View.GONE);
                             f.onViewCreated(f.mView, f.mSavedFragmentState);
+                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
                         } else {
                             f.mInnerView = null;
                         }
@@ -1120,22 +1305,23 @@
                                     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);
+                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
+                                        false);
                             } else {
                                 f.mInnerView = null;
                             }
                         }
 
                         f.performActivityCreated(f.mSavedFragmentState);
+                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                         if (f.mView != null) {
                             f.restoreViewState(f.mSavedFragmentState);
                         }
@@ -1149,11 +1335,13 @@
                     if (newState > Fragment.STOPPED) {
                         if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                         f.performStart();
+                        dispatchOnFragmentStarted(f, false);
                     }
                 case Fragment.STARTED:
                     if (newState > Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                         f.performResume();
+                        dispatchOnFragmentResumed(f, false);
                         f.mSavedFragmentState = null;
                         f.mSavedViewState = null;
                     }
@@ -1164,11 +1352,13 @@
                     if (newState < Fragment.RESUMED) {
                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                         f.performPause();
+                        dispatchOnFragmentPaused(f, false);
                     }
                 case Fragment.STARTED:
                     if (newState < Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                         f.performStop();
+                        dispatchOnFragmentStopped(f, false);
                     }
                 case Fragment.STOPPED:
                     if (newState < Fragment.STOPPED) {
@@ -1186,25 +1376,27 @@
                             }
                         }
                         f.performDestroyView();
+                        dispatchOnFragmentViewDestroyed(f, false);
                         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,34 +1412,36 @@
                 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);
                             if (!f.mRetaining) {
                                 f.performDestroy();
+                                dispatchOnFragmentDestroyed(f, false);
                             } else {
                                 f.mState = Fragment.INITIALIZING;
                             }
 
                             f.performDetach();
+                            dispatchOnFragmentDetached(f, false);
                             if (!keepActive) {
                                 if (!f.mRetaining) {
                                     makeInactive(f);
@@ -1273,26 +1467,122 @@
         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 && !fragment.isHideReplaced()
+                    ? View.GONE
+                    : View.VISIBLE;
+            fragment.mView.setVisibility(visibility);
+            if (fragment.isHideReplaced()) {
+                fragment.setHideReplaced(false);
+            }
+        }
+        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 = Math.min(nextState, Fragment.CREATED);
+            } else {
+                nextState = Math.min(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 +1658,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 +1670,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 +1682,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 +1731,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 +1752,6 @@
                 if (fragment.mHasMenu && fragment.mMenuVisible) {
                     mNeedMenuInvalidate = true;
                 }
-                moveToState(fragment, mCurState, transition, transitionStyle, false);
             }
         }
     }
@@ -1551,7 +1832,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 +1841,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 +1922,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 +1941,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 +1994,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 +2407,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 +2455,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;
     }
@@ -1866,6 +2528,7 @@
             mStateBundle = new Bundle();
         }
         f.performSaveInstanceState(mStateBundle);
+        dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
         if (!mStateBundle.isEmpty()) {
             result = mStateBundle;
             mStateBundle = null;
@@ -1895,6 +2558,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 +2811,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 +2839,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;
@@ -2326,6 +2991,265 @@
         }
     }
 
+    public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
+            boolean recursive) {
+        if (mLifecycleCallbacks == null) {
+            mLifecycleCallbacks = new CopyOnWriteArrayList<>();
+        }
+        mLifecycleCallbacks.add(new Pair(cb, recursive));
+    }
+
+    public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) {
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+
+        synchronized (mLifecycleCallbacks) {
+            for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) {
+                if (mLifecycleCallbacks.get(i).first == cb) {
+                    mLifecycleCallbacks.remove(i);
+                    break;
+                }
+            }
+        }
+    }
+
+    void dispatchOnFragmentPreAttached(Fragment f, Context context, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentPreAttached(f, context, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentPreAttached(this, f, context);
+            }
+        }
+    }
+
+    void dispatchOnFragmentAttached(Fragment f, Context context, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentAttached(f, context, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentAttached(this, f, context);
+            }
+        }
+    }
+
+    void dispatchOnFragmentCreated(Fragment f, Bundle savedInstanceState, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentCreated(f, savedInstanceState, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentCreated(this, f, savedInstanceState);
+            }
+        }
+    }
+
+    void dispatchOnFragmentActivityCreated(Fragment f, Bundle savedInstanceState,
+            boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentActivityCreated(this, f, savedInstanceState);
+            }
+        }
+    }
+
+    void dispatchOnFragmentViewCreated(Fragment f, View v, Bundle savedInstanceState,
+            boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
+            }
+        }
+    }
+
+    void dispatchOnFragmentStarted(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentStarted(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentStarted(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentResumed(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentResumed(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentResumed(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentPaused(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentPaused(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentPaused(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentStopped(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentStopped(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentStopped(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentSaveInstanceState(Fragment f, Bundle outState, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentSaveInstanceState(f, outState, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentSaveInstanceState(this, f, outState);
+            }
+        }
+    }
+
+    void dispatchOnFragmentViewDestroyed(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentViewDestroyed(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentViewDestroyed(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentDestroyed(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentDestroyed(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentDestroyed(this, f);
+            }
+        }
+    }
+
+    void dispatchOnFragmentDetached(Fragment f, boolean onlyRecursive) {
+        if (mParent != null) {
+            FragmentManager parentManager = mParent.getFragmentManager();
+            if (parentManager instanceof FragmentManagerImpl) {
+                ((FragmentManagerImpl) parentManager)
+                        .dispatchOnFragmentDetached(f, true);
+            }
+        }
+        if (mLifecycleCallbacks == null) {
+            return;
+        }
+        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
+            if (!onlyRecursive || p.second) {
+                p.first.onFragmentDetached(this, f);
+            }
+        }
+    }
+
     public static int reverseTransit(int transit) {
         int rev = 0;
         switch (transit) {
@@ -2472,4 +3396,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..9efd1bc
--- /dev/null
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -0,0 +1,1126 @@
+/*
+ * 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.os.Build;
+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
+                || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            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) {
+            replaceHide(exitTransition, outFragment, exitingViews);
+            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);
+        }
+    }
+
+    /**
+     * Replace hide operations with visibility changes on the exiting views. Instead of making
+     * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
+     * transition, make the fragment's view GONE.
+     */
+    private static void replaceHide(Object exitTransition, Fragment exitingFragment,
+            final ArrayList<View> exitingViews) {
+        if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
+                && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
+            exitingFragment.setHideReplaced(true);
+            FragmentTransitionCompat21.scheduleHideFragmentView(exitTransition,
+                    exitingFragment.getView(), exitingViews);
+            final ViewGroup container = exitingFragment.mContainer;
+            container.getViewTreeObserver().addOnPreDrawListener(
+                    new ViewTreeObserver.OnPreDrawListener() {
+                        @Override
+                        public boolean onPreDraw() {
+                            container.getViewTreeObserver().removeOnPreDrawListener(this);
+                            setViewVisibility(exitingViews, View.INVISIBLE);
+                            return true;
+                        }
+                    });
+        }
+    }
+
+    /**
+     * 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/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..9f6b263
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
@@ -0,0 +1,476 @@
+/*
+ * 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.support.v4.view.ViewCompat;
+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(ViewCompat.isAttachedToWindow(fragment1.getView()));
+
+        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(ViewCompat.isAttachedToWindow(fragment1.getView()));
+        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(1000, 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 02cca09..94d0445 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
@@ -17,16 +17,6 @@
 
 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;
@@ -38,6 +28,7 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks;
 import android.support.v4.app.test.EmptyFragmentTestActivity;
 import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
@@ -45,7 +36,6 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.widget.TextView;
-
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -54,6 +44,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import static junit.framework.Assert.*;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.*;
+
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class FragmentLifecycleTest {
@@ -437,6 +431,69 @@
         fc.attachHost(null);
         fc.dispatchCreate();
 
+        FragmentLifecycleCallbacks mockLc = mock(FragmentLifecycleCallbacks.class);
+        FragmentLifecycleCallbacks mockRecursiveLc = mock(FragmentLifecycleCallbacks.class);
+
+        FragmentManager fm = fc.getSupportFragmentManager();
+        fm.registerFragmentLifecycleCallbacks(mockLc, false);
+        fm.registerFragmentLifecycleCallbacks(mockRecursiveLc, true);
+
+        ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
+        fm.beginTransaction()
+                .add(android.R.id.content, fragment)
+                .commitNow();
+
+        verify(mockLc, times(1)).onFragmentCreated(fm, fragment, null);
+
+        fc.dispatchActivityCreated();
+
+        Fragment childFragment = fragment.getChildFragment();
+
+        verify(mockLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+        verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
+        verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, childFragment, null);
+
+        fc.dispatchStart();
+
+        verify(mockLc, times(1)).onFragmentStarted(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, childFragment);
+
+        fc.dispatchResume();
+
+        verify(mockLc, times(1)).onFragmentResumed(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, childFragment);
+
+        // Confirm that the parent fragment received onAttachFragment
+        assertTrue("parent fragment did not receive onAttachFragment",
+                fragment.mCalledOnAttachFragment);
+
+        fc.dispatchStop();
+
+        verify(mockLc, times(1)).onFragmentStopped(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, childFragment);
+
+        fc.dispatchReallyStop();
+        fc.dispatchDestroy();
+
+        verify(mockLc, times(1)).onFragmentDestroyed(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, fragment);
+        verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, childFragment);
+    }
+
+    /**
+     * This test checks that FragmentLifecycleCallbacks are invoked when expected.
+     */
+    @Test
+    @UiThreadTest
+    public void fragmentLifecycleCallbacks() throws Throwable {
+        FragmentController fc = FragmentController.createController(
+                new HostCallbacks(mActivityRule.getActivity()));
+        fc.attachHost(null);
+        fc.dispatchCreate();
+
         FragmentManager fm = fc.getSupportFragmentManager();
 
         ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
@@ -458,6 +515,31 @@
         fc.dispatchDestroy();
     }
 
+    /**
+     * This tests that fragments call onDestroy when the activity finishes.
+     */
+    @Test
+    @UiThreadTest
+    public void fragmentDestroyedOnFinish() throws Throwable {
+        FragmentController fc = startupFragmentController(null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.fragment_a);
+        StrictViewFragment fragmentB = StrictViewFragment.create(R.layout.fragment_b);
+        fm.beginTransaction()
+                .add(android.R.id.content, fragmentA)
+                .commit();
+        fm.executePendingTransactions();
+        fm.beginTransaction()
+                .replace(android.R.id.content, fragmentB)
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+        shutdownFragmentController(fc);
+        assertTrue(fragmentB.mCalledOnDestroy);
+        assertTrue(fragmentA.mCalledOnDestroy);
+    }
+
     private void assertAnimationsMatch(FragmentManager fm, int enter, int exit, int popEnter,
             int popExit) {
         FragmentManagerImpl fmImpl = (FragmentManagerImpl) fm;
@@ -546,8 +628,14 @@
         }
     }
 
+    /**
+     * This tests a deliberately odd use of a child fragment, added in onCreateView instead
+     * of elsewhere. It simulates creating a UI child fragment added to the view hierarchy
+     * created by this fragment.
+     */
     public static class ChildFragmentManagerFragment extends StrictFragment {
         private FragmentManager mSavedChildFragmentManager;
+        private ChildFragmentManagerChildFragment mChildFragment;
 
         @Override
         public void onAttach(Context context) {
@@ -561,13 +649,24 @@
                 @Nullable Bundle savedInstanceState) {
             assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager,
                     getChildFragmentManager());
-            ChildFragmentManagerChildFragment child = new ChildFragmentManagerChildFragment("foo");
-            mSavedChildFragmentManager.beginTransaction()
-                    .add(child, "tag")
-                    .commitNow();
-            assertEquals("argument strings don't match", "foo", child.getString());
+            ChildFragmentManagerChildFragment child =
+                    (ChildFragmentManagerChildFragment) mSavedChildFragmentManager
+                            .findFragmentByTag("tag");
+            if (child == null) {
+                child = new ChildFragmentManagerChildFragment("foo");
+                mSavedChildFragmentManager.beginTransaction()
+                        .add(child, "tag")
+                        .commitNow();
+                assertEquals("argument strings don't match", "foo", child.getString());
+            }
+            mChildFragment = child;
             return new TextView(container.getContext());
         }
+
+        @Nullable
+        public Fragment getChildFragment() {
+            return mChildFragment;
+        }
     }
 
     public static class ChildFragmentManagerChildFragment extends StrictFragment {
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 f7c8a81..6a8ac05 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReceiveResultTest.java
@@ -36,6 +36,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for Fragment startActivityForResult and startIntentSenderForResult.
  */
@@ -127,6 +130,7 @@
                 mFragment.startActivityForResult(intent, requestCode);
             }
         });
+        assertTrue(mFragment.mResultReceiveLatch.await(1, TimeUnit.SECONDS));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
@@ -150,6 +154,7 @@
                 }
             }
         });
+        assertTrue(mFragment.mResultReceiveLatch.await(1, TimeUnit.SECONDS));
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
 
@@ -158,6 +163,7 @@
         int mRequestCode = -1;
         int mResultCode = 100;
         String mResultContent;
+        final CountDownLatch mResultReceiveLatch = new CountDownLatch(1);
 
         @Override
         public void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -165,6 +171,7 @@
             mRequestCode = requestCode;
             mResultCode = resultCode;
             mResultContent = data.getStringExtra(FragmentResultActivity.EXTRA_RESULT_CONTENT);
+            mResultReceiveLatch.countDown();
         }
     }
 }
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/FragmentTransitionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
index 78a9acf..bc8e9e1 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,382 +15,879 @@
  */
 package android.support.v4.app;
 
+import static junit.framework.Assert.assertNull;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 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.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
+import android.graphics.Rect;
+import android.os.Build;
 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.support.v4.view.ViewCompat;
+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;
 
-@RunWith(AndroidJUnit4.class)
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 @MediumTest
+@RunWith(Parameterized.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
 public class FragmentTransitionTest {
+    private final boolean mOptimize;
+
+    @Parameterized.Parameters
+    public static Object[] data() {
+        return new Boolean[] {
+                false, true
+        };
+    }
+
     @Rule
     public ActivityTestRule<FragmentTestActivity> mActivityRule =
             new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
 
-    private TestFragment mStartFragment;
-    private TestFragment mMidFragment;
-    private TestFragment mEndFragment;
-    private FragmentTestActivity mActivity;
     private Instrumentation mInstrumentation;
+    private FragmentManager mFragmentManager;
+
+    public FragmentTransitionTest(final boolean optimize) {
+        mOptimize = optimize;
+    }
 
     @Before
-    public void setup() {
-        mActivity = mActivityRule.getActivity();
+    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 testFragmentTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final View sharedElement = mActivity.findViewById(R.id.hello);
-                assertEquals("source", ViewCompat.getTransitionName(sharedElement));
+    public void enterExitTransitions() throws Throwable {
+        // enter transition
+        TransitionFragment fragment = setupInitialFragment();
+        final View blue = findBlue();
+        final View green = findBlue();
 
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .addSharedElement(sharedElement, "destination")
-                        .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));
-        mActivityRule.runOnUiThread(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));
+        // 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 testFirstOutLastInTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        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)
-                        .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));
+    public void sharedElement() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
 
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
 
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+        verifyTransition(fragment1, fragment2, "blueSquare");
 
-        mStartFragment.clearNotifications();
-        mEndFragment.clearNotifications();
-
-        mActivityRule.runOnUiThread(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));
+        // Now pop the back stack
+        verifyPopTransition(1, fragment2, fragment1);
     }
 
+    // Test that shared element transitions through multiple fragments work together
     @Test
-    public void testPopTwo() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mMidFragment = TestFragment.create(R.layout.fragment_middle);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mMidFragment)
-                        .addToBackStack(null)
-                        .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        waitForEnd(mMidFragment, TestFragment.ENTER);
-        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();
-            }
-        });
-        waitForEnd(mEndFragment, TestFragment.ENTER);
-        assertTrue(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+    public void intermediateFragment() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
 
-        assertTrue(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+        final TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene3);
 
-        assertTrue(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertTrue(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+        verifyTransition(fragment1, fragment2, "shared");
 
-        mStartFragment.clearNotifications();
-        mMidFragment.clearNotifications();
-        mEndFragment.clearNotifications();
+        final TransitionFragment fragment3 = new TransitionFragment();
+        fragment3.setLayoutId(R.layout.scene2);
 
-        mActivityRule.runOnUiThread(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));
+        verifyTransition(fragment2, fragment3, "blueSquare");
 
-        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));
+        // 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 testNullTransition() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        mInstrumentation.waitForIdleSync();
-        mActivityRule.runOnUiThread(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));
+    public void removeAdded() throws Throwable {
+        final TransitionFragment fragment1 = setupInitialFragment();
 
-        mActivityRule.runOnUiThread(new Runnable() {
+        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() {
-                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)
+                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();
             }
         });
-        waitForStart(mEndFragment, TestFragment.ENTER);
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mEndFragment.wasEndCalled(TestFragment.REENTER));
+        FragmentTestUtil.waitForExecution(mActivityRule);
 
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.EXIT));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.ENTER));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.RETURN));
-        assertFalse(mStartFragment.wasEndCalled(TestFragment.REENTER));
+        // 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);
 
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.ENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.EXIT));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.REENTER));
-        assertFalse(mMidFragment.wasStartCalled(TestFragment.RETURN));
+        // Pop should also do the same thing
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
 
-        mActivityRule.runOnUiThread(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));
+        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);
     }
 
+    // Make sure that shared elements on two different fragment containers don't interact
     @Test
-    public void testRemoveAdded() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
-        }
-        launchStartFragment();
-        mActivityRule.runOnUiThread(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));
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.getSupportFragmentManager().popBackStack();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
-            }
-        });
-        assertTrue(waitForEnd(mStartFragment, TestFragment.REENTER));
+    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 testAddRemoved() throws Throwable {
-        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
-            return;
+    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());
+    }
+
+    // Ensure that transitions are done when a fragment is shown and hidden
+    @Test
+    public void showHideTransition() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .add(R.id.fragmentContainer, fragment2)
+                .hide(fragment1)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View endGreen = findViewById(fragment2, R.id.greenSquare);
+        final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+        assertEquals(View.GONE, fragment1.getView().getVisibility());
+        assertEquals(View.VISIBLE, startGreen.getVisibility());
+        assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+        verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+        verifyNoOtherTransitions(fragment1);
+
+        verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+        verifyNoOtherTransitions(fragment2);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        verifyAndClearTransition(fragment1.reenterTransition, null, startGreen, startBlue);
+        verifyNoOtherTransitions(fragment1);
+
+        assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
+        assertEquals(View.VISIBLE, startGreen.getVisibility());
+        assertEquals(View.VISIBLE, startBlue.getVisibility());
+
+        verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+        verifyNoOtherTransitions(fragment2);
+    }
+
+    // Ensure that transitions are done when a fragment is attached and detached
+    @Test
+    public void attachDetachTransition() throws Throwable {
+        TransitionFragment fragment1 = setupInitialFragment();
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .add(R.id.fragmentContainer, fragment2)
+                .detach(fragment1)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final View endGreen = findViewById(fragment2, R.id.greenSquare);
+        final View endBlue = findViewById(fragment2, R.id.blueSquare);
+
+        verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+        verifyNoOtherTransitions(fragment1);
+
+        verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+        verifyNoOtherTransitions(fragment2);
+
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final View reenterBlue = findBlue();
+        final View reenterGreen = findGreen();
+
+        verifyAndClearTransition(fragment1.reenterTransition, null, reenterGreen, reenterBlue);
+        verifyNoOtherTransitions(fragment1);
+
+        verifyAndClearTransition(fragment2.returnTransition, null, endGreen, endBlue);
+        verifyNoOtherTransitions(fragment2);
+    }
+
+    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();
+        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() {
-                mEndFragment = TestFragment.create(R.layout.fragment_end);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mEndFragment)
-                        .replace(R.id.content, mStartFragment)
+                mFragmentManager.beginTransaction()
+                        .setAllowOptimization(mOptimize)
+                        .addSharedElement(fromShared1, "blueSquare")
+                        .replace(R.id.fragmentContainer1, to1)
                         .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));
-        mActivityRule.runOnUiThread(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 {
-        mInstrumentation.waitForIdleSync();
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mStartFragment = TestFragment.create(R.layout.fragment_start);
-                mActivity.getSupportFragmentManager().beginTransaction()
-                        .replace(R.id.content, mStartFragment)
+                mFragmentManager.beginTransaction()
+                        .setAllowOptimization(mOptimize)
+                        .addSharedElement(fromShared2, sharedElementName)
+                        .replace(R.id.fragmentContainer2, to2)
+                        .addToBackStack(null)
                         .commit();
-                mActivity.getSupportFragmentManager().executePendingTransactions();
             }
         });
-        assertTrue(waitForEnd(mStartFragment, TestFragment.ENTER));
-        mStartFragment.clearNotifications();
+        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() {
+                mFragmentManager.popBackStack();
+                mFragmentManager.popBackStack();
+            }
+        });
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        from1.waitForTransition();
+        from2.waitForTransition();
+        to1.waitForTransition();
+        to2.waitForTransition();
+
+        final View returnEnter1 = findViewById(from1, R.id.greenSquare);
+        final View returnShared1 = findViewById(from1, R.id.blueSquare);
+
+        final View returnEnter2 = findViewById(from2, fromExitId2);
+        final View returnShared2 = findViewById(from2, fromSharedId2);
+
+        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);
+
+        verifyAndClearTransition(from1.reenterTransition, fromSharedRect1, returnEnter1);
+        verifyAndClearTransition(from2.reenterTransition, fromSharedRect2, returnEnter2);
+        verifyNoOtherTransitions(from1);
+        verifyNoOtherTransitions(from2);
     }
 
-    private boolean waitForStart(TestFragment fragment, int key) throws InterruptedException {
-        boolean started = fragment.waitForStart(key);
-        mInstrumentation.waitForIdleSync();
-        return started;
-    }
+    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);
 
-    private boolean waitForEnd(TestFragment fragment, int key) throws InterruptedException {
-        if (!waitForStart(fragment, key)) {
-            return false;
+        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);
         }
-        final boolean ended = fragment.waitForEnd(key);
-        mInstrumentation.waitForIdleSync();
-        return ended;
+        verifyAndClearTransition(from.sharedElementReturn, startSharedRect, startBlue, endBlue);
+        verifyNoOtherTransitions(from);
+
+        if (endRed != null) {
+            verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen, endRed);
+        } else {
+            verifyAndClearTransition(to.reenterTransition, endSharedRect, endGreen);
+        }
+        verifyNoOtherTransitions(to);
+
+        if (others != null) {
+            for (TransitionFragment fragment : others) {
+                verifyNoOtherTransitions(fragment);
+            }
+        }
+    }
+
+    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());
+    }
+
+    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();
+
+        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);
+        }
+
     }
 }
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/PostponedTransitionTest.java b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
new file mode 100644
index 0000000..b20298d
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
@@ -0,0 +1,813 @@
+/*
+ * 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.Build;
+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.filters.SdkSuppress;
+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)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+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/StrictFragment.java b/fragment/tests/java/android/support/v4/app/StrictFragment.java
index dfda814..7e57745 100644
--- a/fragment/tests/java/android/support/v4/app/StrictFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictFragment.java
@@ -139,7 +139,10 @@
         super.onSaveInstanceState(outState);
         mCalledOnSaveInstanceState = true;
         checkGetActivity();
-        checkStateAtLeast("onSaveInstanceState", STARTED);
+        // FIXME: We should not allow onSaveInstanceState except when STARTED or greater.
+        // But FragmentManager currently does it in saveAllState for fragments on the
+        // back stack, so fragments may be in the CREATED state.
+        checkStateAtLeast("onSaveInstanceState", CREATED);
     }
 
     @Override
diff --git a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
index e0bdf33..ecaa350 100644
--- a/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
+++ b/fragment/tests/java/android/support/v4/app/StrictViewFragment.java
@@ -17,21 +17,32 @@
 
 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;
+    }
+
+    public static StrictViewFragment create(int layoutId) {
+        StrictViewFragment fragment = new StrictViewFragment();
+        fragment.mLayoutId = layoutId;
+        return fragment;
+    }
 
     @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/fragment/tests/java/android/support/v4/app/TargetTracking.java b/fragment/tests/java/android/support/v4/app/TargetTracking.java
new file mode 100644
index 0000000..2639ff8
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/TargetTracking.java
@@ -0,0 +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.v4.app;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import java.util.ArrayList;
+
+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%
rename from documents-archive/AndroidManifest.xml
rename 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/build.gradle b/graphics/drawable/animated/build.gradle
index df11178..31cb13d 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -52,6 +52,10 @@
     testOptions {
         unitTests.returnDefaultValues = true
     }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
 }
 
 android.libraryVariants.all { variant ->
diff --git a/graphics/drawable/animated/proguard-rules.pro b/graphics/drawable/animated/proguard-rules.pro
new file mode 100644
index 0000000..4695a39
--- /dev/null
+++ b/graphics/drawable/animated/proguard-rules.pro
@@ -0,0 +1,19 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# keep setters in VectorDrawables so that animations can still work.
+-keepclassmembers class android.support.graphics.drawable.VectorDrawableCompat$* {
+   void set*(***);
+   *** get*();
+}
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/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/media-compat/Android.mk b/media-compat/Android.mk
index d1c2569..da9686c 100644
--- a/media-compat/Android.mk
+++ b/media-compat/Android.mk
@@ -14,109 +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 := 24
-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)
-
-# -----------------------------------------------------------------------
-
-# A helper sub-library that makes direct use of V25 APIs.
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-support-media-compat-api25
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-LOCAL_SRC_FILES := $(call all-java-files-under, api25)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-media-compat-api24
-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-api25
 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/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/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/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/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/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/widget/NestedRecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
index a9caf40..88b9992 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/NestedRecyclerViewActivity.java
@@ -17,6 +17,7 @@
 package com.example.android.supportv7.widget;
 
 import android.graphics.Color;
+import android.os.Parcelable;
 import android.support.v7.widget.DividerItemDecoration;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -47,7 +48,7 @@
 
     static class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> {
         public static class ViewHolder extends RecyclerView.ViewHolder {
-            public TextView mTextView;
+            TextView mTextView;
 
             public ViewHolder(TextView itemView) {
                 super(itemView);
@@ -57,7 +58,7 @@
 
         private String[] mData;
 
-        public InnerAdapter(String[] data) {
+        InnerAdapter(String[] data) {
             mData = data;
         }
 
@@ -93,15 +94,17 @@
         }
 
         ArrayList<InnerAdapter> mAdapters = new ArrayList<>();
+        ArrayList<Parcelable> mSavedStates = new ArrayList<>();
         RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();
 
-        public OuterAdapter(String[] data) {
+        OuterAdapter(String[] data) {
             int currentCharIndex = 0;
             char currentChar = data[0].charAt(0);
             for (int i = 1; i <= data.length; i++) {
                 if (i == data.length || data[i].charAt(0) != currentChar) {
                     mAdapters.add(new InnerAdapter(
                             Arrays.copyOfRange(data, currentCharIndex, i - 1)));
+                    mSavedStates.add(null);
                     if (i < data.length) {
                         currentChar = data[i].charAt(0);
                         currentCharIndex = i;
@@ -121,7 +124,20 @@
 
         @Override
         public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.mRecyclerView.setAdapter(mAdapters.get(position));
+            // Note: would be equally valid to replace adapter content instead of swapping adapter
+            holder.mRecyclerView.swapAdapter(mAdapters.get(position), true);
+
+            Parcelable savedState = mSavedStates.get(position);
+            if (savedState != null) {
+                holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState);
+                mSavedStates.set(position, null);
+            }
+        }
+
+        @Override
+        public void onViewRecycled(ViewHolder holder) {
+            mSavedStates.set(holder.getAdapterPosition(),
+                    holder.mRecyclerView.getLayoutManager().onSaveInstanceState());
         }
 
         @Override
diff --git a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
index c868430..3add119 100644
--- a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
+++ b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
@@ -64,6 +64,6 @@
             android:layout_width="match_parent"
             android:layout_height="56dp"
             android:layout_gravity="bottom"
-            android:background="#eee"
+            android:background="#fafafa"
             app:menu="@menu/sample_bottom_menu"/>
 </FrameLayout>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
index 4cfef7a..3442218 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
@@ -94,7 +94,7 @@
                             default:
                                 selectedItem.setText("Selected " + item.getTitle());
                         }
-                        return false;
+                        return true;
                     }
                 });
     }
diff --git a/transition/Android.mk b/transition/Android.mk
index 2de641a..c468ef1 100644
--- a/transition/Android.mk
+++ b/transition/Android.mk
@@ -14,91 +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_USE_AAPT2 := true
-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-compat
-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-compat
-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-compat
-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-compat
-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-compat
-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-transition \
-#       android-support-compat
+#       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_STATIC_ANDROID_LIBRARIES := android-support-transition-res
-LOCAL_SHARED_ANDROID_LIBRARIES := android-support-compat
+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 f06b0bd..4d4e96f 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -3,7 +3,8 @@
 archivesBaseName = 'transition'
 
 dependencies {
-    compile project(':support-compat')
+    compile project(':support-annotations')
+    compile project(':support-v4')
 
     androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
         exclude module: 'support-annotations'
@@ -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/v13/Android.mk b/v13/Android.mk
index 00675b7..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 := 24
-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 b550fe0..c6a50b4 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -14,111 +14,27 @@
 
 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-compat \
@@ -131,51 +47,30 @@
 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 5c279b5..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,9 +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;
@@ -25,6 +26,8 @@
 import android.view.Window;
 import android.view.animation.AnimationUtils;
 
+@RequiresApi(21)
+@TargetApi(21)
 final class TransitionHelperApi21 {
 
     TransitionHelperApi21() {
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 d7eea85..270f552 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'leanback-v17'
 
 dependencies {
@@ -21,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/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/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
index 98bcb3b..0bd03f1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -30,13 +30,34 @@
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
 
 final class DetailsOverviewSharedElementHelper extends SharedElementCallback {
 
-    static final String TAG = "DetailsOverviewSharedElementHelper";
+    static final String TAG = "DetailsTransitionHelper";
     static final boolean DEBUG = false;
 
+    static class TransitionTimeOutRunnable implements Runnable {
+        WeakReference<DetailsOverviewSharedElementHelper> mHelperRef;
+
+        TransitionTimeOutRunnable(DetailsOverviewSharedElementHelper helper) {
+            mHelperRef = new WeakReference<DetailsOverviewSharedElementHelper>(helper);
+        }
+
+        @Override
+        public void run() {
+            DetailsOverviewSharedElementHelper helper = mHelperRef.get();
+            if (helper == null) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
+            }
+            helper.startPostponedEnterTransition();
+        }
+    }
+
     ViewHolder mViewHolder;
     Activity mActivityToRunTransition;
     boolean mStartedPostpone;
@@ -180,18 +201,7 @@
         ActivityCompat.setEnterSharedElementCallback(mActivityToRunTransition, this);
         ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
         if (timeoutMs > 0) {
-            new Handler().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    if (mStartedPostpone) {
-                        return;
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "timeout " + mActivityToRunTransition);
-                    }
-                    startPostponedEnterTransition();
-                }
-            }, timeoutMs);
+            new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
         }
     }
 
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 96379d7..570a7f2 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
@@ -23,6 +23,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.lang.ref.WeakReference;
+
 /**
  * Helper class to assist delayed shared element activity transition for view created by
  * {@link FullWidthDetailsOverviewRowPresenter}. User must call
@@ -35,11 +37,31 @@
 public class FullWidthDetailsOverviewSharedElementHelper extends
         FullWidthDetailsOverviewRowPresenter.Listener {
 
-    static final String TAG = "FullWidthDetailsOverviewSharedElementHelper";
+    static final String TAG = "DetailsTransitionHelper";
     static final boolean DEBUG = false;
 
     private static final long DEFAULT_TIMEOUT = 5000;
 
+    static class TransitionTimeOutRunnable implements Runnable {
+        WeakReference<FullWidthDetailsOverviewSharedElementHelper> mHelperRef;
+
+        TransitionTimeOutRunnable(FullWidthDetailsOverviewSharedElementHelper helper) {
+            mHelperRef = new WeakReference<FullWidthDetailsOverviewSharedElementHelper>(helper);
+        }
+
+        @Override
+        public void run() {
+            FullWidthDetailsOverviewSharedElementHelper helper = mHelperRef.get();
+            if (helper == null) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
+            }
+            helper.startPostponedEnterTransition();
+        }
+    }
+
     ViewHolder mViewHolder;
     Activity mActivityToRunTransition;
     private boolean mStartedPostpone;
@@ -69,15 +91,7 @@
         setAutoStartSharedElementTransition(transition != null);
         ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
         if (timeoutMs > 0) {
-            new Handler().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    if (DEBUG) {
-                        Log.d(TAG, "timeout " + mActivityToRunTransition);
-                    }
-                    startPostponedEnterTransitionInternal();
-                }
-            }, timeoutMs);
+            new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
         }
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
index 7f28705..888cc8f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
@@ -184,11 +184,17 @@
         if (mGuidanceContainer != null) {
             CharSequence contentDescription = mGuidanceContainer.getContentDescription();
             if (TextUtils.isEmpty(contentDescription)) {
-                mGuidanceContainer.setContentDescription(new StringBuilder()
-                        .append(guidance.getBreadcrumb()).append('\n')
-                        .append(guidance.getTitle()).append('\n')
-                        .append(guidance.getDescription())
-                        .toString());
+                StringBuilder builder = new StringBuilder();
+                if (!TextUtils.isEmpty(guidance.getBreadcrumb())) {
+                    builder.append(guidance.getBreadcrumb()).append('\n');
+                }
+                if (!TextUtils.isEmpty(guidance.getTitle())) {
+                    builder.append(guidance.getTitle()).append('\n');
+                }
+                if (!TextUtils.isEmpty(guidance.getDescription())) {
+                    builder.append(guidance.getDescription()).append('\n');
+                }
+                mGuidanceContainer.setContentDescription(builder);
             }
         }
 
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
index 4f43de0..192b20e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepFragmentTest.java
@@ -171,17 +171,13 @@
             }
         });
         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));
@@ -189,6 +185,10 @@
         verify(second, times(2)).onStop();
         verify(second, times(2)).onDestroyView();
         verify(second, times(2)).onDestroy();
+        assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+        assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+        verify(first, times(2)).onCreate(any(Bundle.class));
+        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
                 any(Bundle.class), any(View.class));
     }
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
index 66ade14..879292f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/GuidedStepSupportFragmentTest.java
@@ -174,17 +174,13 @@
             }
         });
         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));
@@ -192,6 +188,10 @@
         verify(second, times(2)).onStop();
         verify(second, times(2)).onDestroyView();
         verify(second, times(2)).onDestroy();
+        assertEquals("modified text", first.getFragment().findActionById(1001).getTitle());
+        assertEquals("text", first.getFragment().findActionById(1002).getTitle());
+        verify(first, times(2)).onCreate(any(Bundle.class));
+        verify(first, times(2)).onCreateActions(any(List.class), any(Bundle.class));
         verify(first, times(2)).onCreateView(any(LayoutInflater.class), any(ViewGroup.class),
                 any(Bundle.class), any(View.class));
     }
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/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/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/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..8f5a1d2 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";
@@ -231,7 +235,7 @@
             return true;
         } else {
             if (DEBUG) {
-                Log.d(TAG, "applyNightMode() | Night mode has not changed. Skipping");
+                Log.d(TAG, "applyNightMode() | Skipping. Night mode has not changed: " + mode);
             }
         }
         return false;
@@ -321,6 +325,7 @@
 
         @ApplyableNightMode
         final int getApplyableNightMode() {
+            mIsNight = mTwilightManager.isNight();
             return mIsNight ? MODE_NIGHT_YES : MODE_NIGHT_NO;
         }
 
@@ -336,7 +341,7 @@
             cleanup();
 
             // If we're set to AUTO, we register a receiver to be notified on time changes. The
-            // system only send the tick out every minute, but that's enough fidelity for our use
+            // system only sends the tick out every minute, but that's enough fidelity for our use
             // case
             if (mAutoTimeChangeReceiver == null) {
                 mAutoTimeChangeReceiver = new BroadcastReceiver() {
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 899d8b1..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 {
 
@@ -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/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/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
index b021ade..905521d 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatVectorDrawableIntegrationTest.java
@@ -73,8 +73,9 @@
         assertEquals("Left side should be white", Color.red(leftColor), 255);
         assertEquals("Right side should be black", Color.red(rightColor), 0);
 
-        if (Build.VERSION.SDK_INT >= 17) {
-            // setLayoutDirection is only available after API 17.
+        if (Build.VERSION.SDK_INT >= 19) {
+            // setLayoutDirection is only available after API 17. However, it correctly set its
+            // drawable's layout direction until API 19.
             view1.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
             vectorDrawable.draw(mCanvas);
 
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 c7246d8..9533f38 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithToolbar.java
@@ -21,13 +21,13 @@
 
 import org.junit.Test;
 
+@SmallTest
 public class BasicsTestCaseWithToolbar extends BaseBasicsTestCase<ToolbarAppCompatActivity> {
     public BasicsTestCaseWithToolbar() {
         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 35cc04c..aee6a50 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BasicsTestCaseWithWindowDecor.java
@@ -21,13 +21,13 @@
 
 import org.junit.Test;
 
+@SmallTest
 public class BasicsTestCaseWithWindowDecor extends BaseBasicsTestCase<WindowDecorAppCompatActivity> {
     public BasicsTestCaseWithWindowDecor() {
         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/NightModeTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
index a9c8dc2..8dd1074 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/NightModeTestCase.java
@@ -27,10 +27,10 @@
 
 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;
@@ -123,6 +123,40 @@
         onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
     }
 
+    @Test
+    public void testNightModeAutoRecreatesOnResume() 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();
+
+        // Set MODE_NIGHT_AUTO so that we will change to night mode automatically
+        setLocalNightModeAndWaitForRecreate(activity, AppCompatDelegate.MODE_NIGHT_AUTO);
+        // Verify that we're currently in day mode
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_DAY)));
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final Instrumentation instrumentation = getInstrumentation();
+                // Now fool the Activity into thinking that it has gone into the background
+                instrumentation.callActivityOnPause(activity);
+                instrumentation.callActivityOnStop(activity);
+
+                // Now update the twilight manager while the Activity is in the 'background'
+                twilightManager.setIsNight(true);
+
+                // Now tell the Activity that it has gone into the foreground again
+                instrumentation.callActivityOnStart(activity);
+                instrumentation.callActivityOnResume(activity);
+            }
+        });
+
+        // finally check that the text has changed, signifying that night resources are being used
+        onView(withId(R.id.text_night_mode)).check(matches(withText(STRING_NIGHT)));
+    }
+
     private static class FakeTwilightManager extends TwilightManager {
         private boolean mIsNight;
 
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/mediarouter/Android.mk b/v7/mediarouter/Android.mk
index 83dedec..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 := 24
-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 := current
-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/values/attrs.xml b/v7/mediarouter/res/values/attrs.xml
index 5ebc0ee..cf6a7b5 100644
--- a/v7/mediarouter/res/values/attrs.xml
+++ b/v7/mediarouter/res/values/attrs.xml
@@ -21,6 +21,8 @@
              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" />
@@ -36,4 +38,6 @@
     <attr name="mediaRouteSpeakerIconDrawable" format="reference" />
     <attr name="mediaRouteSpeakerGroupIconDrawable" format="reference" />
     <attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
+
+    <attr name="mediaRouteTheme" format="reference" />
 </resources>
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/MediaRouterThemeHelper.java b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
index e78c516..23f3bad 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouterThemeHelper.java
@@ -71,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/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 61334f9..55fe020 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -37,6 +37,7 @@
 
     defaultConfig {
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        minSdkVersion 9
     }
 
     sourceSets {
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/src/android/support/v7/widget/GapWorker.java b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
index 3631e7d..ab51de7 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GapWorker.java
@@ -24,7 +24,7 @@
 import java.util.Comparator;
 import java.util.concurrent.TimeUnit;
 
-class GapWorker implements Runnable {
+final class GapWorker implements Runnable {
 
     static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
 
@@ -56,7 +56,7 @@
     private ArrayList<Task> mTasks = new ArrayList<>();
 
     /**
-     * Prefetch information associated with a specfic RecyclerView.
+     * Prefetch information associated with a specific RecyclerView.
      */
     static class PrefetchRegistryImpl implements RecyclerView.PrefetchRegistry {
         private int mPrefetchDx;
@@ -70,7 +70,7 @@
             mPrefetchDy = dy;
         }
 
-        void collectPrefetchPositionsFromView(RecyclerView view) {
+        void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
             mCount = 0;
             if (mPrefetchArray != null) {
                 Arrays.fill(mPrefetchArray, -1);
@@ -79,9 +79,21 @@
             final RecyclerView.LayoutManager layout = view.mLayout;
             if (view.mAdapter != null
                     && layout != null
-                    && layout.isItemPrefetchEnabled()
-                    && !view.hasPendingAdapterUpdates()) {
-                layout.collectPrefetchPositions(mPrefetchDx, mPrefetchDy, view.mState, this);
+                    && layout.isItemPrefetchEnabled()) {
+                if (nested) {
+                    // nested prefetch, only if no adapter updates pending. Note: we don't query
+                    // view.hasPendingAdapterUpdates(), as first layout may not have occurred
+                    if (!view.mAdapterHelper.hasPendingUpdates()) {
+                        layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
+                    }
+                } else {
+                    // momentum based prefetch, only if we trust current child/adapter state
+                    if (!view.hasPendingAdapterUpdates()) {
+                        layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
+                                view.mState, this);
+                    }
+                }
+
                 if (mCount > layout.mPrefetchMaxCountObserved) {
                     layout.mPrefetchMaxCountObserved = mCount;
                     view.mRecycler.updateViewCacheSize();
@@ -195,7 +207,7 @@
         int totalTaskCount = 0;
         for (int i = 0; i < viewCount; i++) {
             RecyclerView view = mRecyclerViews.get(i);
-            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view);
+            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
             totalTaskCount += view.mPrefetchRegistry.mCount;
         }
 
@@ -244,36 +256,67 @@
         return false;
     }
 
+    private RecyclerView.ViewHolder flushWorkWithDeadline(RecyclerView view,
+            int position, long deadlineNs) {
+        if (isPrefetchPositionAttached(view, position)) {
+            // don't attempt to prefetch attached views
+            return null;
+        }
+
+        RecyclerView.Recycler recycler = view.mRecycler;
+        RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
+                position, false, 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, false);
+            }
+        }
+        return holder;
+    }
+
+    private void flushTaskWithDeadline(Task task, long deadlineNs) {
+        long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
+        RecyclerView.ViewHolder holder = flushWorkWithDeadline(task.view,
+                task.position, taskDeadlineNs);
+        if (holder != null && holder.mNestedRecyclerView != null) {
+            // do nested prefetch!
+            final RecyclerView innerView = holder.mNestedRecyclerView;
+            final PrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
+            innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
+
+            if (innerPrefetchRegistry.mCount != 0) {
+                try {
+                    TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG);
+                    innerView.mState.prepareForNestedPrefetch(innerView.mAdapter);
+                    for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) {
+                        // Note that we ignore immediate flag for inner items because
+                        // we have lower confidence they're needed next frame.
+                        final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i];
+                        flushWorkWithDeadline(innerView, innerPosition, deadlineNs);
+                    }
+                } finally {
+                    TraceCompat.endSection();
+                }
+            }
+        }
+    }
+
     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;
+                break; // done with populated tasks
             }
-
-            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);
-                }
-            }
+            flushTaskWithDeadline(task, deadlineNs);
             task.clear();
         }
     }
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 1f4682f..ceabf01 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -144,6 +144,11 @@
     private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult();
 
     /**
+     * Number of items to prefetch when first coming on screen with new data.
+     */
+    private int mInitialItemPrefetchCount = 2;
+
+    /**
      * Creates a vertical LinearLayoutManager
      *
      * @param context Current context, will be used to access resources.
@@ -1190,9 +1195,56 @@
         }
     }
 
+    /**
+     * TODO: we expand cache by largest prefetch seen - not appropriate for nested prefetch?
+     * @hide
+     */
+    @Override
+    public void collectInitialPrefetchPositions(int adapterItemCount,
+            RecyclerView.PrefetchRegistry prefetchRegistry) {
+        final boolean fromEnd;
+        final int anchorPos;
+        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
+            // use restored state, since it hasn't been resolved yet
+            fromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
+            anchorPos = mPendingSavedState.mAnchorPosition;
+        } else {
+            resolveShouldLayoutReverse();
+            fromEnd = mShouldReverseLayout;
+            if (mPendingScrollPosition == NO_POSITION) {
+                anchorPos = fromEnd ? adapterItemCount - 1 : 0;
+            } else {
+                anchorPos = mPendingScrollPosition;
+            }
+        }
+
+        final int direction = fromEnd
+                ? LayoutState.ITEM_DIRECTION_HEAD
+                : LayoutState.ITEM_DIRECTION_TAIL;
+        int targetPos = anchorPos;
+        for (int i = 0; i < mInitialItemPrefetchCount; i++) {
+            if (targetPos >= 0 && targetPos < adapterItemCount) {
+                prefetchRegistry.addPosition(targetPos, 0);
+            } else {
+                break; // no more to prefetch
+            }
+            targetPos += direction;
+        }
+    }
+
+    /** @hide */
+    public void setInitialPrefetchItemCount(int itemCount) {
+        mInitialItemPrefetchCount = itemCount;
+    }
+
+    /** @hide */
+    public int getInitialItemPrefetchCount() {
+        return mInitialItemPrefetchCount;
+    }
+
     /** @hide */
     @Override
-    public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+    public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
             RecyclerView.PrefetchRegistry prefetchRegistry) {
         int delta = (mOrientation == HORIZONTAL) ? dx : dy;
         if (getChildCount() == 0 || delta == 0) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 11e94e5..dc6fb47 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -182,6 +182,12 @@
      */
     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;
     public static final int VERTICAL = 1;
@@ -250,6 +256,12 @@
     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
 
     /**
+     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
+     * RecyclerView.
+     */
+    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
+
+    /**
      * RecyclerView is creating a new View.
      * If too many of these present in Systrace:
      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
@@ -1575,6 +1587,7 @@
                         | AdapterHelper.UpdateOp.MOVE)) {
             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
             eatRequestLayout();
+            onEnterLayoutOrScroll();
             mAdapterHelper.preProcess();
             if (!mLayoutRequestEaten) {
                 if (hasUpdatedView()) {
@@ -1585,6 +1598,7 @@
                 }
             }
             resumeRequestLayout(true);
+            onExitLayoutOrScroll();
             TraceCompat.endSection();
         } else if (mAdapterHelper.hasPendingUpdates()) {
             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
@@ -2234,6 +2248,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;
@@ -2241,6 +2259,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();
@@ -2406,9 +2428,11 @@
                 // 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();
+                if (display != null) {
+                    float displayRefreshRate = display.getRefreshRate();
+                    if (displayRefreshRate >= 30.0f) {
+                        refreshRate = displayRefreshRate;
+                    }
                 }
                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
                 GapWorker.sGapWorker.set(mGapWorker);
@@ -4947,6 +4971,50 @@
         }
     }
 
+    /**
+     * Utility method for finding an internal RecyclerView, if present
+     */
+    @Nullable
+    static RecyclerView findNestedRecyclerView(@NonNull View view) {
+        if (!(view instanceof ViewGroup)) {
+            return null;
+        }
+        if (view instanceof RecyclerView) {
+            return (RecyclerView) view;
+        }
+        final ViewGroup parent = (ViewGroup) view;
+        final int count = parent.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = parent.getChildAt(i);
+            final RecyclerView descendant = findNestedRecyclerView(child);
+            if (descendant != null) {
+                return descendant;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method for clearing holder's internal RecyclerView, if present
+     */
+    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
+        if (holder.mNestedRecyclerView != null) {
+            View item = holder.mNestedRecyclerView;
+            while (item != null) {
+                if (item == holder.itemView) {
+                    return; // match found, don't need to clear
+                }
+
+                ViewParent parent = item.getParent();
+                if (parent instanceof View) {
+                    item = (View) parent;
+                } else {
+                    item = null;
+                }
+            }
+            holder.mNestedRecyclerView = null; // not nested
+        }
+    }
 
     /**
      * Time base for deadline-aware work scheduling. Overridable for testing.
@@ -5310,6 +5378,11 @@
                         return null;
                     }
                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
+                    if (ALLOW_THREAD_GAP_WORK) {
+                        // only bother finding nested RV if prefetching
+                        holder.mNestedRecyclerView = findNestedRecyclerView(holder.itemView);
+                    }
+
                     long end = getNanoTime();
                     mRecyclerPool.factorInCreateTime(type, end - start);
                     if (DEBUG) {
@@ -5468,7 +5541,7 @@
             if (DEBUG) {
                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
             }
-            addViewHolderToRecycledViewPool(viewHolder);
+            addViewHolderToRecycledViewPool(viewHolder, true);
             mCachedViews.remove(cachedViewIndex);
         }
 
@@ -5536,12 +5609,15 @@
                     cached = true;
                 }
                 if (!cached) {
-                    addViewHolderToRecycledViewPool(holder);
+                    addViewHolderToRecycledViewPool(holder, true);
                     recycled = true;
                 }
-            } else if (DEBUG) {
-                Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
-                        + "re-visit here. We are still removing it from animation lists");
+            } else {
+                holder.mNestedRecyclerView = null;
+                if (DEBUG) {
+                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
+                            + "re-visit here. We are still removing it from animation lists");
+                }
             }
             // even if the holder is not removed, we still call this method so that it is removed
             // from view holder lists.
@@ -5551,9 +5627,20 @@
             }
         }
 
-        void addViewHolderToRecycledViewPool(ViewHolder holder) {
+        /**
+         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
+         *
+         * Pass false to dispatchRecycled for views that have not been bound.
+         *
+         * @param holder Holder to be added to the pool.
+         * @param dispatchRecycled True to dispatch View recycled callbacks.
+         */
+        void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
+            clearNestedRecyclerViewIfNotNested(holder);
             ViewCompat.setAccessibilityDelegate(holder.itemView, null);
-            dispatchViewRecycled(holder);
+            if (dispatchRecycled) {
+                dispatchViewRecycled(holder);
+            }
             holder.mOwnerRecyclerView = null;
             getRecycledViewPool().putRecycledView(holder);
         }
@@ -6609,7 +6696,8 @@
 
         /**
          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
-         * requested by a {@link #collectPrefetchPositions(int, int, State, PrefetchRegistry)} call.
+         * requested by a {@link #collectInitialPrefetchPositions(int, PrefetchRegistry)} or
+         * {@link #collectAdjacentPrefetchPositions(int, int, State, PrefetchRegistry)} call.
          */
         int mPrefetchMaxCountObserved;
 
@@ -6935,15 +7023,16 @@
         }
 
         /**
-         * Gather all positions from the LayoutManager to be prefetched.
+         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
          *
          * <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>
+         * item to be prepared, and these positions will have their ViewHolders created and bound,
+         * if there is sufficient time available, in advance of being needed by a
+         * scroll or layout.</p>
          *
          * @param dx X movement component.
          * @param dy Y movement component.
@@ -6951,10 +7040,41 @@
          * @param prefetchRegistry PrefetchRegistry to add prefetch entries into.
          *
          * @see #isItemPrefetchEnabled()
+         * @see #collectInitialPrefetchPositions(int, PrefetchRegistry)
          *
          * @hide
          */
-        public void collectPrefetchPositions(int dx, int dy, State state,
+        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
+                PrefetchRegistry prefetchRegistry) {}
+
+        /**
+         * Gather all positions from the LayoutManager to be prefetched in preperation for its
+         * Recyclerview to come on screen, due to the movement of another, containing RecyclerView.
+         *
+         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
+         *
+         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
+         * LayoutManager, this method is called in between draw traversals to gather
+         * which positions this LayoutManager will first need, once it appears on the screen.</p>
+         *
+         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
+         * vertically scrolling LayoutManager, this method would be called when the horizontal list
+         * is about to come onscreen.</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,
+         * if there is sufficient time available, in advance of being needed by a
+         * scroll or layout.</p>
+         *
+         * @param adapterItemCount number of items in the associated adapter.
+         * @param prefetchRegistry PrefetchRegistry to add prefetch entries into.
+         *
+         * @see #isItemPrefetchEnabled()
+         * @see #collectAdjacentPrefetchPositions(int, int, State, PrefetchRegistry
+         *
+         * @hide
+         */
+        public void collectInitialPrefetchPositions(int adapterItemCount,
                 PrefetchRegistry prefetchRegistry) {}
 
         void dispatchAttachedToWindow(RecyclerView view) {
@@ -9652,6 +9772,7 @@
      */
     public static abstract class ViewHolder {
         public final View itemView;
+        RecyclerView mNestedRecyclerView;
         int mPosition = NO_POSITION;
         int mOldPosition = NO_POSITION;
         long mItemId = NO_ID;
@@ -10042,6 +10163,7 @@
             clearPayload();
             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
+            clearNestedRecyclerViewIfNotNested(this);
         }
 
         /**
@@ -10946,23 +11068,15 @@
             }
         }
 
-        @IntDef(flag = true, value = {
-                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        @interface LayoutState {}
 
+        /** Owned by SmoothScroller */
         private int mTargetPosition = RecyclerView.NO_POSITION;
 
-        @LayoutState
-        int mLayoutStep = STEP_START;
-
         private SparseArray<Object> mData;
 
-        /**
-         * Number of items adapter has.
-         */
-        int mItemCount = 0;
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below are carried from one layout pass to the next
+        ////////////////////////////////////////////////////////////////////////////////////////////
 
         /**
          * Number of items adapter had in the previous layout.
@@ -10975,18 +11089,40 @@
          */
         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
 
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below must be updated or cleared before they are used (generally before a pass)
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        @IntDef(flag = true, value = {
+                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface LayoutState {}
+
+        @LayoutState
+        int mLayoutStep = STEP_START;
+
+        /**
+         * Number of items adapter has.
+         */
+        int mItemCount = 0;
+
         boolean mStructureChanged = false;
 
         boolean mInPreLayout = false;
 
-        boolean mRunSimpleAnimations = false;
-
-        boolean mRunPredictiveAnimations = false;
-
         boolean mTrackOldChangeHolders = false;
 
         boolean mIsMeasuring = false;
 
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below are always reset outside of the pass (or passes) that use them
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        boolean mRunSimpleAnimations = false;
+
+        boolean mRunPredictiveAnimations = false;
+
         /**
          * This data is saved before a layout calculation happens. After the layout is finished,
          * if the previously focused view has been replaced with another view for the same item, we
@@ -10998,6 +11134,8 @@
         // that one instead
         int mFocusedSubChildId;
 
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
         State reset() {
             mTargetPosition = RecyclerView.NO_POSITION;
             if (mData != null) {
@@ -11010,6 +11148,22 @@
         }
 
         /**
+         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
+         * prior to any layout passes.
+         *
+         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
+         * that Recycler#getViewForPosition() can function safely.</p>
+         */
+        void prepareForNestedPrefetch(Adapter adapter) {
+            mLayoutStep = STEP_START;
+            mItemCount = adapter.getItemCount();
+            mStructureChanged = false;
+            mInPreLayout = false;
+            mTrackOldChangeHolders = false;
+            mIsMeasuring = false;
+        }
+
+        /**
          * Returns true if the RecyclerView is currently measuring the layout. This value is
          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
          * has non-exact measurement specs.
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index 7040bcc..62a7f09 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -213,8 +213,8 @@
     private boolean mSmoothScrollbarEnabled = true;
 
     /**
-     * Temporary array used (solely in {@link #collectPrefetchPositions}) for stashing and sorting
-     * distances to views being prefetched.
+     * Temporary array used (solely in {@link #collectAdjacentPrefetchPositions}) for stashing and
+     * sorting distances to views being prefetched.
      */
     private int[] mPrefetchDistances;
 
@@ -2073,7 +2073,7 @@
 
     /** @hide */
     @Override
-    public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+    public void collectAdjacentPrefetchPositions(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
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 9137747..1bb4fc3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
@@ -90,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);
 
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 0a28c2b..3ac53f4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.util.AsyncListUtil;
 import android.view.View;
@@ -60,6 +61,8 @@
     public int mStartPrefetch = 0;
     public int mEndPrefetch = 0;
 
+    // Test is disabled as it is flaky.
+    @Suppress
     @Test
     public void asyncListUtil() throws Throwable {
         mRecyclerView = inflateWrappedRV();
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 8d93553..4eee5de 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseGridLayoutManagerTest.java
@@ -318,10 +318,10 @@
         }
 
         @Override
-        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+        public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                 RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+            super.collectAdjacentPrefetchPositions(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 9bea02c..505b1c3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
@@ -635,10 +635,10 @@
         }
 
         @Override
-        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+        public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                 RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+            super.collectAdjacentPrefetchPositions(dx, dy, state, prefetchRegistry);
         }
     }
 }
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 ea88391..59175ac 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseStaggeredGridLayoutManagerTest.java
@@ -805,10 +805,10 @@
         }
 
         @Override
-        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+        public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                 RecyclerView.PrefetchRegistry prefetchRegistry) {
             if (prefetchLatch != null) prefetchLatch.countDown();
-            super.collectPrefetchPositions(dx, dy, state, prefetchRegistry);
+            super.collectAdjacentPrefetchPositions(dx, dy, state, prefetchRegistry);
         }
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
index 0bd67f7..70a8644 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/CacheUtils.java
@@ -28,7 +28,7 @@
     static void verifyPositionsPrefetched(RecyclerView view, int dx, int dy,
             Integer[]... positionData) {
         RecyclerView.PrefetchRegistry prefetchRegistry = mock(RecyclerView.PrefetchRegistry.class);
-        view.mLayout.collectPrefetchPositions(
+        view.mLayout.collectAdjacentPrefetchPositions(
                 dx, dy, view.mState, prefetchRegistry);
 
         verify(prefetchRegistry, times(positionData.length)).addPosition(anyInt(), anyInt());
@@ -78,4 +78,15 @@
             }
         }
     }
+
+    static RecyclerView.ViewHolder peekAtCachedViewForPosition(RecyclerView view, int position) {
+        for (int i = 0; i < view.mRecycler.mCachedViews.size(); i++) {
+            RecyclerView.ViewHolder holder = view.mRecycler.mCachedViews.get(i);
+            if (holder.mPosition == position) {
+                return holder;
+            }
+        }
+        fail("Unable to find view with position " + position);
+        return null;
+    }
 }
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 7bed232..55bed7e 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/FocusSearchNavigationTest.java
@@ -28,6 +28,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
@@ -60,8 +61,8 @@
 @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;
@@ -73,11 +74,19 @@
 
     @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;
@@ -91,11 +100,11 @@
             @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);
@@ -115,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
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
index d25526f..5d08931 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GapWorkerTest.java
@@ -22,7 +22,9 @@
 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;
 
@@ -129,4 +131,17 @@
         assertEquals(200, list.get(2).distanceToItem);
         assertEquals(900, list.get(3).distanceToItem);
     }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+    @Test
+    public void gapWorkerWithoutLayout() {
+        RecyclerView recyclerView = new RecyclerView(getContext());
+        try {
+            assertFalse(recyclerView.mIsAttached);
+            recyclerView.onAttachedToWindow();
+            recyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        } finally {
+            recyclerView.onDetachedFromWindow();
+        }
+    }
 }
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 1c436d7..22adefd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -19,7 +19,9 @@
 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;
@@ -35,6 +37,7 @@
 @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/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 38b118e..5379354 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -432,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() {
@@ -1392,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];
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 fff41f5..48abe68 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -40,6 +40,7 @@
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import org.junit.Before;
@@ -368,7 +369,7 @@
         mRecyclerView.setAdapter(new MockAdapter(20));
         MockLayoutManager mlm = new MockLayoutManager() {
             @Override
-            public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+            public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                     RecyclerView.PrefetchRegistry prefetchManager) {
                 prefetchManager.addPosition(0, 0);
                 prefetchManager.addPosition(1, 0);
@@ -387,7 +388,7 @@
             mRecyclerView.layout(0, 0, 100, 100);
 
             // prefetch gets 3 items, so expands cache by 3
-            mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView);
+            mRecyclerView.mPrefetchRegistry.collectPrefetchPositionsFromView(mRecyclerView, false);
             assertEquals(3, mRecyclerView.mPrefetchRegistry.mCount);
             assertEquals(RecyclerView.Recycler.DEFAULT_CACHE_SIZE + 3, recycler.mViewCacheMax);
 
@@ -414,6 +415,49 @@
         }
     }
 
+    @Test
+    public void findNestedRecyclerView() {
+        RecyclerView recyclerView = new RecyclerView(getContext());
+        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(recyclerView));
+
+        ViewGroup parent = new FrameLayout(getContext());
+        assertEquals(null, RecyclerView.findNestedRecyclerView(parent));
+        parent.addView(recyclerView);
+        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(parent));
+
+        ViewGroup grandParent = new FrameLayout(getContext());
+        assertEquals(null, RecyclerView.findNestedRecyclerView(grandParent));
+        grandParent.addView(parent);
+        assertEquals(recyclerView, RecyclerView.findNestedRecyclerView(grandParent));
+    }
+
+    @Test
+    public void clearNestedRecyclerViewIfNotNested() {
+        RecyclerView recyclerView = new RecyclerView(getContext());
+        ViewGroup parent = new FrameLayout(getContext());
+        parent.addView(recyclerView);
+        ViewGroup grandParent = new FrameLayout(getContext());
+        grandParent.addView(parent);
+
+        // verify trivial noop case
+        RecyclerView.ViewHolder holder = new RecyclerView.ViewHolder(recyclerView) {};
+        holder.mNestedRecyclerView = recyclerView;
+        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+        assertEquals(recyclerView, holder.mNestedRecyclerView);
+
+        // verify clear case
+        holder = new RecyclerView.ViewHolder(new View(getContext())) {};
+        holder.mNestedRecyclerView = recyclerView;
+        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+        assertNull(holder.mNestedRecyclerView);
+
+        // verify more deeply nested case
+        holder = new RecyclerView.ViewHolder(grandParent) {};
+        holder.mNestedRecyclerView = recyclerView;
+        RecyclerView.clearNestedRecyclerViewIfNotNested(holder);
+        assertEquals(recyclerView, holder.mNestedRecyclerView);
+    }
+
     static class MockLayoutManager extends RecyclerView.LayoutManager {
 
         int mLayoutCount = 0;
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 755173d..a9a67b5 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,12 @@
 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.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -29,6 +31,7 @@
 
 import android.content.Context;
 import android.os.Build;
+import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
@@ -36,6 +39,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -43,6 +48,7 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -109,7 +115,7 @@
             }
 
             @Override
-            public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+            public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                     RecyclerView.PrefetchRegistry prefetchManager) {
                 prefetchManager.addPosition(0, 0);
                 prefetchManager.addPosition(1, 0);
@@ -151,7 +157,7 @@
                     any(RecyclerView.ViewHolder.class), anyInt(), any(List.class));
 
             assertTrue(mRecycler.mCachedViews.size() == 3);
-            CacheUtils.verifyCacheContainsPositions(mRecyclerView, 0, 1, 2);
+            CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 0, 1, 2);
         }
     }
 
@@ -181,7 +187,7 @@
         mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
         assertEquals(5, mRecyclerView.mRecycler.mViewCacheMax);
 
-        CacheUtils.verifyCacheContainsPositions(mRecyclerView, 3, 4, 5);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(mRecyclerView, 3, 4, 5);
 
         // further views recycled, as though from scrolling, shouldn't evict prefetched views:
         mRecycler.recycleView(mRecycler.getViewForPosition(10));
@@ -308,6 +314,62 @@
     }
 
     @Test
+    public void partialPrefetchAvoidsViewRecycledCallback() {
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+        // 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;
+            }
+
+            @Override
+            public void onViewRecycled(RecyclerView.ViewHolder holder) {
+                // verify unbound view doesn't get
+                assertNotEquals(RecyclerView.NO_POSITION, holder.getAdapterPosition());
+            }
+        };
+        mRecyclerView.setAdapter(adapter);
+
+        layout(100, 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 10 ms to inflate + bind, so just give it 9 so it doesn't have time to bind
+        final long deadlineNs = mRecyclerView.getNanoTime() + TimeUnit.MILLISECONDS.toNanos(9);
+
+        // Timed prefetch
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+        mRecyclerView.mGapWorker.prefetch(deadlineNs);
+
+        // will have enough time to inflate but not bind one view
+        assertTrue(mRecycler.mCachedViews.size() == 0);
+        assertTrue(mRecyclerView.getRecycledViewPool().getRecycledViewCount(0) == 1);
+        RecyclerView.ViewHolder pooledHolder = mRecyclerView.getRecycledViewPool()
+                .mScrap.get(0).mScrapHeap.get(0);
+        assertEquals(RecyclerView.NO_POSITION, pooledHolder.getAdapterPosition());
+    }
+
+    @Test
     public void prefetchStaggeredItemsPriority() {
         StaggeredGridLayoutManager sglm =
                 new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
@@ -392,8 +454,7 @@
         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
@@ -450,4 +511,204 @@
         assertEquals(2, llm.getChildCount());
         assertEquals(4, mRecyclerView.getChildCount());
     }
+
+    @Test
+    public void viewHolderFindsNestedRecyclerViews() {
+        LinearLayoutManager llm = new LinearLayoutManager(getContext());
+        mRecyclerView.setLayoutManager(llm);
+
+        RecyclerView.Adapter mockAdapter = mock(RecyclerView.Adapter.class);
+        when(mockAdapter.onCreateViewHolder(any(ViewGroup.class), anyInt()))
+                .thenAnswer(new Answer<RecyclerView.ViewHolder>() {
+                    @Override
+                    public RecyclerView.ViewHolder answer(InvocationOnMock invocation)
+                            throws Throwable {
+                        View view = new RecyclerView(getContext());
+                        view.setLayoutParams(new RecyclerView.LayoutParams(100, 100));
+                        return new RecyclerView.ViewHolder(view) {};
+                    }
+                });
+        when(mockAdapter.getItemCount()).thenReturn(100);
+        mRecyclerView.setAdapter(mockAdapter);
+
+        layout(100, 200);
+
+        verify(mockAdapter, times(2)).onCreateViewHolder(any(ViewGroup.class), anyInt());
+        verify(mockAdapter, times(2)).onBindViewHolder(
+                argThat(new BaseMatcher<RecyclerView.ViewHolder>() {
+                    @Override
+                    public boolean matches(Object item) {
+                        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) item;
+                        return holder.itemView == holder.mNestedRecyclerView;
+                    }
+
+                    @Override
+                    public void describeTo(Description description) { }
+                }),
+                anyInt(),
+                any(List.class));
+    }
+
+    static class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> {
+        private static final int INNER_ITEM_COUNT = 20;
+        static class ViewHolder extends RecyclerView.ViewHolder {
+            ViewHolder(View itemView) {
+                super(itemView);
+            }
+        }
+
+        InnerAdapter() {}
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            View view = new View(parent.getContext());
+            view.setLayoutParams(new RecyclerView.LayoutParams(100, 100));
+            return new ViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, int position) {}
+
+        @Override
+        public int getItemCount() {
+            return INNER_ITEM_COUNT;
+        }
+    }
+
+    static class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> {
+        private static final int OUTER_ITEM_COUNT = 10;
+
+        private boolean mReverseInner;
+
+        static class ViewHolder extends RecyclerView.ViewHolder {
+            private final RecyclerView mRecyclerView;
+            ViewHolder(RecyclerView itemView) {
+                super(itemView);
+                mRecyclerView = itemView;
+            }
+        }
+
+        ArrayList<InnerAdapter> mAdapters = new ArrayList<>();
+        ArrayList<Parcelable> mSavedStates = new ArrayList<>();
+        RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();
+
+        OuterAdapter() {
+            this(false);
+        }
+
+        OuterAdapter(boolean reverseInner) {
+            mReverseInner = reverseInner;
+            for (int i = 0; i <= OUTER_ITEM_COUNT; i++) {
+                mAdapters.add(new InnerAdapter());
+                mSavedStates.add(null);
+            }
+        }
+
+        @Override
+        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            RecyclerView rv = new RecyclerView(parent.getContext());
+            rv.setLayoutManager(new LinearLayoutManager(parent.getContext(),
+                    LinearLayoutManager.HORIZONTAL, mReverseInner));
+            rv.setRecycledViewPool(mSharedPool);
+            rv.setLayoutParams(new RecyclerView.LayoutParams(200, 100));
+            return new ViewHolder(rv);
+        }
+
+        @Override
+        public void onBindViewHolder(ViewHolder holder, int position) {
+            // Tests may rely on bound holders not being shared between inner adapters,
+            // since we force recycle here
+            holder.mRecyclerView.swapAdapter(mAdapters.get(position), true);
+
+            Parcelable savedState = mSavedStates.get(position);
+            if (savedState != null) {
+                holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState);
+                mSavedStates.set(position, null);
+            }
+        }
+
+        @Override
+        public void onViewRecycled(ViewHolder holder) {
+            mSavedStates.set(holder.getAdapterPosition(),
+                    holder.mRecyclerView.getLayoutManager().onSaveInstanceState());
+        }
+
+        @Override
+        public int getItemCount() {
+            return OUTER_ITEM_COUNT;
+        }
+    }
+
+    @Test
+    public void nestedPrefetchSimple() {
+        LinearLayoutManager llm = new LinearLayoutManager(getContext());
+        assertEquals(2, llm.getInitialItemPrefetchCount());
+
+        mRecyclerView.setLayoutManager(llm);
+        mRecyclerView.setAdapter(new OuterAdapter());
+
+        layout(200, 200);
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+
+        // prefetch 2 (default)
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2);
+        assertNotNull(holder);
+        assertNotNull(holder.mNestedRecyclerView);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 0, 1);
+
+        // prefetch 4
+        ((LinearLayoutManager) holder.mNestedRecyclerView.getLayoutManager())
+                .setInitialPrefetchItemCount(4);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 0, 1, 2, 3);
+    }
+
+    @Test
+    public void nestedPrefetchReverseInner() {
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mRecyclerView.setAdapter(new OuterAdapter(/* reverseInner = */ true));
+
+        layout(200, 200);
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, 1);
+
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        RecyclerView.ViewHolder holder = CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 2);
+
+        // anchor from right side, should see last two positions
+        CacheUtils.verifyCacheContainsPrefetchedPositions(holder.mNestedRecyclerView, 18, 19);
+    }
+
+    @Test
+    public void nestedPrefetchOffset() {
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mRecyclerView.setAdapter(new OuterAdapter());
+
+        layout(200, 200);
+
+        // Scroll top row by 5.5 items, verify positions 5, 6, 7 showing
+        RecyclerView inner = (RecyclerView) mRecyclerView.getChildAt(0);
+        inner.scrollBy(550, 0);
+        assertEquals(5, RecyclerView.getChildViewHolderInt(inner.getChildAt(0)).mPosition);
+        assertEquals(6, RecyclerView.getChildViewHolderInt(inner.getChildAt(1)).mPosition);
+        assertEquals(7, RecyclerView.getChildViewHolderInt(inner.getChildAt(2)).mPosition);
+
+        // scroll down 4 rows, up 3 so row 0 is adjacent but uncached
+        mRecyclerView.scrollBy(0, 400);
+        mRecyclerView.scrollBy(0, -300);
+
+        // top row no longer present
+        CacheUtils.verifyCacheDoesNotContainPositions(mRecyclerView, 0);
+
+        // prefetch upward, and validate that we've gotten the top row with correct offsets
+        mRecyclerView.mPrefetchRegistry.setPrefetchVector(0, -1);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        inner = (RecyclerView) CacheUtils.peekAtCachedViewForPosition(mRecyclerView, 0).itemView;
+        CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6);
+
+        // prefetch 4
+        ((LinearLayoutManager) inner.getLayoutManager()).setInitialPrefetchItemCount(4);
+        mRecyclerView.mGapWorker.prefetch(RecyclerView.FOREVER_NS);
+        CacheUtils.verifyCacheContainsPrefetchedPositions(inner, 5, 6, 7, 8);
+    }
 }
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 e861dbb..2c8fe28 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -97,6 +97,64 @@
     }
 
     @Test
+    public void triggerFocusSearchInOnRecycledCallback() throws Throwable {
+        final RecyclerView rv = new RecyclerView(getActivity()) {
+            @Override
+            void consumePendingUpdateOperations() {
+                try {
+                    super.consumePendingUpdateOperations();
+                } catch (Throwable t) {
+                    postExceptionToInstrumentation(t);
+                }
+            }
+        };
+        final AtomicBoolean receivedOnRecycled = new AtomicBoolean(false);
+        final TestAdapter adapter = new TestAdapter(20) {
+            @Override
+            public void onViewRecycled(TestViewHolder holder) {
+                super.onViewRecycled(holder);
+                if (receivedOnRecycled.getAndSet(true)) {
+                    return;
+                }
+                rv.focusSearch(rv.getChildAt(0), View.FOCUS_FORWARD);
+            }
+        };
+        final AtomicInteger layoutCnt = new AtomicInteger(5);
+        TestLayoutManager tlm = new TestLayoutManager() {
+            @Override
+            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                detachAndScrapAttachedViews(recycler);
+                layoutRange(recycler, 0, layoutCnt.get());
+                layoutLatch.countDown();
+            }
+        };
+        rv.setLayoutManager(tlm);
+        rv.setAdapter(adapter);
+        tlm.expectLayouts(1);
+        setRecyclerView(rv);
+        tlm.waitForLayout(2);
+
+        layoutCnt.set(4);
+        tlm.expectLayouts(1);
+        requestLayoutOnUIThread(rv);
+        tlm.waitForLayout(1);
+
+        assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(1));
+        tlm.expectLayouts(1);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                adapter.notifyItemChanged(4);
+                rv.smoothScrollBy(0, 1);
+            }
+        });
+        checkForMainThreadException();
+        tlm.waitForLayout(2);
+        assertThat("test sanity", rv.mRecycler.mCachedViews.size(), is(0));
+        assertThat(receivedOnRecycled.get(), is(true));
+    }
+
+    @Test
     public void detachAttachGetReadyWithoutChanges() throws Throwable {
         detachAttachGetReady(false, false, false);
     }
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 b3f44a8..3001240 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewPrefetchTest.java
@@ -57,7 +57,7 @@
         }
 
         @Override
-        public void collectPrefetchPositions(int dx, int dy, RecyclerView.State state,
+        public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
                 RecyclerView.PrefetchRegistry prefetchRegistry) {
             prefetchLatch.countDown();
             prefetchRegistry.addPosition(6, 0);
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 92245e3..828ffab 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerSnappingTest.java
@@ -33,6 +33,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@MediumTest
 @RunWith(Parameterized.class)
 public class StaggeredGridLayoutManagerSnappingTest extends BaseStaggeredGridLayoutManagerTest {
 
@@ -56,7 +57,6 @@
         return result;
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameViewFixedSize() throws Throwable {
         // This test is a special case for fixed sized children.
@@ -96,7 +96,6 @@
         assertCenterAligned(viewAfterScroll);
     }
 
-    @MediumTest
     @Test
     public void snapOnScrollSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -144,7 +143,6 @@
         assertCenterAligned(viewAfterScroll);
     }
 
-    @MediumTest
     @Test
     public void snapOnFlingSameView() throws Throwable {
         final Config config = (Config) mConfig.clone();
@@ -171,8 +169,6 @@
         assertCenterAligned(viewAfterFling);
     }
 
-
-    @MediumTest
     @Test
     public void snapOnFlingNextView() throws Throwable {
         final Config config = (Config) mConfig.clone();
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 abd9129..c251c5a 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
@@ -29,6 +29,7 @@
 import android.os.Build;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.util.PollingCheck;
 import android.support.v7.util.TouchUtils;
@@ -119,6 +120,8 @@
         basicSwipeTest(END, START | END, getActivity().getWindow().getDecorView().getWidth());
     }
 
+    // Test is disabled as it is flaky.
+    @Suppress
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR1)
     @Test
     public void swipeStartInRTL() throws Throwable {