Merge "Add drawable caching"
diff --git a/api/current.txt b/api/current.txt
index 35041d1..3919fb8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45254,7 +45254,7 @@
     method public static boolean allowFileSchemeCookies();
     method public abstract void flush();
     method public abstract java.lang.String getCookie(java.lang.String);
-    method public static synchronized android.webkit.CookieManager getInstance();
+    method public static android.webkit.CookieManager getInstance();
     method public abstract boolean hasCookies();
     method public abstract deprecated void removeAllCookie();
     method public abstract void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
@@ -45269,8 +45269,8 @@
   }
 
   public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
-    method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
-    method public static synchronized android.webkit.CookieSyncManager getInstance();
+    method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+    method public static android.webkit.CookieSyncManager getInstance();
     method protected deprecated void syncFromRamToFlash();
     field protected static final java.lang.String LOGTAG = "websync";
     field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/api/system-current.txt b/api/system-current.txt
index 5c6c122..44ee2c9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -48416,7 +48416,7 @@
     method public abstract java.lang.String getCookie(java.lang.String);
     method public abstract java.lang.String getCookie(java.lang.String, boolean);
     method public synchronized java.lang.String getCookie(android.net.WebAddress);
-    method public static synchronized android.webkit.CookieManager getInstance();
+    method public static android.webkit.CookieManager getInstance();
     method public abstract boolean hasCookies();
     method public abstract boolean hasCookies(boolean);
     method public abstract deprecated void removeAllCookie();
@@ -48433,8 +48433,8 @@
   }
 
   public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
-    method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
-    method public static synchronized android.webkit.CookieSyncManager getInstance();
+    method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+    method public static android.webkit.CookieSyncManager getInstance();
     method protected deprecated void syncFromRamToFlash();
     field protected static final java.lang.String LOGTAG = "websync";
     field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/api/test-current.txt b/api/test-current.txt
index 4d55c29..9e3387f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -45334,7 +45334,7 @@
     method public static boolean allowFileSchemeCookies();
     method public abstract void flush();
     method public abstract java.lang.String getCookie(java.lang.String);
-    method public static synchronized android.webkit.CookieManager getInstance();
+    method public static android.webkit.CookieManager getInstance();
     method public abstract boolean hasCookies();
     method public abstract deprecated void removeAllCookie();
     method public abstract void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
@@ -45349,8 +45349,8 @@
   }
 
   public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
-    method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
-    method public static synchronized android.webkit.CookieSyncManager getInstance();
+    method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+    method public static android.webkit.CookieSyncManager getInstance();
     method protected deprecated void syncFromRamToFlash();
     field protected static final java.lang.String LOGTAG = "websync";
     field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 3ec235b..67289c2 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -35,7 +35,7 @@
      *
      * @return the singleton CookieManager instance
      */
