Merge "MediaRouter: Fix errprone warnings" into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index d2fa87d..e173069 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -10147,10 +10147,6 @@
     method public abstract void onTabUnselected(android.support.v7.app.ActionBar.Tab, android.support.v4.app.FragmentTransaction);
   }
 
-  public deprecated class ActionBarActivity extends android.support.v7.app.AppCompatActivity {
-    ctor public ActionBarActivity();
-  }
-
   public class ActionBarDrawerToggle implements android.support.v4.widget.DrawerLayout.DrawerListener {
     ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, int, int);
     ctor public ActionBarDrawerToggle(android.app.Activity, android.support.v4.widget.DrawerLayout, android.support.v7.widget.Toolbar, int, int);
diff --git a/compat/java/android/support/v4/content/res/ResourcesCompat.java b/compat/java/android/support/v4/content/res/ResourcesCompat.java
index f9468ec..134149d 100644
--- a/compat/java/android/support/v4/content/res/ResourcesCompat.java
+++ b/compat/java/android/support/v4/content/res/ResourcesCompat.java
@@ -198,25 +198,21 @@
             // Use framework support.
             return context.getResources().getFont(id);
         }
-        return loadFont(context, id, Typeface.NORMAL);
+        return loadFont(context, id, new TypedValue(), Typeface.NORMAL);
     }
 
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
-    public static Typeface getFont(@NonNull Context context, @FontRes int id, int style)
-            throws NotFoundException {
+    public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,
+            int style) throws NotFoundException {
         if (context.isRestricted()) {
             return null;
         }
-        if (BuildCompat.isAtLeastO()) {
-            // Use framework support.
-            return context.getResources().getFont(id);
-        }
-        return loadFont(context, id, style);
+        return loadFont(context, id, value, style);
     }
 
-    private static Typeface loadFont(@NonNull Context context, int id, int style) {
-        final TypedValue value = new TypedValue();
+    private static Typeface loadFont(@NonNull Context context, int id, TypedValue value,
+            int style) {
         final Resources resources = context.getResources();
         resources.getValue(id, value, true);
         Typeface typeface = loadFont(context, resources, value, id, style);
@@ -234,12 +230,17 @@
                     + Integer.toHexString(id) + ") is not a Font: " + value);
         }
 
+        final String file = value.string.toString();
+        if (!file.startsWith("res/")) {
+            // Early exit if the specified string is unlikely to the resource path.
+            return null;
+        }
+
         Typeface cached = TypefaceCompat.findFromCache(wrapper, id, style);
         if (cached != null) {
             return cached;
         }
 