-    public static synchronized CookieManager getInstance() {
+    public static CookieManager getInstance() {
         return WebViewFactory.getProvider().getCookieManager();
     }
 
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index eda8d36..c974b32 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -65,6 +65,7 @@
 
     private static CookieSyncManager sRef;
     private static boolean sGetInstanceAllowed = false;
+    private static final Object sLock = new Object();
 
     private CookieSyncManager() {
         super(null, null);
@@ -77,12 +78,14 @@
      *
      * @return CookieSyncManager
      */
-    public static synchronized CookieSyncManager getInstance() {
-        checkInstanceIsAllowed();
-        if (sRef == null) {
-            sRef = new CookieSyncManager();
+    public static CookieSyncManager getInstance() {
+        synchronized (sLock) {
+            checkInstanceIsAllowed();
+            if (sRef == null) {
+                sRef = new CookieSyncManager();
+            }
+            return sRef;
         }
-        return sRef;
     }
 
     /**
@@ -90,12 +93,14 @@
      * @param context
      * @return CookieSyncManager
      */
-    public static synchronized CookieSyncManager createInstance(Context context) {
-        if (context == null) {
-            throw new IllegalArgumentException("Invalid context argument");
+    public static CookieSyncManager createInstance(Context context) {
+        synchronized (sLock) {
+            if (context == null) {
+                throw new IllegalArgumentException("Invalid context argument");
+            }
+            setGetInstanceIsAllowed();
+            return getInstance();
         }
-        setGetInstanceIsAllowed();
-        return getInstance();
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cbfe43a..6f3fa36 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2322,7 +2322,7 @@
         }
     }
 
-    private static synchronized WebViewFactoryProvider getFactory() {
+    private static WebViewFactoryProvider getFactory() {
         return WebViewFactory.getProvider();
     }
 
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
index 86f813e..3629162 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.media.MediaScannerConnection;
 import android.net.Uri;
@@ -50,6 +51,9 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
 
 public class NekoLand extends Activity implements PrefsListener {
     public static boolean DEBUG = false;
@@ -98,7 +102,19 @@
                 cats[i] = Cat.create(this);
             }
         } else {
-            cats = mPrefs.getCats().toArray(new Cat[0]);
+            final float[] hsv = new float[3];
+            List<Cat> list = mPrefs.getCats();
+            Collections.sort(list, new Comparator<Cat>() {
+                @Override
+                public int compare(Cat cat, Cat cat2) {
+                    Color.colorToHSV(cat.getBodyColor(), hsv);
+                    float bodyH1 = hsv[0];
+                    Color.colorToHSV(cat2.getBodyColor(), hsv);
+                    float bodyH2 = hsv[0];
+                    return Float.compare(bodyH1, bodyH2);
+                }
+            });
+            cats = list.toArray(new Cat[0]);
         }
         mAdapter.setCats(cats);
         return cats.length;
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index fe134db..801de5d 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -80,8 +80,18 @@
     <item msgid="3606047780792894151">"4 MB vienam žurnāla buferim"</item>
     <item msgid="5431354956856655120">"16 MB vienam žurnāla buferim"</item>
   </string-array>
-    <!-- no translation found for select_logpersist_titles:3 (2850427388488887328) -->
-    <!-- no translation found for select_logpersist_summaries:3 (8489661142527693381) -->
+  <string-array name="select_logpersist_titles">
+    <item msgid="1744840221860799971">"Izslēgts"</item>
+    <item msgid="3054662377365844197">"Visi"</item>
+    <item msgid="688870735111627832">"Visi, izņemot radio"</item>
+    <item msgid="2850427388488887328">"tikai kodolā"</item>
+  </string-array>
+  <string-array name="select_logpersist_summaries">
+    <item msgid="2216470072500521830">"Izslēgts"</item>
+    <item msgid="172978079776521897">"Visi reģistra buferi"</item>
+    <item msgid="3873873912383879240">"Visi, izņemot radio reģistra buferus"</item>
+    <item msgid="8489661142527693381">"tikai kodola žurnāla buferī"</item>
+  </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="8134156599370824081">"Animācija ir izslēgta"</item>
     <item msgid="6624864048416710414">"Animācijas mērogs: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 659007b..8a3febd 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -80,8 +80,18 @@
     <item msgid="3606047780792894151">"每个日志缓冲区 4M"</item>
     <item msgid="5431354956856655120">"每个日志缓冲区 16M"</item>
   </string-array>
-    <!-- no translation found for select_logpersist_titles:3 (2850427388488887328) -->
-    <!-- no translation found for select_logpersist_summaries:3 (8489661142527693381) -->
+  <string-array name="select_logpersist_titles">
+    <item msgid="1744840221860799971">"关闭"</item>
+    <item msgid="3054662377365844197">"全部"</item>
+    <item msgid="688870735111627832">"所有非无线电"</item>
+    <item msgid="2850427388488887328">"仅限内核"</item>
+  </string-array>
+  <string-array name="select_logpersist_summaries">
+    <item msgid="2216470072500521830">"关闭"</item>
+    <item msgid="172978079776521897">"所有日志缓冲区"</item>
+    <item msgid="3873873912383879240">"所有非无线电日志缓冲区"</item>
+    <item msgid="8489661142527693381">"仅限内核日志缓冲区"</item>
+  </string-array>
   <string-array name="window_animation_scale_entries">
     <item msgid="8134156599370824081">"关闭动画"</item>
     <item msgid="6624864048416710414">"动画缩放 0.5x"</item>
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 53c2fd7..407d315 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -30,7 +30,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
 import libcore.util.HexEncoding;
 
 import java.io.FileDescriptor;
@@ -457,7 +457,9 @@
 
     private void closeSocket() {
         if (mSocket != null) {
-            IoUtils.closeQuietly(mSocket);
+            try {
+                IoBridge.closeAndSignalBlockedThreads(mSocket);
+            } catch (IOException ignored) {}
         }
         mSocket = null;
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 1dfd305..2903e3a4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -91,16 +91,14 @@
 
     @LayoutlibDelegate
     static long nCreateTree(long rootGroupPtr) {
-        VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
-        return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
+        return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroupPtr));
     }
 
     @LayoutlibDelegate
     static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) {
-        VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
         VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr);
         return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rendererToCopy,
-                rootGroup));
+                rootGroupPtr));
     }
 
     @LayoutlibDelegate
@@ -163,7 +161,6 @@
     @LayoutlibDelegate
     static long nCreateFullPath(long nativeFullPathPtr) {
         VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
-
         return sPathManager.addNewDelegate(new VFullPath_Delegate(original));
     }
 
@@ -247,8 +244,7 @@
     @LayoutlibDelegate
     static long nCreateGroup(long groupPtr) {
         VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
-        return sPathManager.addNewDelegate(
-                new VGroup_Delegate(original, new ArrayMap<String, Object>()));
+        return sPathManager.addNewDelegate(new VGroup_Delegate(original, new ArrayMap<>()));
     }
 
     @LayoutlibDelegate
@@ -1029,7 +1025,7 @@
         private final Path mPath;
         private final Path mRenderPath;
         private final Matrix mFinalPathMatrix = new Matrix();
-        private final VGroup_Delegate mRootGroup;
+        private final long mRootGroupPtr;
         private float mViewportWidth = 0;
         private float mViewportHeight = 0;
         private float mRootAlpha = 1.0f;
@@ -1037,15 +1033,15 @@
         private Paint mFillPaint;
         private PathMeasure mPathMeasure;
 
-        private VPathRenderer_Delegate(VGroup_Delegate rootGroup) {
-            mRootGroup = rootGroup;
+        private VPathRenderer_Delegate(long rootGroupPtr) {
+            mRootGroupPtr = rootGroupPtr;
             mPath = new Path();
             mRenderPath = new Path();
         }
 
         private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy,
-                VGroup_Delegate rootGroup) {
-            this(rootGroup);
+                long rootGroupPtr) {
+            this(rootGroupPtr);
             mViewportWidth = rendererToCopy.mViewportWidth;
             mViewportHeight = rendererToCopy.mViewportHeight;
             mRootAlpha = rendererToCopy.mRootAlpha;
@@ -1087,7 +1083,7 @@
 
         public void draw(long canvasPtr, long filterPtr, int w, int h) {
             // Traverse the tree in pre-order to draw.
-            drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
+            drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
         }
 
         private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr,
@@ -1227,5 +1223,14 @@
         @Override
         public void setName(String name) {
         }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+
+            // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we
+            // need to free it here.
+            sPathManager.removeJavaReferenceFor(mRootGroupPtr);
+        }
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
index 4f00b5d..aa393a9 100644
--- a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -21,6 +21,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.InflateException;
 
 public class BridgePreferenceInflater extends PreferenceInflater {
 
@@ -42,7 +43,15 @@
             viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie();
         }
 
-        Preference preference = super.onCreateItem(name, attrs);
+        Preference preference = null;
+        try {
+            preference = super.onCreateItem(name, attrs);
+        } catch (ClassNotFoundException | InflateException exception) {
+            // name is probably not a valid preference type
+            if ("SwitchPreferenceCompat".equals(name)) {
+                preference = super.onCreateItem("SwitchPreference", attrs);
+            }
+        }
 
         if (viewKey != null && bc != null) {
             bc.addCookie(preference, viewKey);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index d432120..ab27819 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -21,6 +21,7 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
 
 import android.annotation.NonNull;
@@ -116,7 +117,7 @@
     private static void setProperty(@NonNull Object object, @NonNull String propertyClassName,
       @NonNull Object propertyValue, @NonNull String propertySetter)
             throws ReflectionException {
-        Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName);
+        Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName);
         setProperty(object, propertyClass, propertyValue, propertySetter);
     }
 