-        final String file = value.string.toString();
         try {
             if (file.toLowerCase().endsWith(".xml")) {
                 final XmlResourceParser rp = wrapper.getXml(id);
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompat.java b/compat/java/android/support/v4/graphics/TypefaceCompat.java
index 99ce7eb..444616a 100644
--- a/compat/java/android/support/v4/graphics/TypefaceCompat.java
+++ b/compat/java/android/support/v4/graphics/TypefaceCompat.java
@@ -18,12 +18,10 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.net.Uri;
-import android.support.annotation.GuardedBy;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
@@ -32,6 +30,7 @@
 import android.support.v4.content.res.FontResourcesParserCompat.ProviderResourceEntry;
 import android.support.v4.provider.FontsContractCompat;
 import android.support.v4.provider.FontsContractCompat.FontInfo;
+import android.support.v4.util.LruCache;
 
 import java.nio.ByteBuffer;
 import java.util.Map;
@@ -42,38 +41,26 @@
  */
 @RestrictTo(LIBRARY_GROUP)
 public class TypefaceCompat {
-    @GuardedBy("sLock")
-    private static TypefaceCompatImpl sTypefaceCompatImpl;
-    private static final Object sLock = new Object();
+    // TODO(nona): Introduce API 24 implementation.
+    private static final TypefaceCompatImpl sTypefaceCompatImpl = new TypefaceCompatBaseImpl();
+
+    /**
+     * Cache for Typeface objects dynamically loaded from assets.
+     */
+    private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
 
     interface TypefaceCompatImpl {
         // Create Typeface from font file in res/font directory.
-        Typeface createFromResourcesFontFile(Resources resources, int id, int style);
+        Typeface createFromResourcesFontFile(Context context, Resources resources, int id,
+                int style);
 
         // Create Typeface from XML which root node is "font-family"
         Typeface createFromFontFamilyFilesResourceEntry(
-                FontFamilyFilesResourceEntry entry, Resources resources, int id, int style);
+                Context context, FontFamilyFilesResourceEntry entry, Resources resources, int id,
+                int style);
 
-        // For finiding cache before parsing xml data.
-        Typeface findFromCache(Resources resources, int id, int style);
-
-        Typeface createTypeface(@NonNull FontInfo[] fonts, Map<Uri, ByteBuffer> uriBuffer);
-    }
-
-    /**
-     * If the current implementation is not set, set it according to the current build version. This
-     * is safe to call several times, even if the implementation has already been set.
-     */
-    @TargetApi(26)
-    private static void maybeInitImpl(Context context) {
-        if (sTypefaceCompatImpl == null) {
-            synchronized (sLock) {
-                if (sTypefaceCompatImpl == null) {
-                    // TODO: Maybe we can do better thing on Android N or later.
-                    sTypefaceCompatImpl = new TypefaceCompatBaseImpl(context);
-                }
-            }
-        }
+        Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
+                Map<Uri, ByteBuffer> uriBuffer);
     }
 
     private TypefaceCompat() {}
@@ -84,13 +71,19 @@
      * @return null if not found.
      */
     public static Typeface findFromCache(Resources resources, int id, int style) {
-        synchronized (sLock) {
-            // There is no cache if there is no impl.
-            if (sTypefaceCompatImpl == null) {
-                return null;
-            }
-        }
-        return sTypefaceCompatImpl.findFromCache(resources, id, style);
+        return sTypefaceCache.get(createResourceUid(resources, id, style));
+    }
+
+    /**
+     * Create a unique id for a given Resource and id.
+     *
+     * @param resources Resources instance
+     * @param id a resource id
+     * @param a style to be used for this resource, -1 if not availbale.
+     * @return Unique id for a given resource and id.
+     */
+    private static String createResourceUid(final Resources resources, int id, int style) {
+        return resources.getResourcePackageName(id) + "-" + id + "-" + style;
     }
 
     /**
@@ -100,14 +93,18 @@
      */
     public static Typeface createFromResourcesFamilyXml(
             Context context, FamilyResourceEntry entry, Resources resources, int id, int style) {
-        maybeInitImpl(context);
+        Typeface typeface;
         if (entry instanceof ProviderResourceEntry) {
-            return FontsContractCompat.getFontSync(context,
+            typeface = FontsContractCompat.getFontSync(context,
                     ((ProviderResourceEntry) entry).getRequest());
         } else {
-            return sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
-                    (FontFamilyFilesResourceEntry) entry, resources, id, style);
+            typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
+                    context, (FontFamilyFilesResourceEntry) entry, resources, id, style);
         }
+        if (typeface != null) {
+            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+        }
+        return typeface;
     }
 
     /**
@@ -116,8 +113,12 @@
     @Nullable
     public static Typeface createFromResourcesFontFile(
             Context context, Resources resources, int id, int style) {
-        maybeInitImpl(context);
-        return sTypefaceCompatImpl.createFromResourcesFontFile(resources, id, style);
+        Typeface typeface = sTypefaceCompatImpl.createFromResourcesFontFile(
+                context, resources, id, style);
+        if (typeface != null) {
+            sTypefaceCache.put(createResourceUid(resources, id, style), typeface);
+        }
+        return typeface;
     }
 
     /**
@@ -125,7 +126,6 @@
      */
     public static Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
             Map<Uri, ByteBuffer> uriBuffer) {
-        maybeInitImpl(context);
-        return sTypefaceCompatImpl.createTypeface(fonts, uriBuffer);
+        return sTypefaceCompatImpl.createTypeface(context, fonts, uriBuffer);
     }
 }
diff --git a/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
index ffc57a1..86bcae8 100644
--- a/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
+++ b/compat/java/android/support/v4/graphics/TypefaceCompatBaseImpl.java
@@ -29,7 +29,6 @@
 import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
 import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
 import android.support.v4.provider.FontsContractCompat.FontInfo;
-import android.support.v4.util.LruCache;
 import android.util.Log;
 
 import java.io.Closeable;
@@ -50,21 +49,9 @@
     private static final String TAG = "TypefaceCompatBaseImpl";
     private static final String CACHE_FILE_PREFIX = "cached_font_";
 
-    /**
-     * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
-     */
-    private static final LruCache<String, Typeface> sDynamicTypefaceCache =
-            new LruCache<>(16);
-
-    private final Context mApplicationContext;
-
-    TypefaceCompatBaseImpl(Context context) {
-        mApplicationContext = context.getApplicationContext();
-    }
-
     @Override