@@ -126,22 +127,4 @@
         invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue);
     }
 
-    /**
-     * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
-     * the name {@code className}.
-     * <p/>
-     * This is used when we cannot use Class.forName() since the class we want was loaded from a
-     * different ClassLoader.
-     */
-    @NonNull
-    private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
-        Class<?> superClass = object.getClass();
-        while (superClass != null) {
-            if (className.equals(superClass.getName())) {
-                return superClass;
-            }
-            superClass = superClass.getSuperclass();
-        }
-        throw new RuntimeException("invalid object/classname combination.");
-    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
new file mode 100644
index 0000000..0124e83
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android.support;
+
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ScrollView;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getClassInstance;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+
+/**
+ * Class with utility methods to instantiate Preferences provided by the support library.
+ * This class uses reflection to access the support preference objects so it heavily depends on
+ * the API being stable.
+ */
+public class SupportPreferencesUtil {
+    private static final String PREFERENCE_PKG = "android.support.v7.preference";
+    private static final String PREFERENCE_MANAGER = PREFERENCE_PKG + ".PreferenceManager";
+    private static final String PREFERENCE_GROUP = PREFERENCE_PKG + ".PreferenceGroup";
+    private static final String PREFERENCE_GROUP_ADAPTER =
+      PREFERENCE_PKG + ".PreferenceGroupAdapter";
+    private static final String PREFERENCE_INFLATER = PREFERENCE_PKG + ".PreferenceInflater";
+
+    private SupportPreferencesUtil() {
+    }
+
+    @NonNull
+    private static Object instantiateClass(@NonNull LayoutlibCallback callback,
+            @NonNull String className, @Nullable Class[] constructorSignature,
+            @Nullable Object[] constructorArgs) throws ReflectionException {
+        try {
+            Object instance = callback.loadClass(className, constructorSignature, constructorArgs);
+            if (instance == null) {
+                throw new ClassNotFoundException(className + " class not found");
+            }
+            return instance;
+        } catch (ClassNotFoundException e) {
+            throw new ReflectionException(e);
+        }
+    }
+
+    @NonNull
+    private static Object createPreferenceGroupAdapter(@NonNull LayoutlibCallback callback,
+            @NonNull Object preferenceScreen) throws ReflectionException {
+        Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+
+        return instantiateClass(callback, PREFERENCE_GROUP_ADAPTER,
+                new Class[]{preferenceGroupClass}, new Object[]{preferenceScreen});
+    }
+
+    @NonNull
+    private static Object createInflatedPreference(@NonNull LayoutlibCallback callback,
+      @NonNull Context context, @NonNull XmlPullParser parser, @NonNull Object preferenceScreen,
+      @NonNull Object preferenceManager) throws ReflectionException {
+        Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+        Object preferenceInflater = instantiateClass(callback, PREFERENCE_INFLATER,
+          new Class[]{Context.class, preferenceManager.getClass()},
+          new Object[]{context, preferenceManager});
+        Object inflatedPreference = invoke(
+          getMethod(preferenceInflater.getClass(), "inflate", XmlPullParser.class,
+            preferenceGroupClass), preferenceInflater, parser, null);
+
+        if (inflatedPreference == null) {
+            throw new ReflectionException("inflate method returned null");
+        }
+
+        return inflatedPreference;
+    }
+
+    /**
+     * Returns a themed wrapper context of {@link BridgeContext} with the theme specified in
+     * ?attr/preferenceTheme applied to it.
+     */
+    @Nullable
+    private static Context getThemedContext(@NonNull BridgeContext bridgeContext) {
+        RenderResources resources = bridgeContext.getRenderResources();
+        ResourceValue preferenceTheme = resources.findItemInTheme("preferenceTheme", false);
+
+        if (preferenceTheme != null) {
+            // resolve it, if needed.
+            preferenceTheme = resources.resolveResValue(preferenceTheme);
+        }
+        if (preferenceTheme instanceof StyleResourceValue) {
+            int styleId = bridgeContext.getDynamicIdByStyle(((StyleResourceValue) preferenceTheme));
+            if (styleId != 0) {
+                return new ContextThemeWrapper(bridgeContext, styleId);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns a {@link LinearLayout} containing all the UI widgets representing the preferences
+     * passed in the group adapter.
+     */
+    @Nullable
+    private static LinearLayout setUpPreferencesListView(@NonNull BridgeContext bridgeContext,
+            @NonNull Context themedContext, @NonNull ArrayList<Object> viewCookie,
+            @NonNull Object preferenceGroupAdapter) throws ReflectionException {
+        // Setup the LinearLayout that will contain the preferences
+        LinearLayout listView = new LinearLayout(themedContext);
+        listView.setOrientation(LinearLayout.VERTICAL);
+        listView.setLayoutParams(
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+        if (!viewCookie.isEmpty()) {
+            bridgeContext.addViewKey(listView, viewCookie.get(0));
+        }
+
+        // Get all the preferences and add them to the LinearLayout
+        Integer preferencesCount =
+                (Integer) invoke(getMethod(preferenceGroupAdapter.getClass(), "getItemCount"),
+                        preferenceGroupAdapter);
+        if (preferencesCount == null) {
+            return listView;
+        }
+
+        Method getItemId = getMethod(preferenceGroupAdapter.getClass(), "getItemId", int.class);
+        Method getItemViewType =
+                getMethod(preferenceGroupAdapter.getClass(), "getItemViewType", int.class);
+        Method onCreateViewHolder =
+                getMethod(preferenceGroupAdapter.getClass(), "onCreateViewHolder", ViewGroup.class,
+                        int.class);
+        for (int i = 0; i < preferencesCount; i++) {
+            Long id = (Long) invoke(getItemId, preferenceGroupAdapter, i);
+            if (id == null) {
+                continue;
+            }
+
+            // Get the type of the preference layout and bind it to a newly created view holder
+            Integer type = (Integer) invoke(getItemViewType, preferenceGroupAdapter, i);
+            Object viewHolder =
+                    invoke(onCreateViewHolder, preferenceGroupAdapter, listView, type);
+            if (viewHolder == null) {
+                continue;
+            }
+            invoke(getMethod(preferenceGroupAdapter.getClass(), "onBindViewHolder",
+                    viewHolder.getClass(), int.class), preferenceGroupAdapter, viewHolder, i);
+
+            try {
+                // Get the view from the view holder and add it to our layout
+                View itemView =
+                        (View) viewHolder.getClass().getField("itemView").get(viewHolder);
+
+                int arrayPosition = id.intValue() - 1; // IDs are 1 based
+                if (arrayPosition >= 0 && arrayPosition < viewCookie.size()) {
+                    bridgeContext.addViewKey(itemView, viewCookie.get(arrayPosition));
+                }
+                listView.addView(itemView);
+            } catch (IllegalAccessException | NoSuchFieldException ignored) {
+            }
+        }
+
+        return listView;
+    }
+
+    /**
+     * Inflates a preferences layout using the support library. If the support library is not
+     * available, this method will return null without advancing the parsers.
+     */
+    @Nullable
+    public static View inflatePreference(@NonNull BridgeContext bridgeContext,
+            @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+        try {
+            LayoutlibCallback callback = bridgeContext.getLayoutlibCallback();
+
+            Context context = getThemedContext(bridgeContext);
+            if (context == null) {
+                // Probably we couldn't find the "preferenceTheme" in the theme
+                return null;
+            }
+
+            // Create PreferenceManager
+            Object preferenceManager =
+                    instantiateClass(callback, PREFERENCE_MANAGER, new Class[]{Context.class},
+                            new Object[]{context});
+
+            // From this moment on, we can assume that we found the support library and that
+            // nothing should fail
+
+            // Create PreferenceScreen
+            Object preferenceScreen =
+                    invoke(getMethod(preferenceManager.getClass(), "createPreferenceScreen",
+                            Context.class), preferenceManager, context);
+            if (preferenceScreen == null) {
+                return null;
+            }
+
+            // Setup a parser that stores the list of cookies in the same order as the preferences
+            // are inflated. That way we can later reconstruct the list using the preference id
+            // since they are sequential and start in 1.
+            ArrayList<Object> viewCookie = new ArrayList<>();
+            if (parser instanceof BridgeXmlBlockParser) {
+                // Setup a parser that stores the XmlTag
+                parser = new BridgeXmlBlockParser(parser, null, false) {
+                    @Override
+                    public Object getViewCookie() {
+                        return ((BridgeXmlBlockParser) getParser()).getViewCookie();
+                    }
+
+                    @Override
+                    public int next() throws XmlPullParserException, IOException {
+                        int ev = super.next();
+                        if (ev == XmlPullParser.START_TAG) {
+                            viewCookie.add(this.getViewCookie());
+                        }
+
+                        return ev;
+                    }
+                };
+            }
+
+            // Create the PreferenceInflater
+            Object inflatedPreference =
+              createInflatedPreference(callback, context, parser, preferenceScreen,
+                preferenceManager);
+
+            // Setup the RecyclerView (set adapter and layout manager)
+            Object preferenceGroupAdapter =
+                    createPreferenceGroupAdapter(callback, inflatedPreference);
+
+            // Instead of just setting the group adapter as adapter for a RecyclerView, we manually
+            // get all the items and add them to a LinearLayout. This allows us to set the view
+            // cookies so the preferences are correctly linked to their XML.
+            LinearLayout listView = setUpPreferencesListView(bridgeContext, context, viewCookie,
+                    preferenceGroupAdapter);
+
+            ScrollView scrollView = new ScrollView(context);
+            scrollView.setLayoutParams(
+              new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+            scrollView.addView(listView);
+
+            if (root != null) {
+                root.addView(scrollView);
+            }
+
+            return scrollView;
+        } catch (ReflectionException e) {
+            return null;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 0c39026..ea40ba7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -165,5 +165,6 @@
             int idx = sDelegates.indexOfValue(reference);
             out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
         }
+        out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c890793..feed045 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -45,6 +45,7 @@
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.android.graphics.NopCanvas;
 import com.android.layoutlib.bridge.android.support.DesignLibUtil;
+import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.resources.ResourceType;
@@ -326,8 +327,14 @@
             boolean isPreference = "PreferenceScreen".equals(rootTag);
             View view;
             if (isPreference) {
-                view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+                // First try to use the support library inflater. If something fails, fallback
+                // to the system preference inflater.
+                view = SupportPreferencesUtil.inflatePreference(getContext(), mBlockParser,
                         mContentRoot);
+                if (view == null) {
+                    view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+                            mContentRoot);
+                }
             } else {
                 view = mInflater.inflate(mBlockParser, mContentRoot);
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index 7ce27b6..040191e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -37,6 +37,15 @@
         }
     }
 
+    @NonNull
+    public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
+      @Nullable Class<?>... params) throws ReflectionException {
+        Method method = getMethod(clazz, name, params);
+        method.setAccessible(true);
+
+        return method;
+    }
+
     @Nullable
     public static Object invoke(@NonNull Method method, @Nullable Object object,
             @Nullable Object... args) throws ReflectionException {
@@ -74,6 +83,25 @@
     }
 
     /**
+     * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
+     * the name {@code className}.
+     * <p>
+     * This is used when we cannot use Class.forName() since the class we want was loaded from a
+     * different ClassLoader.
+     */
+    @NonNull
+    public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
+        Class<?> superClass = object.getClass();
+        while (superClass != null) {
+            if (className.equals(superClass.getName())) {
+                return superClass;
+            }
+            superClass = superClass.getSuperclass();
+        }
+        throw new RuntimeException("invalid object/classname combination.");
+    }
+
+    /**
      * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
      * introduced in 1.7 and we are still on 1.6
      */
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index ba687fe..d160268 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -349,7 +349,11 @@
         obj = null;
         while(ref.get() != null) {
             System.gc();
+            System.runFinalization();
         }
+
+        System.gc();
+        System.runFinalization();
     }
 
     @AfterClass