-    public Typeface createTypeface(
-            @NonNull FontInfo[] fonts, Map<Uri, ByteBuffer> uriBuffer) {
+    public Typeface createTypeface(Context context, @NonNull FontInfo[] fonts,
+            Map<Uri, ByteBuffer> uriBuffer) {
         // When we load from file, we can only load one font so just take the first one.
         if (fonts.length < 1) {
             return null;
@@ -72,7 +59,7 @@
         Typeface typeface = null;
         FontInfo font = fonts[0];
         ByteBuffer buffer = uriBuffer.get(font.getUri());
-        File tmpFile = copyToCacheFile(buffer);
+        File tmpFile = copyToCacheFile(context, buffer);
         if (tmpFile != null) {
             try {
                 typeface = Typeface.createFromFile(tmpFile.getPath());
@@ -88,11 +75,11 @@
         return typeface;
     }
 
-    private File copyToCacheFile(final InputStream is) {
+    private static File copyToCacheFile(Context context, final InputStream is) {
         FileOutputStream fos = null;
         File cacheFile;
         try {
-            cacheFile = new File(mApplicationContext.getCacheDir(),
+            cacheFile = new File(context.getCacheDir(),
                     CACHE_FILE_PREFIX + Thread.currentThread().getId());
             fos = new FileOutputStream(cacheFile, false);
 
@@ -111,11 +98,11 @@
         return cacheFile;
     }
 
-    private File copyToCacheFile(final ByteBuffer is) {
+    private static File copyToCacheFile(Context context, final ByteBuffer is) {
         FileOutputStream fos = null;
         File cacheFile;
         try {
-            cacheFile = new File(mApplicationContext.getCacheDir(),
+            cacheFile = new File(context.getCacheDir(),
                     CACHE_FILE_PREFIX + Thread.currentThread().getId());
             fos = new FileOutputStream(cacheFile, false);
 
@@ -134,7 +121,7 @@
         return cacheFile;
     }
 
-    static void closeQuietly(InputStream is) {
+    private static void closeQuietly(InputStream is) {
         if (is != null) {
             try {
                 is.close();
@@ -146,17 +133,12 @@
 
     @Nullable
     @Override
-    public Typeface createFromResourcesFontFile(Resources resources, int id, int style) {
+    public Typeface createFromResourcesFontFile(Context context, Resources resources, int id,
+            int style) {
         InputStream is = null;
         try {
             is = resources.openRawResource(id);
-            Typeface typeface = createTypeface(resources, is);
-            if (typeface == null) {
-                return null;
-            }
-            final String key = createAssetUid(resources, id, style);
-            sDynamicTypefaceCache.put(key, typeface);
-            return typeface;
+            return createTypeface(context, resources, is);
         } catch (IOException e) {
             return null;
         } finally {
@@ -164,18 +146,6 @@
         }
     }
 
-    @Nullable
-    @Override
-    public Typeface createFromFontFamilyFilesResourceEntry(
-            FontFamilyFilesResourceEntry filesEntry, Resources resources, int id, int style) {
-        Typeface typeface = createFromResources(filesEntry, resources, id, style);
-        if (typeface != null) {
-            final String key = createAssetUid(resources, id, style);
-            sDynamicTypefaceCache.put(key, typeface);
-        }
-        return typeface;
-    }
-
     private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry,
             int targetWeight, boolean isTargetItalic) {
         FontFileResourceEntry bestEntry = null;
@@ -193,13 +163,10 @@
         return bestEntry;
     }
 
-    /**
-     * Implementation of resources font retrieval for a file type xml resource. This should be
-     * overriden by other implementations.
-     */
     @Nullable
-    Typeface createFromResources(FontFamilyFilesResourceEntry entry, Resources resources,
-            int id, int style) {
+    @Override
+    public Typeface createFromFontFamilyFilesResourceEntry(Context context,
+            FontFamilyFilesResourceEntry entry, Resources resources, int id, int style) {
         FontFileResourceEntry best = findBestEntry(
                 entry, ((style & Typeface.BOLD) == 0) ? 400 : 700, (style & Typeface.ITALIC) != 0);
         if (best == null) {
@@ -209,7 +176,7 @@
         InputStream is = null;
         try {
             is = resources.openRawResource(best.getResourceId());
-            return createTypeface(resources, is);
+            return createTypeface(context, resources, is);
         } catch (IOException e) {
             // This is fine. The resource can be string type which indicates a name of Typeface.
         } finally {
@@ -218,29 +185,10 @@
         return null;
     }
 
-    @Override
-    public Typeface findFromCache(Resources resources, int id, int style) {
-        final String key = createAssetUid(resources, id, style);
-        synchronized (sDynamicTypefaceCache) {
-            return sDynamicTypefaceCache.get(key);
-        }
-    }
-
-    /**
-     * Creates a unique id for a given AssetManager and asset id
-     *
-     * @param resources Resources instance
-     * @param id a resource id
-     * @param style a style to be used for this resource, -1 if not avaialble.
-     * @return Unique id for a given AssetManager and id
-     */
-    private static String createAssetUid(final Resources resources, int id, int style) {
-        return resources.getResourcePackageName(id) + "-" + id + "-" + style;
-    }
-
     // Caller must close "is"
-    Typeface createTypeface(Resources resources, InputStream is) throws IOException {
-        File tmpFile = copyToCacheFile(is);
+    Typeface createTypeface(Context context, Resources resources, InputStream is)
+            throws IOException {
+        File tmpFile = copyToCacheFile(context, is);
         if (tmpFile != null) {
             try {
                 return Typeface.createFromFile(tmpFile.getPath());
diff --git a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
index f715079..56f5ab4 100644
--- a/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
+++ b/compat/tests/java/android/support/v4/content/res/ResourcesCompatTest.java
@@ -289,12 +289,12 @@
 
     @Test(expected = Resources.NotFoundException.class)
     public void testGetFont_invalidResourceId() {
-        ResourcesCompat.getFont(mContext, -1, Typeface.NORMAL);
+        ResourcesCompat.getFont(mContext, -1);
     }
 
     @Test
     public void testGetFont_fontFile() {
-        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont, Typeface.NORMAL);
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont);
 
         assertNotNull(font);
         assertNotSame(Typeface.DEFAULT, font);
@@ -302,7 +302,7 @@
 
     @Test
     public void testGetFont_xmlFile() {
-        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont, Typeface.NORMAL);
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
 
         assertNotNull(font);
         assertNotSame(Typeface.DEFAULT, font);
@@ -312,13 +312,13 @@
     public void testGetFont_invalidXmlFile() {
         try {
             assertNull(
-                    ResourcesCompat.getFont(mContext, R.font.invalid_xmlfamily, Typeface.NORMAL));
+                    ResourcesCompat.getFont(mContext, R.font.invalid_xmlfamily));
         } catch (Resources.NotFoundException e) {
             // pass
         }
 
         try {
-            assertNull(ResourcesCompat.getFont(mContext, R.font.invalid_xmlempty, Typeface.NORMAL));
+            assertNull(ResourcesCompat.getFont(mContext, R.font.invalid_xmlempty));
         } catch (Resources.NotFoundException e) {
             // pass
         }
@@ -326,16 +326,16 @@
 
     @Test
     public void testGetFont_fontFileIsCached() {
-        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont, Typeface.NORMAL);
-        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplefont, Typeface.NORMAL);
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplefont);
+        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplefont);
 
         assertSame(font, font2);
     }
 
     @Test
     public void testGetFont_xmlFileIsCached() {
-        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont, Typeface.NORMAL);
-        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplexmlfont, Typeface.NORMAL);
+        Typeface font = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
+        Typeface font2 = ResourcesCompat.getFont(mContext, R.font.samplexmlfont);
 
         assertSame(font, font2);
     }
diff --git a/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java b/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java
index 16df3b5..2a07272 100644
--- a/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java
+++ b/compat/tests/java/android/support/v4/util/SimpleArrayMapTest.java
@@ -16,7 +16,7 @@
 
 import static org.junit.Assert.fail;
 
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
@@ -29,7 +29,7 @@
  * Unit tests for SimpleArrayMap
  */
 @RunWith(AndroidJUnit4.class)
-@SmallTest
+@LargeTest
 public class SimpleArrayMapTest {
     private static final String TAG = "SimpleArrayMapTest";
     SimpleArrayMap<String, String> map = new SimpleArrayMap<>();
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index bb88554..9f92d30 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -2821,23 +2821,25 @@
                         f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                         if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                     }
-                    boolean addedChild = false;
+                    FragmentManagerNonConfig child;
                     if (f.mChildFragmentManager != null) {
                         f.mChildFragmentManager.saveNonConfig();
-                        FragmentManagerNonConfig child = f.mChildFragmentManager.mSavedNonConfig;
-                        if (child != null) {
-                            if (childFragments == null) {
-                                childFragments = new ArrayList<FragmentManagerNonConfig>();
-                                for (int j = 0; j < i; j++) {
-                                    childFragments.add(null);
-                                }
-                            }
-                            childFragments.add(child);
-                            addedChild = true;
+                        child = f.mChildFragmentManager.mSavedNonConfig;
+                    } else {
+                        // f.mChildNonConfig may be not null, when the parent fragment is
+                        // in the backstack.
+                        child = f.mChildNonConfig;
+                    }
+
+                    if (childFragments == null && child != null) {
+                        childFragments = new ArrayList<>(mActive.size());
+                        for (int j = 0; j < i; j++) {
+                            childFragments.add(null);
                         }
                     }
-                    if (childFragments != null && !addedChild) {
-                        childFragments.add(null);
+
+                    if (childFragments != null) {
+                        childFragments.add(child);
                     }
                 }
             }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
index 6f6c213..a312b9b 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentLifecycleTest.java
@@ -964,6 +964,52 @@
     }
 
     /**
+     * Check that retained fragments in the backstack correctly restored after two "configChanges"
+     */
+    @Test
+    @UiThreadTest
+    public void retainedFragmentInBackstack() throws Throwable {
+        FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, null);
+        FragmentManager fm = fc.getSupportFragmentManager();
+
+        Fragment fragment1 = new StrictFragment();
+        fm.beginTransaction()
+                .add(fragment1, "1")
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+
+        Fragment child = new StrictFragment();
+        child.setRetainInstance(true);
+        fragment1.getChildFragmentManager().beginTransaction()
+                .add(child, "child").commit();
+        fragment1.getChildFragmentManager().executePendingTransactions();
+
+        Fragment fragment2 = new StrictFragment();
+        fm.beginTransaction()
+                .remove(fragment1)
+                .add(fragment2, "2")
+                .addToBackStack(null)
+                .commit();
+        fm.executePendingTransactions();
+
+        Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                FragmentTestUtil.destroy(mActivityRule, fc);
+
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        savedState = FragmentTestUtil.destroy(mActivityRule, fc);
+        fc = FragmentTestUtil.createController(mActivityRule);
+        FragmentTestUtil.resume(mActivityRule, fc, savedState);
+        fm = fc.getSupportFragmentManager();
+        fm.popBackStackImmediate();
+        Fragment retainedChild = fm.findFragmentByTag("1")
+                .getChildFragmentManager().findFragmentByTag("child");
+        assertEquals(child, retainedChild);
+    }
+
+    /**
      * When a fragment has been optimized out, it state should still be saved during
      * save and restore instance state.
      */
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
index d6531ac..98d6452 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
@@ -1585,17 +1585,11 @@
 
         public MediaBrowserImplApi21(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
-            // Do not send the client version for API 26 and higher, since we don't need to use
-            // EXTRA_MESSENGER_BINDER for API 26 and higher.
-            if (Build.VERSION.SDK_INT <= 25) {
-                if (rootHints == null) {
-                    rootHints = new Bundle();
-                }
-                rootHints.putInt(EXTRA_CLIENT_VERSION, CLIENT_VERSION_CURRENT);
-                mRootHints = new Bundle(rootHints);
-            } else {
-                mRootHints = rootHints == null ? null : new Bundle(rootHints);
+            if (rootHints == null) {
+                rootHints = new Bundle();
             }
+            rootHints.putInt(EXTRA_CLIENT_VERSION, CLIENT_VERSION_CURRENT);
+            mRootHints = new Bundle(rootHints);
             callback.setInternalConnectionCallback(this);
             mBrowserObj = MediaBrowserCompatApi21.createBrowser(context, serviceComponent,
                     callback.mConnectionCallbackObj, mRootHints);
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
index c2bff47..542e045 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -309,9 +309,11 @@
                 rootExtras = new Bundle();
                 rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
                 BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
-                IMediaSession extraBinder = mSession.getExtraBinder();
-                BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
-                        extraBinder == null ? null : extraBinder.asBinder());
+                if (mSession != null) {
+                    IMediaSession extraBinder = mSession.getExtraBinder();
+                    BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
+                            extraBinder == null ? null : extraBinder.asBinder());
+                }
             }
             BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
                     clientPackageName, clientUid, rootHints);
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index b17c3f4..bcbc5ba 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -56,6 +56,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.KeyEvent;
+import android.view.ViewConfiguration;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -863,7 +864,9 @@
      */
     public abstract static class Callback {
         final Object mCallbackObj;
-        WeakReference<MediaSessionImpl> mSessionImpl;
+        private WeakReference<MediaSessionImpl> mSessionImpl;
+        private CallbackHandler mCallbackHandler = null;
+        private boolean mMediaPlayPauseKeyHandled;
 
         public Callback() {
             if (android.os.Build.VERSION.SDK_INT >= 24) {
@@ -877,6 +880,14 @@
             }
         }
 
+        private void setSessionImpl(MediaSessionImpl impl, Handler handler) {
+            mSessionImpl = new WeakReference<MediaSessionImpl>(impl);
+            if (mCallbackHandler != null) {
+                mCallbackHandler.removeCallbacksAndMessages(null);
+            }
+            mCallbackHandler = new CallbackHandler(handler.getLooper());
+        }
+
         /**
          * Called when a controller has sent a custom command to this session.
          * The owner of the session may handle custom commands but is not
@@ -891,14 +902,77 @@
 
         /**
          * Override to handle media button events.
+         * <p>
+         * The double tap of {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} or {@link
+         * KeyEvent#KEYCODE_HEADSETHOOK} will call the {@link #onSkipToNext} by default.
          *
          * @param mediaButtonEvent The media button event intent.
          * @return True if the event was handled, false otherwise.
          */
         public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
+            MediaSessionImpl impl = mSessionImpl.get();
+            if (impl == null || mCallbackHandler == null) {
+                return false;
+            }
+            KeyEvent keyEvent = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+            if (keyEvent == null || keyEvent.getAction() != KeyEvent.ACTION_DOWN) {
+                return false;
+            }
+            int keyCode = keyEvent.getKeyCode();
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                    if (keyEvent.getRepeatCount() > 0) {
+                        mCallbackHandler.removeMessages(
+                                CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+                        if (keyEvent.getRepeatCount() == 1 && !mMediaPlayPauseKeyHandled) {
+                            handleMediaPlayPauseKeySingleTap();
+                        }
+                    } else if (mCallbackHandler.hasMessages(
+                            CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT)) {
+                        mCallbackHandler.removeMessages(
+                                CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+                        PlaybackStateCompat state = impl.getPlaybackState();
+                        long validActions = state == null ? 0 : state.getActions();
+                        // Consider double tap as the next.
+                        if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+                            onSkipToNext();
+                        }
+                    } else {
+                        mMediaPlayPauseKeyHandled = false;
+                        mCallbackHandler.sendEmptyMessageDelayed(
+                                CallbackHandler.MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
+                                ViewConfiguration.getDoubleTapTimeout());
+                    }
+                    return true;
+            }
             return false;
         }
 
+        private void handleMediaPlayPauseKeySingleTap() {
+            if (mMediaPlayPauseKeyHandled) {
+                return;
+            }
+            MediaSessionImpl impl = mSessionImpl.get();
+            if (impl == null) {
+                return;
+            }
+            mMediaPlayPauseKeyHandled = true;
+            PlaybackStateCompat state = impl.getPlaybackState();
+            long validActions = state == null ? 0 : state.getActions();
+            boolean isPlaying = state != null
+                    && state.getState() == PlaybackStateCompat.STATE_PLAYING;
+            boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+                        | PlaybackStateCompat.ACTION_PLAY)) != 0;
+            boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
+                        | PlaybackStateCompat.ACTION_PAUSE)) != 0;
+            if (isPlaying && canPause) {
+                onPause();
+            } else if (!isPlaying && canPlay) {
+                onPlay();
+            }
+        }
+
         /**
          * Override to handle requests to prepare playback. During the preparation, a session
          * should not hold audio focus in order to allow other session play seamlessly.
@@ -1144,6 +1218,21 @@
         public void onRemoveQueueItemAt(int index) {
         }
 
+        private class CallbackHandler extends Handler {
+            private static final int MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 1;
+
+            CallbackHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_MEDIA_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT) {
+                    handleMediaPlayPauseKeySingleTap();
+                }
+            }
+        }
+
         @RequiresApi(21)
         private class StubApi21 implements MediaSessionCompatApi21.Callback {
 
@@ -1684,6 +1773,7 @@
         void release();
         Token getSessionToken();
         void setPlaybackState(PlaybackStateCompat state);
+        PlaybackStateCompat getPlaybackState();
         void setMetadata(MediaMetadataCompat metadata);
 
         void setSessionActivity(PendingIntent pi);
@@ -1792,7 +1882,11 @@
                     handler = new Handler();
                 }
                 synchronized (mLock) {
+                    if (mHandler != null) {
+                        mHandler.removeCallbacksAndMessages(null);
+                    }
                     mHandler = new MessageHandler(handler.getLooper());
+                    mCallback.setSessionImpl(this, handler);
                 }
             }
         }
@@ -1922,6 +2016,13 @@
             }
         }
 
+        @Override
+        public PlaybackStateCompat getPlaybackState() {
+            synchronized (mLock) {
+                return mState;
+            }
+        }
+
         void setRccState(PlaybackStateCompat state) {
             mRcc.setPlaybackState(getRccStateFromState(state.getState()));
         }
@@ -2910,17 +3011,8 @@
                         break;
                     case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                     case KeyEvent.KEYCODE_HEADSETHOOK:
-                        boolean isPlaying = mState != null
-                                && mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                        boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
-                                | PlaybackStateCompat.ACTION_PLAY)) != 0;
-                        boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE
-                                | PlaybackStateCompat.ACTION_PAUSE)) != 0;
-                        if (isPlaying && canPause) {
-                            cb.onPause();
-                        } else if (!isPlaying && canPlay) {
-                            cb.onPlay();
-                        }
+                        Log.w(TAG, "KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK are handled"
+                                + " already");
                         break;
                 }
             }
@@ -3111,7 +3203,7 @@
             MediaSessionCompatApi21.setCallback(mSessionObj,
                     callback == null ? null : callback.mCallbackObj, handler);
             if (callback != null) {
-                callback.mSessionImpl = new WeakReference<MediaSessionImpl>(this);
+                callback.setSessionImpl(this, handler);
             }
         }
 
@@ -3185,6 +3277,11 @@
         }
 
         @Override
+        public PlaybackStateCompat getPlaybackState() {
+            return mPlaybackState;
+        }
+
+        @Override
         public void setMetadata(MediaMetadataCompat metadata) {
             mMetadata = metadata;
             MediaSessionCompatApi21.setMetadata(mSessionObj,
diff --git a/media-compat/tests/AndroidManifest.xml b/media-compat/tests/AndroidManifest.xml
index f8ebb8b..60b7890 100644
--- a/media-compat/tests/AndroidManifest.xml
+++ b/media-compat/tests/AndroidManifest.xml
@@ -41,6 +41,11 @@
                 <action android:name="android.media.browse.MediaBrowserService"/>
             </intent-filter>
         </service>
+        <service android:name="android.support.v4.media.StubMediaBrowserServiceCompatWithDelayedMediaSession">
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowserService"/>
+            </intent-filter>
+        </service>
     </application>
 
 </manifest>
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
index 5b85e10..20d6119 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
@@ -27,6 +27,7 @@
 
 import android.content.ComponentName;
 import android.os.Bundle;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.testutils.PollingCheck;
@@ -369,7 +370,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testUnsubscribeForMultipleSubscriptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
@@ -417,7 +418,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
index abd4214..0640f4d 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserServiceCompatTest.java
@@ -48,6 +48,11 @@
     private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
             "android.support.mediacompat.test",
             "android.support.v4.media.StubMediaBrowserServiceCompat");
+    private static final ComponentName TEST_BROWSER_SERVICE_DELAYED_MEDIA_SESSION =
+            new ComponentName(
+                    "android.support.mediacompat.test",
+                    "android.support.v4.media"
+                            + ".StubMediaBrowserServiceCompatWithDelayedMediaSession");
     private static final String TEST_KEY_1 = "key_1";
     private static final String TEST_VALUE_1 = "value_1";
     private static final String TEST_KEY_2 = "key_2";
@@ -64,6 +69,7 @@
     private final SearchCallback mSearchCallback = new SearchCallback();
 
     private MediaBrowserCompat mMediaBrowser;
+    private MediaBrowserCompat mMediaBrowserForDelayedMediaSession;
     private StubMediaBrowserServiceCompat mMediaBrowserService;
     private Bundle mRootHints;
 
@@ -371,6 +377,33 @@
         assertEquals(val, browserRoot.getExtras().getString(key));
     }
 
+
+    @Test
+    @SmallTest
+    public void testDelayedSetSessionToken() throws Exception {
+        final ConnectionCallbackForDelayedMediaSession callback =
+                new ConnectionCallbackForDelayedMediaSession();
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowserForDelayedMediaSession =
+                        new MediaBrowserCompat(getInstrumentation().getTargetContext(),
+                                TEST_BROWSER_SERVICE_DELAYED_MEDIA_SESSION, callback, null);
+            }
+        });
+
+        synchronized (mWaitLock) {
+            mMediaBrowserForDelayedMediaSession.connect();
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertEquals(0, callback.mConnectedCount);
+
+            StubMediaBrowserServiceCompatWithDelayedMediaSession.sInstance.callSetSessionToken();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(1, callback.mConnectedCount);
+        }
+    }
+
     private void assertRootHints(MediaItem item) {
         Bundle rootHints = item.getDescription().getExtras();
         assertNotNull(rootHints);
@@ -527,4 +560,18 @@
             mData = null;
         }
     }
+
+    private class ConnectionCallbackForDelayedMediaSession extends
+            MediaBrowserCompat.ConnectionCallback {
+        private int mConnectedCount = 0;
+
+        @Override
+        public void onConnected() {
+            synchronized (mWaitLock) {
+                mConnectedCount++;
+                mWaitLock.notify();
+            }
+        }
+    };
+
 }
diff --git a/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompatWithDelayedMediaSession.java b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompatWithDelayedMediaSession.java
new file mode 100644
index 0000000..e93c940
--- /dev/null
+++ b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompatWithDelayedMediaSession.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.media.session.MediaSessionCompat;
+
+import java.util.List;
+
+/**
+ * Stub implementation of {@link MediaBrowserServiceCompat}.
+ * This implementation does not call
+ * {@link MediaBrowserServiceCompat#setSessionToken(MediaSessionCompat.Token)} in its
+ * {@link android.app.Service#onCreate}.
+ */
+public class StubMediaBrowserServiceCompatWithDelayedMediaSession extends
+        MediaBrowserServiceCompat {
+
+    static StubMediaBrowserServiceCompatWithDelayedMediaSession sInstance;
+    private MediaSessionCompat mSession;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        mSession = new MediaSessionCompat(
+                this, "StubMediaBrowserServiceCompatWithDelayedMediaSession");
+    }
+
+    @Nullable
+    @Override
+    public BrowserRoot onGetRoot(@NonNull String clientPackageName,
+            int clientUid, @Nullable Bundle rootHints) {
+        return new BrowserRoot("StubRootId", null);
+    }
+
+    @Override
+    public void onLoadChildren(@NonNull String parentId,
+            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
+        result.detach();
+    }
+
+    void callSetSessionToken() {
+        setSessionToken(mSession.getSessionToken());
+    }
+}
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
index a91a888..44d0089 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
@@ -605,8 +605,6 @@
             sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(sessionCallback.mOnFastForwardCalled);
-
-            sessionCallback.reset();
             sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_REWIND);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(sessionCallback.mOnRewindCalled);
@@ -627,6 +625,13 @@
             sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
             mWaitLock.wait(TIME_OUT_MS);
             assertTrue(sessionCallback.mOnPauseCalled);
+
+            // Double tap of PLAY_PAUSE is the next track.
+            sessionCallback.reset();
+            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+            sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(sessionCallback.mOnSkipToNextCalled);
         }
     }
 
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java
index 2daba66..519f1de 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/ProgressBarManagerTest.java
@@ -19,7 +19,7 @@
 
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.LargeTest;
 import android.support.v17.leanback.testutils.PollingCheck;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,7 +28,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
-@SmallTest
+@LargeTest
 public class ProgressBarManagerTest {
 
     Context mContext;
diff --git a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java b/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
deleted file mode 100644
index 1834681..0000000
--- a/v7/appcompat/src/android/support/v7/app/ActionBarActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.app;
-
-/**
- * @deprecated Use {@link android.support.v7.app.AppCompatActivity} instead.
- */
-@Deprecated
-public class ActionBarActivity extends AppCompatActivity {
-}
diff --git a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
index a67313f..b962013 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
@@ -30,6 +30,7 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.StyleableRes;
 import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.os.BuildCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -103,13 +104,17 @@
      */
     @Nullable
     public Typeface getFont(@StyleableRes int index, int style) {
-        if (mWrapped.hasValue(index)) {
-            final int resourceId = mWrapped.getResourceId(index, 0);
-            if (resourceId != 0) {
-                return ResourcesCompat.getFont(mContext, resourceId, style);
-            }
+        if (BuildCompat.isAtLeastO()) {
+            return mWrapped.getFont(index);
         }
-        return null;
+        final int resourceId = mWrapped.getResourceId(index, 0);
+        if (resourceId == 0) {
+            return null;
+        }
+        if (mTypedValue == null) {
+            mTypedValue = new TypedValue();
+        }
+        return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style);
     }
 
     public int length() {
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
index b7a28af..a9da3f5 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatSpinnerTest.java
@@ -28,6 +28,7 @@
 import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
 import android.support.annotation.IdRes;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.res.ResourcesCompat;
@@ -88,11 +89,13 @@
         onView(withText(itemText)).perform(click());
     }
 
+    @LargeTest
     @Test
     public void testPopupThemingFromXmlAttribute() {
         verifySpinnerPopupTheming(R.id.view_magenta_themed_popup, R.color.test_magenta, true);
     }
 
+    @LargeTest
     @Test
     public void testUnthemedPopupRuntimeTheming() {
         final AppCompatSpinner spinner =
@@ -106,6 +109,7 @@
         verifySpinnerPopupTheming(R.id.view_unthemed_popup, R.color.test_green, false);
     }
 
+    @LargeTest
     @Test
     public void testThemedPopupRuntimeTheming() {
         final AppCompatSpinner spinner =
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
index 14e8e88..a891d36 100644
--- a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.preference.AndroidResources;
@@ -73,6 +74,7 @@
         verify(mImageFrame).setVisibility(View.INVISIBLE);
     }
 
+    @LargeTest
     @Test
     @UiThreadTest
     public void bindViewHolder_iconSpaceNotReserved_shouldNotReserveIconSpace() {
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 1f7beac..5529ead 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -201,7 +201,12 @@
         recyclerView.setItemViewCacheSize(0); // no cache, directly goes to pool
         recyclerView.setLayoutManager(layoutManager);
         setRecyclerView(recyclerView);
-        recyclerView.setAdapter(adapter);
+         mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.setAdapter(adapter);
+            }
+        });
         layoutManager.waitForLayout(1);
 
         assertEquals(layoutCount, recyclerView.getChildCount());
@@ -276,7 +281,12 @@
         recyclerView.setItemViewCacheSize(0); // no cache, directly goes to pool
         recyclerView.setLayoutManager(layoutManager);
         setRecyclerView(recyclerView);
-        recyclerView.setAdapter(adapter);
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                recyclerView.setAdapter(adapter);
+            }
+        });
         layoutManager.waitForLayout(1);
 
         assertEquals(firstPassLayoutCount, recyclerView.getChildCount());