Merge "Support library gradle improvements" into mnc-ub-dev am: 04292f146c
am: f09f1e1a1c

* commit 'f09f1e1a1c932d2d0ed446dc7167c016a2d2df93':
  Support library gradle improvements
diff --git a/annotations/build.gradle b/annotations/build.gradle
index 755830f..61d036b 100644
--- a/annotations/build.gradle
+++ b/annotations/build.gradle
@@ -7,6 +7,9 @@
     main.java.srcDir 'src'
 }
 
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
+
 jar {
     from sourceSets.main.output
 }
@@ -51,6 +54,13 @@
     source    sourceSets.main.allJava
 }
 
+// Disable strict javadoc lint checks with javadoc v8 builds on Mac. http://b/26744780
+if (JavaVersion.current().isJava8Compatible()) {
+    tasks.withType(Javadoc) {
+        options.addBooleanOption('Xdoclint:none', true)
+    }
+}
+
 // custom tasks for creating source/javadoc jars
 task sourcesJar(type: Jar, dependsOn:classes) {
     classifier = 'sources'
diff --git a/annotations/src/android/support/annotation/AnimRes.java b/annotations/src/android/support/annotation/AnimRes.java
index 906461b..e91fa81 100644
--- a/annotations/src/android/support/annotation/AnimRes.java
+++ b/annotations/src/android/support/annotation/AnimRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an anim resource reference (e.g. {@link android.R.anim#fade_in}).
+ * to be an anim resource reference (e.g. {@code android.R.anim.fade_in}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/AnimatorRes.java b/annotations/src/android/support/annotation/AnimatorRes.java
index 4681236..e969356 100644
--- a/annotations/src/android/support/annotation/AnimatorRes.java
+++ b/annotations/src/android/support/annotation/AnimatorRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an animator resource reference (e.g. {@link android.R.animator#fade_in}).
+ * to be an animator resource reference (e.g. {@code android.R.animator.fade_in}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/ArrayRes.java b/annotations/src/android/support/annotation/ArrayRes.java
index 347de36..758bce4 100644
--- a/annotations/src/android/support/annotation/ArrayRes.java
+++ b/annotations/src/android/support/annotation/ArrayRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an array resource reference (e.g. {@link android.R.array#phoneTypes}).
+ * to be an array resource reference (e.g. {@code android.R.array.phoneTypes}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/AttrRes.java b/annotations/src/android/support/annotation/AttrRes.java
index 7ba1f0d..2034be5 100644
--- a/annotations/src/android/support/annotation/AttrRes.java
+++ b/annotations/src/android/support/annotation/AttrRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an attribute reference (e.g. {@link android.R.attr#action}).
+ * to be an attribute reference (e.g. {@code android.R.attr.action}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/BinderThread.java b/annotations/src/android/support/annotation/BinderThread.java
index 4dc4233..db7dd3a 100644
--- a/annotations/src/android/support/annotation/BinderThread.java
+++ b/annotations/src/android/support/annotation/BinderThread.java
@@ -30,10 +30,10 @@
  * on the binder thread.
  * <p>
  * Example:
- * <pre>{@code
- *  (&#64;BinderThread
+ * <pre><code>
+ *  &#64;BinderThread
  *  public BeamShareData createBeamShareData() { ... }
- * }</pre>
+ * </code></pre>
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/CallSuper.java b/annotations/src/android/support/annotation/CallSuper.java
index d321e87..9c01cdf 100644
--- a/annotations/src/android/support/annotation/CallSuper.java
+++ b/annotations/src/android/support/annotation/CallSuper.java
@@ -26,10 +26,10 @@
  * Denotes that any overriding methods should invoke this method as well.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;CallSuper
  *  public abstract void onFocusLost();
- * }</pre>
+ * </code></pre>
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/CheckResult.java b/annotations/src/android/support/annotation/CheckResult.java
index 2a11dd4..7f40676 100644
--- a/annotations/src/android/support/annotation/CheckResult.java
+++ b/annotations/src/android/support/annotation/CheckResult.java
@@ -30,7 +30,7 @@
  * <p>
  * Example:
  * <pre>{@code
- *  public &#64;CheckResult String trim(String s) { return s.trim(); }
+ *  public @CheckResult String trim(String s) { return s.trim(); }
  *  ...
  *  s.trim(); // this is probably an error
  *  s = s.trim(); // ok
diff --git a/annotations/src/android/support/annotation/ColorInt.java b/annotations/src/android/support/annotation/ColorInt.java
index 9b3cb7e..4fa46eb 100644
--- a/annotations/src/android/support/annotation/ColorInt.java
+++ b/annotations/src/android/support/annotation/ColorInt.java
@@ -31,7 +31,7 @@
  * <p>
  * Example:
  * <pre>{@code
- *  public abstract void setTextColor(&#64;ColorInt int color);
+ *  public abstract void setTextColor(@ColorInt int color);
  * }</pre>
  */
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/ColorRes.java b/annotations/src/android/support/annotation/ColorRes.java
index eb273c4..d0fd49a 100644
--- a/annotations/src/android/support/annotation/ColorRes.java
+++ b/annotations/src/android/support/annotation/ColorRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a color resource reference (e.g. {@link android.R.color#black}).
+ * to be a color resource reference (e.g. {@code android.R.color.black}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/DimenRes.java b/annotations/src/android/support/annotation/DimenRes.java
index c3492a5..08dc4a9 100644
--- a/annotations/src/android/support/annotation/DimenRes.java
+++ b/annotations/src/android/support/annotation/DimenRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a dimension resource reference (e.g. {@link android.R.dimen#app_icon_size}).
+ * to be a dimension resource reference (e.g. {@code android.R.dimen.app_icon_size}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/DrawableRes.java b/annotations/src/android/support/annotation/DrawableRes.java
index 0ea1bca..f130380 100644
--- a/annotations/src/android/support/annotation/DrawableRes.java
+++ b/annotations/src/android/support/annotation/DrawableRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a drawable resource reference (e.g. {@link android.R.attr#alertDialogIcon}).
+ * to be a drawable resource reference (e.g. {@code android.R.attr.alertDialogIcon}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/FloatRange.java b/annotations/src/android/support/annotation/FloatRange.java
index 2544388..275b5b6 100644
--- a/annotations/src/android/support/annotation/FloatRange.java
+++ b/annotations/src/android/support/annotation/FloatRange.java
@@ -29,12 +29,12 @@
  * Denotes that the annotated element should be a float or double in the given range
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;FloatRange(from=0.0,to=1.0)
  *  public float getAlpha() {
  *      ...
  *  }
- * }</pre>
+ * </code></pre>
  */
 @Retention(CLASS)
 @Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
diff --git a/annotations/src/android/support/annotation/IdRes.java b/annotations/src/android/support/annotation/IdRes.java
index 9a0060f..7cb3f98 100644
--- a/annotations/src/android/support/annotation/IdRes.java
+++ b/annotations/src/android/support/annotation/IdRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an id resource reference (e.g. {@link android.R.id#copy}).
+ * to be an id resource reference (e.g. {@code android.R.id.copy}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/IntDef.java b/annotations/src/android/support/annotation/IntDef.java
index fedd7b4..be2e2b8 100644
--- a/annotations/src/android/support/annotation/IntDef.java
+++ b/annotations/src/android/support/annotation/IntDef.java
@@ -32,24 +32,24 @@
  * multiple constants can be combined.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;Retention(SOURCE)
- *  &#64;IntDef(&#123;NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS&#125;)
- *  public &#64;interface NavigationMode &#123;&#125;
+ *  &#64;IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ *  public @interface NavigationMode {}
  *  public static final int NAVIGATION_MODE_STANDARD = 0;
  *  public static final int NAVIGATION_MODE_LIST = 1;
  *  public static final int NAVIGATION_MODE_TABS = 2;
  *  ...
- *  public abstract void setNavigationMode(&#64;NavigationMode int mode);
+ *  public abstract void setNavigationMode(@NavigationMode int mode);
  *  &#64;NavigationMode
  *  public abstract int getNavigationMode();
- * }</pre>
+ * </code></pre>
  * For a flag, set the flag attribute:
- * <pre>{@code
+ * <pre><code>
  *  &#64;IntDef(
  *      flag = true
- *      value = &#123;NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS&#125;)
- * }</pre>
+ *      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ * </code></pre>
  */
 @Retention(SOURCE)
 @Target({ANNOTATION_TYPE})
diff --git a/annotations/src/android/support/annotation/IntRange.java b/annotations/src/android/support/annotation/IntRange.java
index 6011276..d489acb 100644
--- a/annotations/src/android/support/annotation/IntRange.java
+++ b/annotations/src/android/support/annotation/IntRange.java
@@ -18,6 +18,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
 import static java.lang.annotation.ElementType.METHOD;
@@ -28,15 +29,15 @@
  * Denotes that the annotated element should be an int or long in the given range
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;IntRange(from=0,to=255)
  *  public int getAlpha() {
  *      ...
  *  }
- * }</pre>
+ * </code></pre>
  */
 @Retention(CLASS)
-@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
+@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
 public @interface IntRange {
     /** Smallest value, inclusive */
     long from() default Long.MIN_VALUE;
diff --git a/annotations/src/android/support/annotation/IntegerRes.java b/annotations/src/android/support/annotation/IntegerRes.java
index 6bfcc37..d965675 100644
--- a/annotations/src/android/support/annotation/IntegerRes.java
+++ b/annotations/src/android/support/annotation/IntegerRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an integer resource reference (e.g. {@link android.R.integer#config_shortAnimTime}).
+ * to be an integer resource reference (e.g. {@code android.R.integer.config_shortAnimTime}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/InterpolatorRes.java b/annotations/src/android/support/annotation/InterpolatorRes.java
index 20f42b8..3195c57 100644
--- a/annotations/src/android/support/annotation/InterpolatorRes.java
+++ b/annotations/src/android/support/annotation/InterpolatorRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be an interpolator resource reference (e.g. {@link android.R.interpolator#cycle}).
+ * to be an interpolator resource reference (e.g. {@code android.R.interpolator.cycle}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/Keep.java b/annotations/src/android/support/annotation/Keep.java
index de1c9e5..111971c 100644
--- a/annotations/src/android/support/annotation/Keep.java
+++ b/annotations/src/android/support/annotation/Keep.java
@@ -33,12 +33,12 @@
  * so a compiler may think that the code is unused.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;Keep
  *  public void foo() {
  *      ...
  *  }
- * }</pre>
+ * </code></pre>
  */
 @Retention(CLASS)
 @Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})
diff --git a/annotations/src/android/support/annotation/LayoutRes.java b/annotations/src/android/support/annotation/LayoutRes.java
index ad04ef0..0de4e84 100644
--- a/annotations/src/android/support/annotation/LayoutRes.java
+++ b/annotations/src/android/support/annotation/LayoutRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a layout resource reference (e.g. {@link android.R.layout#list_content}).
+ * to be a layout resource reference (e.g. {@code android.R.layout.list_content}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/MainThread.java b/annotations/src/android/support/annotation/MainThread.java
index cff464f..1ce8b3c 100644
--- a/annotations/src/android/support/annotation/MainThread.java
+++ b/annotations/src/android/support/annotation/MainThread.java
@@ -30,10 +30,10 @@
  * on the main thread.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;MainThread
  *  public void deliverResult(D data) { ... }
- * }</pre>
+ * </code></pre>
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/RequiresPermission.java b/annotations/src/android/support/annotation/RequiresPermission.java
index bb9afb2..1ff1964 100644
--- a/annotations/src/android/support/annotation/RequiresPermission.java
+++ b/annotations/src/android/support/annotation/RequiresPermission.java
@@ -22,40 +22,59 @@
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
 /**
  * Denotes that the annotated element requires (or may require) one or more permissions.
- * <p/>
+ * <p>
  * Example of requiring a single permission:
- * <pre>{@code
+ * <pre><code>
  *   &#64;RequiresPermission(Manifest.permission.SET_WALLPAPER)
  *   public abstract void setWallpaper(Bitmap bitmap) throws IOException;
  *
  *   &#64;RequiresPermission(ACCESS_COARSE_LOCATION)
  *   public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
  * Example of requiring at least one permission from a set:
- * <pre>{@code
+ * <pre><code>
  *   &#64;RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
  *   public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
  * Example of requiring multiple permissions:
- * <pre>{@code
+ * <pre><code>
  *   &#64;RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
  *   public abstract Location getLastKnownLocation(String provider);
- * }</pre>
+ * </code></pre>
  * Example of requiring separate read and write permissions for a content provider:
- * <pre>{@code
- *   &#64;RequiresPermission.Read(&#64;RequiresPermission(READ_HISTORY_BOOKMARKS))
- *   &#64;RequiresPermission.Write(&#64;RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * <pre><code>
+ *   &#64;RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ *   &#64;RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
  *   public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * </code></pre>
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter. For example, consider
+ * {@code android.app.Activity.startActivity(android.content.Intent)}:
+ * <pre>{@code
+ *   public void startActivity(@RequiresPermission Intent intent) { ... }
  * }</pre>
- *
- * @hide
+ * Notice how there are no actual permission names listed in the annotation. The actual
+ * permissions required will depend on the particular intent passed in. For example,
+ * the code may look like this:
+ * <pre>{@code
+ *   Intent intent = new Intent(Intent.ACTION_CALL);
+ *   startActivity(intent);
+ * }</pre>
+ * and the actual permission requirement for this particular intent is described on
+ * the Intent name itself:
+ * <pre><code>
+ *   &#64;RequiresPermission(Manifest.permission.CALL_PHONE)
+ *   public static final String ACTION_CALL = "android.intent.action.CALL";
+ * </code></pre>
  */
 @Retention(CLASS)
-@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
 public @interface RequiresPermission {
     /**
      * The name of the permission that is required, if precisely one permission
@@ -87,18 +106,28 @@
     boolean conditional() default false;
 
     /**
-     * Specifies that the given permission is required for read operations
+     * Specifies that the given permission is required for read operations.
+     * <p>
+     * When specified on a parameter, the annotation indicates that the method requires
+     * a permission which depends on the value of the parameter (and typically
+     * the corresponding field passed in will be one of a set of constants which have
+     * been annotated with a {@code @RequiresPermission} annotation.)
      */
-    @Target(FIELD)
+    @Target({FIELD, METHOD, PARAMETER})
     @interface Read {
-        RequiresPermission value();
+        RequiresPermission value() default @RequiresPermission;
     }
 
     /**
-     * Specifies that the given permission is required for write operations
+     * Specifies that the given permission is required for write operations.
+     * <p>
+     * When specified on a parameter, the annotation indicates that the method requires
+     * a permission which depends on the value of the parameter (and typically
+     * the corresponding field passed in will be one of a set of constants which have
+     * been annotated with a {@code @RequiresPermission} annotation.)
      */
-    @Target(FIELD)
+    @Target({FIELD, METHOD, PARAMETER})
     @interface Write {
-        RequiresPermission value();
+        RequiresPermission value() default @RequiresPermission;
     }
 }
diff --git a/annotations/src/android/support/annotation/Size.java b/annotations/src/android/support/annotation/Size.java
index 5903890..b1c0308 100644
--- a/annotations/src/android/support/annotation/Size.java
+++ b/annotations/src/android/support/annotation/Size.java
@@ -32,7 +32,7 @@
  * <p>
  * Example:
  * <pre>{@code
- *  public void getLocationInWindow(&#64;Size(2) int[] location) {
+ *  public void getLocationInWindow(@Size(2) int[] location) {
  *      ...
  *  }
  * }</pre>
diff --git a/annotations/src/android/support/annotation/StringDef.java b/annotations/src/android/support/annotation/StringDef.java
index cae227e..33f71e9 100644
--- a/annotations/src/android/support/annotation/StringDef.java
+++ b/annotations/src/android/support/annotation/StringDef.java
@@ -29,20 +29,20 @@
  * type and that its value should be one of the explicitly named constants.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;Retention(SOURCE)
- *  &#64;StringDef(&#123;
+ *  &#64;StringDef({
  *     POWER_SERVICE,
  *     WINDOW_SERVICE,
  *     LAYOUT_INFLATER_SERVICE
- *  &#125;)
- *  public &#64;interface ServiceName &#123;&#125;
+ *  })
+ *  public @interface ServiceName {}
  *  public static final String POWER_SERVICE = "power";
  *  public static final String WINDOW_SERVICE = "window";
  *  public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
  *  ...
- *  public abstract Object getSystemService(&#64;ServiceName String name);
- * }</pre>
+ *  public abstract Object getSystemService(@ServiceName String name);
+ * </code></pre>
  */
 @Retention(SOURCE)
 @Target({ANNOTATION_TYPE})
diff --git a/annotations/src/android/support/annotation/StringRes.java b/annotations/src/android/support/annotation/StringRes.java
index 2c1149c..cf427f6 100644
--- a/annotations/src/android/support/annotation/StringRes.java
+++ b/annotations/src/android/support/annotation/StringRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a String resource reference (e.g. {@link android.R.string#ok}).
+ * to be a String resource reference (e.g. {@code android.R.string.ok}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/StyleRes.java b/annotations/src/android/support/annotation/StyleRes.java
index 6d931bf..ca7a187 100644
--- a/annotations/src/android/support/annotation/StyleRes.java
+++ b/annotations/src/android/support/annotation/StyleRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a style resource reference (e.g. {@link android.R.style#TextAppearance}).
+ * to be a style resource reference (e.g. {@code android.R.style.TextAppearance}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/StyleableRes.java b/annotations/src/android/support/annotation/StyleableRes.java
index d7902d1..6e4d97f 100644
--- a/annotations/src/android/support/annotation/StyleableRes.java
+++ b/annotations/src/android/support/annotation/StyleableRes.java
@@ -27,7 +27,7 @@
 
 /**
  * Denotes that an integer parameter, field or method return value is expected
- * to be a styleable resource reference (e.g. {@link android.R.styleable#TextView_text}).
+ * to be a styleable resource reference (e.g. {@code android.R.styleable.TextView_text}).
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/UiThread.java b/annotations/src/android/support/annotation/UiThread.java
index 98318b4..98d35f9 100644
--- a/annotations/src/android/support/annotation/UiThread.java
+++ b/annotations/src/android/support/annotation/UiThread.java
@@ -30,11 +30,11 @@
  * on the UI thread.
  * <p>
  * Example:
- * <pre>{@code
+ * <pre><code>
  *  &#64;UiThread
  *
- *  public abstract void setText(&#64;NonNull String text) { ... }
- * }</pre>
+ *  public abstract void setText(@NonNull String text) { ... }
+ * </code></pre>
  */
 @Documented
 @Retention(CLASS)
diff --git a/annotations/src/android/support/annotation/WorkerThread.java b/annotations/src/android/support/annotation/WorkerThread.java
index 2919286..a5aabd7 100644
--- a/annotations/src/android/support/annotation/WorkerThread.java
+++ b/annotations/src/android/support/annotation/WorkerThread.java
@@ -30,10 +30,10 @@
  * on a worker thread.
  * <p>
  * Example:
- * <pre>{@code
- *  (&#64;WorkerThread
+ * <pre><code>
+ *  &#64;WorkerThread
  *  protected abstract FilterResults performFiltering(CharSequence constraint);
- * }</pre>
+ * </code></pre>
  */
 @Documented
 @Retention(CLASS)
diff --git a/apicheck.mk b/apicheck.mk
index 739cfb3..97146cf 100644
--- a/apicheck.mk
+++ b/apicheck.mk
@@ -46,8 +46,9 @@
     $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(support_module),,COMMON)/src
 LOCAL_SDK_VERSION := current
 
+LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/src
+
 LOCAL_DROIDDOC_OPTIONS:= \
-    -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/src \
     -stubpackages "$(subst $(space),:,$(support_module_java_packages))" \
     -api $(support_module_api_file) \
     -removedApi $(support_module_removed_file) \
diff --git a/build.gradle b/build.gradle
index 56f0479..14a4c77 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@
     }
 }
 
-ext.supportVersion = '23.2.0-SNAPSHOT'
+ext.supportVersion = '24.0.0-SNAPSHOT'
 ext.extraVersion = 25
 ext.supportRepoOut = ''
 ext.buildToolsVersion = '23.0.2'
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index ec07441..7f6bed6 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -376,19 +376,26 @@
                     && mDummyView.getVisibility() == VISIBLE;
 
             if (mDrawCollapsingTitle) {
+                final boolean isRtl = ViewCompat.getLayoutDirection(this)
+                        == ViewCompat.LAYOUT_DIRECTION_RTL;
+
+                // Update the collapsed bounds
                 int bottomOffset = 0;
                 if (mToolbarDirectChild != null && mToolbarDirectChild != this) {
                     final LayoutParams lp = (LayoutParams) mToolbarDirectChild.getLayoutParams();
                     bottomOffset = lp.bottomMargin;
                 }
                 ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
-                mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left,
-                        bottom - mTmpRect.height() - bottomOffset,
-                        mTmpRect.right,
-                        bottom - bottomOffset);
+                mCollapsingTextHelper.setCollapsedBounds(
+                        mTmpRect.left + (isRtl
+                                ? mToolbar.getTitleMarginEnd()
+                                : mToolbar.getTitleMarginStart()),
+                        bottom + mToolbar.getTitleMarginTop() - mTmpRect.height() - bottomOffset,
+                        mTmpRect.right + (isRtl
+                                ? mToolbar.getTitleMarginStart()
+                                : mToolbar.getTitleMarginEnd()),
+                        bottom - bottomOffset - mToolbar.getTitleMarginBottom());
 
-                final boolean isRtl = ViewCompat.getLayoutDirection(this)
-                        == ViewCompat.LAYOUT_DIRECTION_RTL;
                 // Update the expanded bounds
                 mCollapsingTextHelper.setExpandedBounds(
                         isRtl ? mExpandedMarginEnd : mExpandedMarginStart,
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index ea5b0c4..a5ce9a3 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -356,7 +356,7 @@
                 // Cancel all behaviors beneath the one that intercepted.
                 // If the event is "down" then we don't have anything to cancel yet.
                 if (b != null) {
-                    if (cancelEvent == null) {
+                    if (cancelEvent != null) {
                         final long now = SystemClock.uptimeMillis();
                         cancelEvent = MotionEvent.obtain(now, now,
                                 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index 84786c0..e83b0cf 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -25,6 +25,7 @@
 import android.os.Message;
 import android.support.annotation.ColorInt;
 import android.support.annotation.IntDef;
+import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.StringRes;
 import android.support.design.R;
@@ -121,6 +122,7 @@
      * @hide
      */
     @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
+    @IntRange(from = 1)
     @Retention(RetentionPolicy.SOURCE)
     public @interface Duration {}
 
diff --git a/documents-archive/Android.mk b/documents-archive/Android.mk
new file mode 100644
index 0000000..187ef79
--- /dev/null
+++ b/documents-archive/Android.mk
@@ -0,0 +1,37 @@
+# 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.
+# 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.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-documents-archive
+LOCAL_SDK_VERSION := current
+LOCAL_AIDL_INCLUDES := $LOCAL_PATH/src
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android-support-annotations \
+    android-support-v4
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# API Check
+# ---------------------------------------------
+support_module := $(LOCAL_MODULE)
+support_module_api_dir := $(LOCAL_PATH)/api
+support_module_src_files := $(LOCAL_SRC_FILES)
+support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES)
+support_module_java_packages := android.support.provider
+include $(SUPPORT_API_CHECK)
diff --git a/v7/appcompat/res/values-h320dp/bools.xml b/documents-archive/AndroidManifest.xml
similarity index 77%
rename from v7/appcompat/res/values-h320dp/bools.xml
rename to documents-archive/AndroidManifest.xml
index 5576c18..2cd0f7a 100644
--- a/v7/appcompat/res/values-h320dp/bools.xml
+++ b/documents-archive/AndroidManifest.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <bool name="abc_allow_stacked_button_bar">true</bool>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.provider">
+    <uses-sdk android:minSdkVersion="19"/>
+    <application />
+</manifest>
diff --git a/documents-archive/api/current.txt b/documents-archive/api/current.txt
new file mode 100644
index 0000000..5bc1103
--- /dev/null
+++ b/documents-archive/api/current.txt
@@ -0,0 +1,19 @@
+package android.support.provider {
+
+  public class DocumentArchiveHelper {
+    ctor public DocumentArchiveHelper(android.provider.DocumentsProvider, char);
+    method public void close();
+    method public void closeArchive(java.lang.String);
+    method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException;
+    method public boolean isArchivedDocument(java.lang.String);
+    method public boolean isChildDocument(java.lang.String, java.lang.String);
+    method public boolean isSupportedArchiveType(java.lang.String);
+    method public android.os.ParcelFileDescriptor openDocument(java.lang.String, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public android.content.res.AssetFileDescriptor openDocumentThumbnail(java.lang.String, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
+    method public android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
+    field public static final java.lang.String COLUMN_LOCAL_FILE_PATH = "local_file_path";
+  }
+
+}
+
diff --git a/documents-archive/api/removed.txt b/documents-archive/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/documents-archive/api/removed.txt
diff --git a/documents-archive/build.gradle b/documents-archive/build.gradle
new file mode 100644
index 0000000..8c6e323
--- /dev/null
+++ b/documents-archive/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'documents-archive'
+
+dependencies {
+    compile project(':support-v4')
+    compile project(':support-annotations')
+}
+
+android {
+    compileSdkVersion 'current'
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.aidl.srcDirs = ['src']
+        main.res.srcDir 'res'
+        main.assets.srcDir 'assets'
+        main.resources.srcDir 'java'
+
+        androidTest.setRoot('tests')
+        androidTest.java.srcDir('tests/src/')
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
diff --git a/documents-archive/src/android/support/provider/DocumentArchive.java b/documents-archive/src/android/support/provider/DocumentArchive.java
new file mode 100644
index 0000000..1182b0d
--- /dev/null
+++ b/documents-archive/src/android/support/provider/DocumentArchive.java
@@ -0,0 +1,471 @@
+/*
+ * 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.net.Uri;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsProvider;
+import android.support.annotation.Nullable;
+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.Map;
+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;
+
+/**
+ * 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
+ */
+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, 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>());
+
+        // Traversing entries via ZipFile.entries() is very slow, so copy the entries to a temporary
+        // map for building the tree.
+        final Map<String, ZipEntry> entries = new HashMap<String, ZipEntry>();
+        for (final ZipEntry entry : Collections.list(mZipFile.entries())) {
+            entries.put(entry.getName(), entry);
+            if (entry.isDirectory()) {
+                mTree.put(entry.getName(), new ArrayList<ZipEntry>());
+            }
+        }
+
+        int delimiterIndex;
+        List<ZipEntry> parentList;
+        String parentPath;
+        for (final ZipEntry entry : entries.values()) {
+            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) {
+                parentList.add(entry);
+            } else {
+                Log.w(TAG, "Archived files without a parent are not supported: " + entry.getName());
+            }
+        }
+    }
+
+    /**
+     * 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 = mZipFile.getEntry(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 = mZipFile.getEntry(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 = mZipFile.getEntry(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 = mZipFile.getEntry(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 = mZipFile.getEntry(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.");
+
+        // TODO: Extract thumbnails from EXIF.
+        final ZipEntry entry = mZipFile.getEntry(parsedId.mPath);
+        if (entry == null) {
+            throw new FileNotFoundException();
+        }
+
+        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();
+            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
new file mode 100644
index 0000000..717eb03
--- /dev/null
+++ b/documents-archive/src/android/support/provider/DocumentArchiveHelper.java
@@ -0,0 +1,368 @@
+/*
+ * 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.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;
+
+/**
+ * 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
+ */
+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
new file mode 100644
index 0000000..61047dc
--- /dev/null
+++ b/documents-archive/src/android/support/provider/IoUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.io.Closeable;
+import java.io.InputStream;
+import java.util.Collection;
+
+/**
+ * Simple static methods to perform common IO operations.
+ * @hide
+ */
+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
new file mode 100644
index 0000000..ee2c366
--- /dev/null
+++ b/documents-archive/src/android/support/provider/ParsedDocumentId.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+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
new file mode 100644
index 0000000..fd62a2e
--- /dev/null
+++ b/documents-archive/src/android/support/provider/Preconditions.java
@@ -0,0 +1,54 @@
+/*
+ * 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.text.TextUtils;
+
+import java.util.Collection;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ * @hide
+ */
+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
new file mode 100644
index 0000000..f42b9ad
--- /dev/null
+++ b/documents-archive/tests/Android.mk
@@ -0,0 +1,28 @@
+# 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 := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-documents-archive
+LOCAL_JAVA_LIBRARIES := android.test.runner
+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
new file mode 100644
index 0000000..47da733
--- /dev/null
+++ b/documents-archive/tests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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/res/raw/archive.zip b/documents-archive/tests/res/raw/archive.zip
new file mode 100644
index 0000000..c3b8d22
--- /dev/null
+++ b/documents-archive/tests/res/raw/archive.zip
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
new file mode 100644
index 0000000..afe99b4
--- /dev/null
+++ b/documents-archive/tests/src/android/support/provider/DocumentArchiveTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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;
+
+    @Override
+    public void setUp() {
+        // 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(R.raw.archive);
+            ) {
+                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 {
+        final Cursor cursor = mArchive.queryChildDocuments(DOCUMENT_ID, null, null);
+
+        assertTrue(cursor.moveToFirst());
+        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: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: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 testGetDocumentType() throws IOException {
+        assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType("document-id:dir1/"));
+        assertEquals("text/plain", mArchive.getDocumentType("document-id:file1.txt"));
+    }
+
+    public void testIsChildDocument() throws IOException {
+        assertTrue(mArchive.isChildDocument(DOCUMENT_ID, "document-id:dir1"));
+        assertTrue(mArchive.isChildDocument("document-id:dir1/", "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 {
+        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 {
+        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
new file mode 100644
index 0000000..0445d82
--- /dev/null
+++ b/documents-archive/tests/src/android/support/provider/IntegrationTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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
new file mode 100644
index 0000000..3f72cd2
--- /dev/null
+++ b/documents-archive/tests/src/android/support/provider/StubProvider.java
@@ -0,0 +1,154 @@
+/*
+ * 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
new file mode 100644
index 0000000..17ec3e1
--- /dev/null
+++ b/documents-archive/tests/src/android/support/provider/TestUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
index 956b428..2416f88 100644
--- a/percent/src/android/support/percent/PercentLayoutHelper.java
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -72,6 +72,9 @@
 public class PercentLayoutHelper {
     private static final String TAG = "PercentLayout";
 
+    private static final boolean DEBUG = false;
+    private static final boolean VERBOSE = false;
+
     private final ViewGroup mHost;
 
     public PercentLayoutHelper(ViewGroup host) {
@@ -96,7 +99,7 @@
      * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
      */
     public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
+        if (DEBUG) {
             Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
                     + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
                     + View.MeasureSpec.toString(heightMeasureSpec));
@@ -107,13 +110,13 @@
         for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
             View view = mHost.getChildAt(i);
             ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (DEBUG) {
                 Log.d(TAG, "should adjust " + view + " " + params);
             }
             if (params instanceof PercentLayoutParams) {
                 PercentLayoutInfo info =
                         ((PercentLayoutParams) params).getPercentLayoutInfo();
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                if (DEBUG) {
                     Log.d(TAG, "using " + info);
                 }
                 if (info != null) {
@@ -139,7 +142,7 @@
         float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent width: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -147,7 +150,7 @@
         }
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent height: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -155,7 +158,7 @@
         }
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -167,7 +170,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent left margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -176,7 +179,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent top margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -185,7 +188,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent right margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -194,7 +197,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent bottom margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -203,7 +206,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent start margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -212,7 +215,7 @@
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
                 -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "percent end margin: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -221,7 +224,7 @@
 
         value = array.getFraction(R.styleable.PercentLayout_Layout_layout_aspectRatio, 1, 1, -1f);
         if (value != -1f) {
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            if (VERBOSE) {
                 Log.v(TAG, "aspect ratio: " + value);
             }
             info = info != null ? info : new PercentLayoutInfo();
@@ -229,7 +232,7 @@
         }
 
         array.recycle();
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
+        if (DEBUG) {
             Log.d(TAG, "constructed: " + info);
         }
         return info;
@@ -244,13 +247,13 @@
         for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
             View view = mHost.getChildAt(i);
             ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (DEBUG) {
                 Log.d(TAG, "should restore " + view + " " + params);
             }
             if (params instanceof PercentLayoutParams) {
                 PercentLayoutInfo info =
                         ((PercentLayoutParams) params).getPercentLayoutInfo();
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                if (DEBUG) {
                     Log.d(TAG, "using " + info);
                 }
                 if (info != null) {
@@ -283,7 +286,7 @@
         for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
             View view = mHost.getChildAt(i);
             ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (DEBUG) {
                 Log.d(TAG, "should handle measured state too small " + view + " " + params);
             }
             if (params instanceof PercentLayoutParams) {
@@ -301,7 +304,7 @@
                 }
             }
         }
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
+        if (DEBUG) {
             Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
         }
         return needsSecondMeasure;
@@ -411,7 +414,7 @@
                 }
             }
 
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (DEBUG) {
                 Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
             }
         }
@@ -474,7 +477,7 @@
                 MarginLayoutParamsCompat.resolveLayoutDirection(params,
                         ViewCompat.getLayoutDirection(view));
             }
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (DEBUG) {
                 Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
                         + ")");
             }
diff --git a/tests/Android.mk b/tests/Android.mk
index 7bd3b00..7993eed 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-v4 junit-runner
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := AndroidSupportTests
 
diff --git a/v13/build.gradle b/v13/build.gradle
index 5060842..b0d59e6 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -51,6 +51,9 @@
     compile files(internalJar.archivePath)
 }
 
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
+
 android {
     compileSdkVersion 13
 
@@ -60,7 +63,6 @@
         //targetSdkVersion 19
     }
 
-
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['java']
@@ -70,6 +72,11 @@
         androidTest.java.srcDir 'tests/java'
     }
 
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index fc7a9aa..6f56bac 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -21,7 +21,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v14-preference-res
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_SRC_FILES := $(call all-java-files-under, ../../v7/preference/constants)
 LOCAL_RESOURCE_DIR := \
         frameworks/support/v7/appcompat/res \
         frameworks/support/v7/preference/res \
@@ -56,4 +56,4 @@
 support_module_src_files := $(LOCAL_SRC_FILES)
 support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES)
 support_module_java_packages := android.support.v14.preference
-include $(SUPPORT_API_CHECK)
\ No newline at end of file
+include $(SUPPORT_API_CHECK)
diff --git a/v14/preference/res/layout/preference_dropdown_material.xml b/v14/preference/res/layout/preference_dropdown_material.xml
new file mode 100644
index 0000000..d1ca64e
--- /dev/null
+++ b/v14/preference/res/layout/preference_dropdown_material.xml
@@ -0,0 +1,30 @@
+<?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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Spinner
+        android:id="@+id/spinner"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:visibility="invisible" />
+
+    <include layout="@layout/preference_material" />
+
+</FrameLayout>
diff --git a/v14/preference/res/layout/preference_widget_switch.xml b/v14/preference/res/layout/preference_widget_switch.xml
index ae83afa..afc4351 100644
--- a/v14/preference/res/layout/preference_widget_switch.xml
+++ b/v14/preference/res/layout/preference_widget_switch.xml
@@ -18,7 +18,7 @@
 <!-- Layout used by SwitchPreference for the switch widget style. This is inflated
      inside android.R.layout.preference. -->
 <Switch xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/switchWidget"
+    android:id="@android:id/switch_widget"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:focusable="false"
diff --git a/v14/preference/res/values/styles.xml b/v14/preference/res/values/styles.xml
index c197121..a83417a 100644
--- a/v14/preference/res/values/styles.xml
+++ b/v14/preference/res/values/styles.xml
@@ -60,6 +60,10 @@
         <item name="android:layout">@layout/preference_material</item>
     </style>
 
+    <style name="Preference.DropDown.Material">
+        <item name="android:layout">@layout/preference_dropdown_material</item>
+    </style>
+
     <style name="Preference_TextAppearanceMaterialBody2">
         <item name="android:textSize">14sp</item>
         <item name="android:fontFamily">sans-serif</item>
diff --git a/v14/preference/res/values/themes.xml b/v14/preference/res/values/themes.xml
index 64bc91e..026d2d8 100644
--- a/v14/preference/res/values/themes.xml
+++ b/v14/preference/res/values/themes.xml
@@ -33,6 +33,7 @@
         <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.Material</item>
         <item name="dialogPreferenceStyle">@style/Preference.DialogPreference.Material</item>
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
+        <item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
         <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Material</item>
     </style>
 </resources>
diff --git a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
index 2a636fe..8316b0d 100644
--- a/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
+++ b/v14/preference/src/android/support/v14/preference/MultiSelectListPreference.java
@@ -72,7 +72,8 @@
     }
 
     public MultiSelectListPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.dialogPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+                android.R.attr.dialogPreferenceStyle));
     }
 
     public MultiSelectListPreference(Context context) {
diff --git a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
index b9fc935..c0b69f0 100644
--- a/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
+++ b/v14/preference/src/android/support/v14/preference/PreferenceFragment.java
@@ -28,7 +28,9 @@
 import android.os.Message;
 import android.support.annotation.Nullable;
 import android.support.annotation.XmlRes;
+import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v4.view.ViewCompat;
+import android.support.v7.preference.AndroidResources;
 import android.support.v7.preference.DialogPreference;
 import android.support.v7.preference.EditTextPreference;
 import android.support.v7.preference.ListPreference;
@@ -242,7 +244,8 @@
 
         TypedArray a = mStyledContext.obtainStyledAttributes(null,
                 R.styleable.PreferenceFragment,
-                R.attr.preferenceFragmentStyle,
+                TypedArrayUtils.getAttr(mStyledContext, R.attr.preferenceFragmentStyle,
+                        AndroidResources.ANDROID_R_PREFERENCE_FRAGMENT_STYLE),
                 0);
 
         mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_android_layout, mLayoutResId);
@@ -263,10 +266,10 @@
 
         final View view = themedInflater.inflate(mLayoutResId, container, false);
 
-        final View rawListContainer = view.findViewById(R.id.list_container);
+        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
         if (!(rawListContainer instanceof ViewGroup)) {
-            throw new RuntimeException("Content has view with id attribute 'R.id.list_container' "
-                    + "that is not a ViewGroup class");
+            throw new RuntimeException("Content has view with id attribute "
+                    + "'android.R.id.list_container' that is not a ViewGroup class");
         }
 
         final ViewGroup listContainer = (ViewGroup) rawListContainer;
@@ -315,14 +318,19 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
 
         if (mHavePrefs) {
             bindPreferences();
         }
 
         mInitDone = true;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
 
         if (savedInstanceState != null) {
             Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
@@ -351,9 +359,12 @@
 
     @Override
     public void onDestroyView() {
-        mList = null;
         mHandler.removeCallbacks(mRequestFocus);
         mHandler.removeMessages(MSG_BIND_PREFERENCES);
+        if (mHavePrefs) {
+            unbindPreferences();
+        }
+        mList = null;
         super.onDestroyView();
     }
 
@@ -520,6 +531,15 @@
         onBindPreferences();
     }
 
+    private void unbindPreferences() {
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        if (preferenceScreen != null) {
+            preferenceScreen.onDetached();
+            getListView().setAdapter(null);
+        }
+        onUnbindPreferences();
+    }
+
     /** @hide */
     protected void onBindPreferences() {
     }
@@ -668,22 +688,26 @@
 
         private boolean shouldDrawDividerAbove(View view, RecyclerView parent) {
             final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
-            return holder.getAdapterPosition() == 0 &&
+            return holder.getAdapterPosition() == 0 && holder instanceof PreferenceViewHolder &&
                     ((PreferenceViewHolder) holder).isDividerAllowedAbove();
         }
 
         private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
-            final PreferenceViewHolder holder =
-                    (PreferenceViewHolder) parent.getChildViewHolder(view);
+            final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
+            final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
+                    && ((PreferenceViewHolder) holder).isDividerAllowedBelow();
+            if (!dividerAllowedBelow) {
+                return false;
+            }
             boolean nextAllowed = true;
             int index = parent.indexOfChild(view);
             if (index < parent.getChildCount() - 1) {
                 final View nextView = parent.getChildAt(index + 1);
-                final PreferenceViewHolder nextHolder =
-                        (PreferenceViewHolder) parent.getChildViewHolder(nextView);
-                nextAllowed = nextHolder.isDividerAllowedAbove();
+                final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
+                nextAllowed = nextHolder instanceof PreferenceViewHolder
+                        && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
             }
-            return nextAllowed && holder.isDividerAllowedBelow();
+            return nextAllowed;
         }
 
         public void setDivider(Drawable divider) {
diff --git a/v14/preference/src/android/support/v14/preference/SwitchPreference.java b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
index 1a46cc4..ac2a9c6 100644
--- a/v14/preference/src/android/support/v14/preference/SwitchPreference.java
+++ b/v14/preference/src/android/support/v14/preference/SwitchPreference.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.AndroidResources;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.support.v7.preference.TwoStatePreference;
 import android.util.AttributeSet;
@@ -121,7 +122,8 @@
      * @param attrs Style attributes that differ from the default
      */
     public SwitchPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.switchPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
+                android.R.attr.switchPreferenceStyle));
     }
 
     /**
@@ -136,7 +138,7 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        View switchView = holder.findViewById(R.id.switchWidget);
+        View switchView = holder.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
         syncSwitchView(switchView);
         syncSummaryView(holder);
     }
diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt
index d90f9f6..3b7bab6 100644
--- a/v17/leanback/api/current.txt
+++ b/v17/leanback/api/current.txt
@@ -485,6 +485,7 @@
 
   public class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
     ctor public PlaybackOverlayFragment();
+    method public void fadeOut();
     method public int getBackgroundType();
     method public android.support.v17.leanback.app.PlaybackOverlayFragment.OnFadeCompleteListener getFadeCompleteListener();
     method public final android.support.v17.leanback.app.PlaybackOverlayFragment.InputEventHandler getInputEventHandler();
@@ -513,6 +514,7 @@
 
   public class PlaybackOverlaySupportFragment extends android.support.v17.leanback.app.DetailsSupportFragment {
     ctor public PlaybackOverlaySupportFragment();
+    method public void fadeOut();
     method public int getBackgroundType();
     method public android.support.v17.leanback.app.PlaybackOverlaySupportFragment.OnFadeCompleteListener getFadeCompleteListener();
     method public final android.support.v17.leanback.app.PlaybackOverlaySupportFragment.InputEventHandler getInputEventHandler();
@@ -1528,6 +1530,10 @@
     method public void setSecondaryLabels(java.lang.String[]);
   }
 
+  public static class PlaybackControlsRow.PictureInPictureAction extends android.support.v17.leanback.widget.Action {
+    ctor public PlaybackControlsRow.PictureInPictureAction(android.content.Context);
+  }
+
   public static class PlaybackControlsRow.PlayPauseAction extends android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction {
     ctor public PlaybackControlsRow.PlayPauseAction(android.content.Context);
     field public static int PAUSE;
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_pip.png b/v17/leanback/res/drawable-xhdpi/lb_ic_pip.png
new file mode 100644
index 0000000..1a59e47
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_pip.png
Binary files differ
diff --git a/v17/leanback/res/values-af/strings.xml b/v17/leanback/res/values-af/strings.xml
index 0310403..7698f40 100644
--- a/v17/leanback/res/values-af/strings.xml
+++ b/v17/leanback/res/values-af/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiveer hoë gehalte"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktiveer onderskrifte"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiveer onderskrifte"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Voer prent in prentmodus in"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooi"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Gaan voort"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-am/strings.xml b/v17/leanback/res/values-am/strings.xml
index 77d2993..0d5f076 100644
--- a/v17/leanback/res/values-am/strings.xml
+++ b/v17/leanback/res/values-am/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ከፍተኛ ጥራትን አሰናክል"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አንቃ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ዝግ የምስል ስር ጽሑፍ አጻጻፍን አሰናክል"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ስዕሉን በስዕል ሁነታ ውስጥ ያክሉ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ጨርስ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ቀጥል"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"፦"</string>
 </resources>
diff --git a/v17/leanback/res/values-ar/strings.xml b/v17/leanback/res/values-ar/strings.xml
index c52be7f..0f62ca9 100644
--- a/v17/leanback/res/values-ar/strings.xml
+++ b/v17/leanback/res/values-ar/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"تعطيل الجودة العالية"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"تمكين الترجمة المصاحبة"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"تعطيل الترجمة المصاحبة"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"إدخال صورة في وضع الصورة"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"إنهاء"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"متابعة"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-az-rAZ/strings.xml b/v17/leanback/res/values-az-rAZ/strings.xml
index cb558a0..e23920d 100644
--- a/v17/leanback/res/values-az-rAZ/strings.xml
+++ b/v17/leanback/res/values-az-rAZ/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksək keyfiyyəti deaktiv edin"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Qapalı çəkilişi aktiv edin"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Qapalı çəkilişi deaktiv edin"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Şəkil içində Şəkil Rejiminə daxil olun"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Bitir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davam edin"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-b+sr+Latn/strings.xml b/v17/leanback/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..6c9bbb5
--- /dev/null
+++ b/v17/leanback/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="orb_search_action" msgid="5651268540267663887">"Radnja pretrage"</string>
+    <string name="lb_search_bar_hint" msgid="8325490927970116252">"Pretražite"</string>
+    <string name="lb_search_bar_hint_speech" msgid="5511270823320183816">"Govorite da biste pretraživali"</string>
+    <string name="lb_search_bar_hint_with_title" msgid="1627103380996590035">"Pretražite <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_search_bar_hint_with_title_speech" msgid="2712734639766312034">"Izgovorite da biste pretražili <xliff:g id="SEARCH_CONTEXT">%1$s</xliff:g>"</string>
+    <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
+    <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
+    <string name="lb_playback_controls_play" msgid="731953341987346903">"Pusti"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"Pauziraj"</string>
+    <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"Premotaj unapred"</string>
+    <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"Premotaj unapred %1$dX"</string>
+    <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"Premotaj unazad"</string>
+    <string name="lb_playback_controls_rewind_multiplier" msgid="1640629531440849942">"Premotaj unazad %1$dX"</string>
+    <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Preskoči sledeću"</string>
+    <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Preskoči prethodnu"</string>
+    <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Još radnji"</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Opozovi izbor palca nagore"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Izaberi palac nagore"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Opozovi izbor palca nadole"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Izaberi palac nadole"</string>
+    <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ne ponavljaj nijednu"</string>
+    <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Ponovi sve"</string>
+    <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Ponovi jednu"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="1099874107835264529">"Omogući nasumičnu reprodukciju"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8388150597335115226">"Onemogući nasumičnu reprodukciju"</string>
+    <string name="lb_playback_controls_high_quality_enable" msgid="202415780019335254">"Omogući visok kvalitet"</string>
+    <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Onemogući visok kvalitet"</string>
+    <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogući titlove"</string>
+    <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogući titlove"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Uđi u režim Slika u slici"</string>
+    <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dovrši"</string>
+    <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastavi"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
+</resources>
diff --git a/v17/leanback/res/values-bg/strings.xml b/v17/leanback/res/values-bg/strings.xml
index 6b16775..459c4f4 100644
--- a/v17/leanback/res/values-bg/strings.xml
+++ b/v17/leanback/res/values-bg/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Деактивиране на високото качество"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Активиране на субтитрите"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Деактивиране на субтитрите"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Вход в режима „Картина в картина“"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Край"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Напред"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-bn-rBD/strings.xml b/v17/leanback/res/values-bn-rBD/strings.xml
index 04669bd..0f03c18 100644
--- a/v17/leanback/res/values-bn-rBD/strings.xml
+++ b/v17/leanback/res/values-bn-rBD/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"উচ্চ গুণমান অক্ষম করুন"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"সাবটাইটেল সক্ষম করুন"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"সাবটাইটেল অক্ষম করুন"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ছবি মোডে ছবি প্রবেশ করান"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"শেষ করুন"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"চালিয়ে যান"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-bs-rBA/strings.xml b/v17/leanback/res/values-bs-rBA/strings.xml
new file mode 100644
index 0000000..a2e02f0
--- /dev/null
+++ b/v17/leanback/res/values-bs-rBA/strings.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright (C) 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for orb_search_action (5651268540267663887) -->
+    <skip />
+    <!-- no translation found for lb_search_bar_hint (8325490927970116252) -->
+    <skip />
+    <!-- no translation found for lb_search_bar_hint_speech (5511270823320183816) -->
+    <skip />
+    <!-- no translation found for lb_search_bar_hint_with_title (1627103380996590035) -->
+    <skip />
+    <!-- no translation found for lb_search_bar_hint_with_title_speech (2712734639766312034) -->
+    <skip />
+    <!-- no translation found for lb_control_display_fast_forward_multiplier (4541442045214207774) -->
+    <skip />
+    <!-- no translation found for lb_control_display_rewind_multiplier (3097220783222910245) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_play (731953341987346903) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_pause (6189521112079849518) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_fast_forward (8569951318244687220) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_fast_forward_multiplier (1058753672110224526) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_rewind (2227196334132350684) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_rewind_multiplier (1640629531440849942) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_skip_next (2946499493161095772) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_skip_previous (2326801832933178348) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_more_actions (2330770008796987655) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_thumb_up (6530420347129222601) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_thumb_up_outline (1577637924003500946) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_thumb_down (4498041193172964797) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_thumb_down_outline (2936020280629424365) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_repeat_none (87476947476529036) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_repeat_all (6730354406289599000) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_repeat_one (3285202316452203619) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_shuffle_enable (1099874107835264529) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_shuffle_disable (8388150597335115226) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_high_quality_enable (202415780019335254) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_high_quality_disable (8637371582779057866) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_closed_captioning_enable (2429655367176440226) -->
+    <skip />
+    <!-- no translation found for lb_playback_controls_closed_captioning_disable (6133362019475930048) -->
+    <skip />
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Unesite sliku u načinu rada za slike"</string>
+    <!-- no translation found for lb_guidedaction_finish_title (4015190340667946245) -->
+    <skip />
+    <!-- no translation found for lb_guidedaction_continue_title (8842094924543063706) -->
+    <skip />
+    <!-- no translation found for lb_date_separator (2440386660906697298) -->
+    <skip />
+    <!-- no translation found for lb_time_separator (2763247350845477227) -->
+    <skip />
+</resources>
diff --git a/v17/leanback/res/values-ca/strings.xml b/v17/leanback/res/values-ca/strings.xml
index 6578f3b..cdafe0c 100644
--- a/v17/leanback/res/values-ca/strings.xml
+++ b/v17/leanback/res/values-ca/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactiva l\'alta qualitat"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activa els subtítols tancats"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactiva els subtítols tancats"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entra al mode d\'imatge en imatge"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalitza"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-cs/strings.xml b/v17/leanback/res/values-cs/strings.xml
index 8ffb4f3..b9a6006 100644
--- a/v17/leanback/res/values-cs/strings.xml
+++ b/v17/leanback/res/values-cs/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Vypnout vysokou kvalitu"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnout titulky"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnout titulky"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Přejít do režimu Obraz v obraze"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončit"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovat"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-da/strings.xml b/v17/leanback/res/values-da/strings.xml
index 87c507b..b8fe757 100644
--- a/v17/leanback/res/values-da/strings.xml
+++ b/v17/leanback/res/values-da/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiver høj kvalitet"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér undertekster"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver undertekster"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tilføj billedet i billedtilstand"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Afslut"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsæt"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-de/strings.xml b/v17/leanback/res/values-de/strings.xml
index 9d018c6..d731309 100644
--- a/v17/leanback/res/values-de/strings.xml
+++ b/v17/leanback/res/values-de/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Hohe Qualität deaktivieren"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Untertitel aktivieren"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Untertitel deaktivieren"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Bild-in-Bild-Modus aktivieren"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fertigstellen"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Weiter"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-el/strings.xml b/v17/leanback/res/values-el/strings.xml
index 310b6a9..525d03cd 100644
--- a/v17/leanback/res/values-el/strings.xml
+++ b/v17/leanback/res/values-el/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Απενεργοποίηση Υψηλής ποιότητας"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ενεργοποίηση υποτίτλων"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Απενεργοποίηση υποτίτλων"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Εισαγωγή εικόνας στη Λειτουργία εικόνας"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Τέλος"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Συνέχεια"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rAU/strings.xml b/v17/leanback/res/values-en-rAU/strings.xml
index 0097135..e22808f 100644
--- a/v17/leanback/res/values-en-rAU/strings.xml
+++ b/v17/leanback/res/values-en-rAU/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rGB/strings.xml b/v17/leanback/res/values-en-rGB/strings.xml
index 0097135..e22808f 100644
--- a/v17/leanback/res/values-en-rGB/strings.xml
+++ b/v17/leanback/res/values-en-rGB/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-en-rIN/strings.xml b/v17/leanback/res/values-en-rIN/strings.xml
index 0097135..e22808f 100644
--- a/v17/leanback/res/values-en-rIN/strings.xml
+++ b/v17/leanback/res/values-en-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disable High Quality"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Enable Closed Captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disable Closed Captioning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Enter Picture In Picture Mode"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continue"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-es-rUS/strings.xml b/v17/leanback/res/values-es-rUS/strings.xml
index 8341a4d..b9c9a47 100644
--- a/v17/leanback/res/values-es-rUS/strings.xml
+++ b/v17/leanback/res/values-es-rUS/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inhabilitar calidad alta"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar el modo Imagen en imagen"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-es/strings.xml b/v17/leanback/res/values-es/strings.xml
index 9f308c0..9d14d0d 100644
--- a/v17/leanback/res/values-es/strings.xml
+++ b/v17/leanback/res/values-es/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inhabilitar alta calidad"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Habilitar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inhabilitar subtítulos"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar modo Imagen en imagen"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-et-rEE/strings.xml b/v17/leanback/res/values-et-rEE/strings.xml
index a97c385..5aff8af 100644
--- a/v17/leanback/res/values-et-rEE/strings.xml
+++ b/v17/leanback/res/values-et-rEE/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Keela kõrgkvaliteetne taasesitus"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Luba subtiitrid"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Keela subtiitrid"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Sisene režiimi Pilt pildis"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Lõpeta"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jätka"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-eu-rES/strings.xml b/v17/leanback/res/values-eu-rES/strings.xml
index c22f172..f71261f 100644
--- a/v17/leanback/res/values-eu-rES/strings.xml
+++ b/v17/leanback/res/values-eu-rES/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desgaitu kalitate handiko erreprodukzioa"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Gaitu azpitituluak"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desgaitu azpitituluak"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Aktibatu \"Argazkia argazkian\" modua"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Amaitu"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jarraitu"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-fa/strings.xml b/v17/leanback/res/values-fa/strings.xml
index 58e823f..f256e6d 100644
--- a/v17/leanback/res/values-fa/strings.xml
+++ b/v17/leanback/res/values-fa/strings.xml
@@ -25,7 +25,7 @@
     <string name="lb_control_display_fast_forward_multiplier" msgid="4541442045214207774">"%1$dX"</string>
     <string name="lb_control_display_rewind_multiplier" msgid="3097220783222910245">"%1$dX"</string>
     <string name="lb_playback_controls_play" msgid="731953341987346903">"پخش"</string>
-    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"توقف موقت"</string>
+    <string name="lb_playback_controls_pause" msgid="6189521112079849518">"مکث"</string>
     <string name="lb_playback_controls_fast_forward" msgid="8569951318244687220">"جلو بردن سریع"</string>
     <string name="lb_playback_controls_fast_forward_multiplier" msgid="1058753672110224526">"‏بازارسال سریع %1$dX"</string>
     <string name="lb_playback_controls_rewind" msgid="2227196334132350684">"عقب بردن"</string>
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"غیرفعال کردن کیفیت بالا"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"فعال کردن زیرنویس"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"غیرفعال کردن زیرنویس"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"وارد حالت تصویر در تصویر شوید"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"پایان"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ادامه"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-fi/strings.xml b/v17/leanback/res/values-fi/strings.xml
index 0c55e6f..9be6cf3 100644
--- a/v17/leanback/res/values-fi/strings.xml
+++ b/v17/leanback/res/values-fi/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Poista korkea laatu käytöstä"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ota tekstitys käyttöön"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Poista tekstitys käytöstä"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vaihda kuva kuvassa ‑tilaan"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Valmis"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Jatka"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
 </resources>
diff --git a/v17/leanback/res/values-fr-rCA/strings.xml b/v17/leanback/res/values-fr-rCA/strings.xml
index b28beae..be3e646 100644
--- a/v17/leanback/res/values-fr-rCA/strings.xml
+++ b/v17/leanback/res/values-fr-rCA/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Désactiver la lecture haute qualité"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer le sous-titrage"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver le sous-titrage"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activer le mode Incrustation d\'image"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-fr/strings.xml b/v17/leanback/res/values-fr/strings.xml
index 914bf08..832b1cf 100644
--- a/v17/leanback/res/values-fr/strings.xml
+++ b/v17/leanback/res/values-fr/strings.xml
@@ -33,10 +33,10 @@
     <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Ignorer l\'élément suivant"</string>
     <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Ignorer l\'élément précédent"</string>
     <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Autres actions"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Désélectionner \"J\'aime\""</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Sélectionner \"J\'aime\""</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Désélectionner \"Je n\'aime pas\""</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Sélectionner \"Je n\'aime pas\""</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Annuler +1"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Sélectionner +1"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Annuler -1"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Sélectionner -1"</string>
     <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ne rien lire en boucle"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Tout lire en boucle"</string>
     <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Lire en boucle un élément"</string>
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Désactiver la haute qualité"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activer les sous-titres"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Désactiver les sous-titres"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activer le mode PIP"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Terminer"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuer"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-gl-rES/strings.xml b/v17/leanback/res/values-gl-rES/strings.xml
index 62b3a2b..d8a0f1c 100644
--- a/v17/leanback/res/values-gl-rES/strings.xml
+++ b/v17/leanback/res/values-gl-rES/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desactivar alta calidade"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activar subtítulos"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desactivar subtítulos"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activar o modo Imaxe superposta"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizar"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-gu-rIN/strings.xml b/v17/leanback/res/values-gu-rIN/strings.xml
index afec9da..cea81b2 100644
--- a/v17/leanback/res/values-gu-rIN/strings.xml
+++ b/v17/leanback/res/values-gu-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ઉચ્ચ ગુણવત્તા અક્ષમ કરો"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ઉપશીર્ષક સક્ષમ કરો"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"વિગતવાર ઉપશીર્ષકોને અક્ષમ કરો"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ચિત્ર મોડમાં ચિત્ર દાખલ કરો"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"સમાપ્ત કરો"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ચાલુ રાખો"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-hi/strings.xml b/v17/leanback/res/values-hi/strings.xml
index c243c17..d608602 100644
--- a/v17/leanback/res/values-hi/strings.xml
+++ b/v17/leanback/res/values-hi/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करें"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षक सक्षम करें"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षक अक्षम करें"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोड में चित्र डालें"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त करें"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी रखें"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-hr/strings.xml b/v17/leanback/res/values-hr/strings.xml
index bb6ebcc..daa4a5f 100644
--- a/v17/leanback/res/values-hr/strings.xml
+++ b/v17/leanback/res/values-hr/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Onemogući visoku kvalitetu"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogući titlove"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogući titlove"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Unos slike u načinu slike"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Završi"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Nastavi"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
 </resources>
diff --git a/v17/leanback/res/values-hu/strings.xml b/v17/leanback/res/values-hu/strings.xml
index cfeb2ad..b8b682e 100644
--- a/v17/leanback/res/values-hu/strings.xml
+++ b/v17/leanback/res/values-hu/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Jó minőségű lejátszás letiltása"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Feliratok engedélyezése"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Feliratok letiltása"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Kép a képben mód indítása"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Befejezés"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Folytatás"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-hy-rAM/strings.xml b/v17/leanback/res/values-hy-rAM/strings.xml
index 1ac5dd8..152c426 100644
--- a/v17/leanback/res/values-hy-rAM/strings.xml
+++ b/v17/leanback/res/values-hy-rAM/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Անջատել բարձր որակը"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Միացնել խորագրերը"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Անջատել խորագրերը"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Մուտք «Նկար նկարի մեջ» ռեժիմ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Վերջ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Շարունակել"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-in/strings.xml b/v17/leanback/res/values-in/strings.xml
index 6569825..78de623 100644
--- a/v17/leanback/res/values-in/strings.xml
+++ b/v17/leanback/res/values-in/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Nonaktifkan Kualitas Tinggi"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktifkan Pembuatan Teks"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Nonaktifkan Pembuatan Teks"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Masukkan Foto Dalam Mode Foto"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Lanjutkan"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
 </resources>
diff --git a/v17/leanback/res/values-is-rIS/strings.xml b/v17/leanback/res/values-is-rIS/strings.xml
index 830b11e..2b937a7 100644
--- a/v17/leanback/res/values-is-rIS/strings.xml
+++ b/v17/leanback/res/values-is-rIS/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Slökkva á miklum gæðum"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Kveikja á skjátextum"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Slökkva á skjátextum"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Skoða mynd í myndsniði"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Ljúka"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Halda áfram"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-it/strings.xml b/v17/leanback/res/values-it/strings.xml
index 2f0ca47..0c211d6 100644
--- a/v17/leanback/res/values-it/strings.xml
+++ b/v17/leanback/res/values-it/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Disattiva alta qualità"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Attiva sottotitoli"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Disattiva sottotitoli"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Attiva modalità Picture-in-picture"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fine"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continua"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-iw/strings.xml b/v17/leanback/res/values-iw/strings.xml
index f2bda58..a98e4b2 100644
--- a/v17/leanback/res/values-iw/strings.xml
+++ b/v17/leanback/res/values-iw/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"השבת איכות גבוהה"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"הפעל כתוביות"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"השבת כתוביות"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"הזן את התמונה במצב תמונה"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"סיום"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"המשך"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ja/strings.xml b/v17/leanback/res/values-ja/strings.xml
index 09faa8b..57d0fdf 100644
--- a/v17/leanback/res/values-ja/strings.xml
+++ b/v17/leanback/res/values-ja/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"高品質を無効にする"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"字幕を有効にする"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"字幕を無効にする"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"PIP モードに移動"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完了"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"続行"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ka-rGE/strings.xml b/v17/leanback/res/values-ka-rGE/strings.xml
index ac9f4cd..f0ef953 100644
--- a/v17/leanback/res/values-ka-rGE/strings.xml
+++ b/v17/leanback/res/values-ka-rGE/strings.xml
@@ -50,6 +50,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"მაღალი ხარისხის გამორთვა"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"დახურული წარწერების ჩართვა"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"დახურული წარწერების გაუქმება"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"რეჟიმზე „სურათი სურათში“ გადასვლა"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"დასრულება"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"გაგრძელება"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-kk-rKZ/strings.xml b/v17/leanback/res/values-kk-rKZ/strings.xml
index 380695b..3752558 100644
--- a/v17/leanback/res/values-kk-rKZ/strings.xml
+++ b/v17/leanback/res/values-kk-rKZ/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жоғары сапаны өшіру"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жасырын титрлерді қосу"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жасырын титрлерді өшіру"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сурет ішіндегі сурет режиміне кіру"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Аяқтау"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Жалғастыру"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-km-rKH/strings.xml b/v17/leanback/res/values-km-rKH/strings.xml
index 60f90e5..45c58d8 100644
--- a/v17/leanback/res/values-km-rKH/strings.xml
+++ b/v17/leanback/res/values-km-rKH/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"បិទ​គុណភាព​ខ្ពស់"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"បើក​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"បិទ​ការ​ដាក់​ចំណង​ដែល​បាន​បិទ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"បញ្ចូលរូបភាពនៅក្នុងរបៀបរូបភាព"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"បញ្ចប់"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"បន្ត"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"៖"</string>
 </resources>
diff --git a/v17/leanback/res/values-kn-rIN/strings.xml b/v17/leanback/res/values-kn-rIN/strings.xml
index 18206ed..ccfb6eb 100644
--- a/v17/leanback/res/values-kn-rIN/strings.xml
+++ b/v17/leanback/res/values-kn-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ಹೆಚ್ಚು ಗುಣಮಟ್ಟವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ಮುಚ್ಚಿದ ಶೀರ್ಷಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ಚಿತ್ರವನ್ನು ಚಿತ್ರ ಮೋಡ್‌ನಲ್ಲಿ ಪ್ರವೇಶಿಸಿ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ಪೂರ್ಣಗೊಳಿಸು"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ಮುಂದುವರಿಸು"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ko/strings.xml b/v17/leanback/res/values-ko/strings.xml
index e262b1c..5f09018 100644
--- a/v17/leanback/res/values-ko/strings.xml
+++ b/v17/leanback/res/values-ko/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"고화질 사용 중지"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"자막 사용 설정"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"자막 사용 중지"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"사진 모드에서 사진 입력"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"완료"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"계속"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ky-rKG/strings.xml b/v17/leanback/res/values-ky-rKG/strings.xml
index 74cc841..3e2c6ec 100644
--- a/v17/leanback/res/values-ky-rKG/strings.xml
+++ b/v17/leanback/res/values-ky-rKG/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Жогорку сапатты өчүрүү"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Жабык субтитрлерди иштетүү"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Жабык субтитрлерди өчүрүү"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Сүрөт режиминде сүрөт киргизүү"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Бүтүрүү"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Улантуу"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-lo-rLA/strings.xml b/v17/leanback/res/values-lo-rLA/strings.xml
index d919e93..8cceef4 100644
--- a/v17/leanback/res/values-lo-rLA/strings.xml
+++ b/v17/leanback/res/values-lo-rLA/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"​ປິດ​ນຳ​ໃຊ້​ການຫຼິ້ນ​ດ້ວຍຄຸນ​ນະ​ພາບ​ສູງ"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"​ເປີດ​ນຳ​ໃຊ້​​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"​ປິດ​ນຳ​ໃຊ້​ຄຳ​ບັນ​ຍາຍ​ແບບ​ປິດ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ປ້ອນຮູບພາບໃນໂໝດຮູບພາບ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ສໍາເລັດ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"​ສືບ​ຕໍ່"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-lt/strings.xml b/v17/leanback/res/values-lt/strings.xml
index 415fc25..96a475b 100644
--- a/v17/leanback/res/values-lt/strings.xml
+++ b/v17/leanback/res/values-lt/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Išjungti aukštą kokybę"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Įgalinti subtitrus"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Išjungti subtitrus"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Įjungti vaizdo vaizde režimą"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Baigti"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tęsti"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-lv/strings.xml b/v17/leanback/res/values-lv/strings.xml
index 3979e43..b4d9729 100644
--- a/v17/leanback/res/values-lv/strings.xml
+++ b/v17/leanback/res/values-lv/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Atspējot augstas kvalitātes vienumu atskaņošanu"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Iespējot slēgtos parakstus"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Atspējot slēgtos parakstus"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Aktivizēt režīmu Attēls attēlā"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Pabeigt"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Turpināt"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-mk-rMK/strings.xml b/v17/leanback/res/values-mk-rMK/strings.xml
index ccf6d62..dd8cec9 100644
--- a/v17/leanback/res/values-mk-rMK/strings.xml
+++ b/v17/leanback/res/values-mk-rMK/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Оневозможи висок квалитет"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Овозможи затворено објаснување"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Оневозможи затворено објаснување"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Влези во режимот „Слика во слика“"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Заврши"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продолжи"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ml-rIN/strings.xml b/v17/leanback/res/values-ml-rIN/strings.xml
index 356b0e9..66ad9bc 100644
--- a/v17/leanback/res/values-ml-rIN/strings.xml
+++ b/v17/leanback/res/values-ml-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ഉയർന്ന നിലവാരം പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനക്ഷമമാക്കുക"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"അടച്ച അടിക്കുറിപ്പ് നൽകൽ പ്രവർത്തനരഹിതമാക്കുക"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'ചിത്രത്തിനുള്ളിൽ ചിത്രം\' മോഡിലേക്ക് പ്രവേശിക്കുക"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"പൂര്‍ത്തിയാക്കുക"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"തുടരുക"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-mn-rMN/strings.xml b/v17/leanback/res/values-mn-rMN/strings.xml
index a7a640f..fc88ba8 100644
--- a/v17/leanback/res/values-mn-rMN/strings.xml
+++ b/v17/leanback/res/values-mn-rMN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Өндөр чанарыг идэвхгүйжүүлэх"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Текст тайлбарыг идэвхжүүлэх"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Текст тайлбарыг идэвхгүйжүүлэх"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Зургийн горимд зураг оруулна уу"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Дуусгах"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Үргэлжлүүлэх"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-mr-rIN/strings.xml b/v17/leanback/res/values-mr-rIN/strings.xml
index b9b568a..8023dad 100644
--- a/v17/leanback/res/values-mr-rIN/strings.xml
+++ b/v17/leanback/res/values-mr-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करा"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षके सक्षम करा"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"उपशीर्षके अक्षम करा"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोडमध्ये चित्र प्रविष्ट करा"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"सुरू ठेवा"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ms-rMY/strings.xml b/v17/leanback/res/values-ms-rMY/strings.xml
index 0a5f8bd..3e340bd 100644
--- a/v17/leanback/res/values-ms-rMY/strings.xml
+++ b/v17/leanback/res/values-ms-rMY/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Lumpuhkan Kualiti Tinggi"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Dayakan Kapsyen Tertutup"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Lumpuhkan Kapsyen Tertutup"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Masukkan Gambar Dalam Mod Gambar"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Selesai"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Teruskan"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-my-rMM/strings.xml b/v17/leanback/res/values-my-rMM/strings.xml
index 1b3cdee..0f2f3d8 100644
--- a/v17/leanback/res/values-my-rMM/strings.xml
+++ b/v17/leanback/res/values-my-rMM/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"အရည်အသွေးကောင်းအား ပိတ်ထားရန်"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"စာတမ်းထိုး ဖွင့်ရန်"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"စာတမ်းထိုးအား ပိတ်ထားရန်"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ဓာတ်ပုံမုဒ်တွင် ဓာတ်ပုံထည့်ပါ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ပြီးပြီ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ဆက်လုပ်ရန်"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"−"</string>
 </resources>
diff --git a/v17/leanback/res/values-nb/strings.xml b/v17/leanback/res/values-nb/strings.xml
index c58544d..6bb2412 100644
--- a/v17/leanback/res/values-nb/strings.xml
+++ b/v17/leanback/res/values-nb/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Deaktiver høy kvalitet"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivér teksting"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Deaktiver teksting"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Slå på modusen Bilde-i-bilde"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Fullfør"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsett"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">"."</string>
 </resources>
diff --git a/v17/leanback/res/values-ne-rNP/strings.xml b/v17/leanback/res/values-ne-rNP/strings.xml
index 54c9285..734f856 100644
--- a/v17/leanback/res/values-ne-rNP/strings.xml
+++ b/v17/leanback/res/values-ne-rNP/strings.xml
@@ -48,6 +48,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणस्तर असक्षम"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"बन्द क्याप्सनहरु सक्षम"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"बन्द क्याप्सनहरु असक्षम"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्रमा चित्र मोड प्रविष्ट गर्नुहोस्"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"समाप्त गर्नुहोस्"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"जारी राख्नुहोस्"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-nl/strings.xml b/v17/leanback/res/values-nl/strings.xml
index 941aa79..f1b07b7 100644
--- a/v17/leanback/res/values-nl/strings.xml
+++ b/v17/leanback/res/values-nl/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Hoge kwaliteit uitschakelen"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ondertiteling inschakelen"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Ondertiteling uitschakelen"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Beeld-in-beeld-modus openen"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Voltooien"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Doorgaan"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"-"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-pa-rIN/strings.xml b/v17/leanback/res/values-pa-rIN/strings.xml
index f197657..c0fca37 100644
--- a/v17/leanback/res/values-pa-rIN/strings.xml
+++ b/v17/leanback/res/values-pa-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ਉੱਚ ਗੁਣਵੱਤਾ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਸਮਰੱਥ ਬਣਾਓ"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ਬੰਦ ਕੈਪਸ਼ਨਿੰਗ ਅਸਮਰੱਥ ਬਣਾਓ"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"ਤਸਵੀਰ ਮੋਡ ਵਿੱਚ ਤਸਵੀਰ ਦਾਖਲ ਕਰੋ"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ਖ਼ਤਮ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ਜਾਰੀ ਰੱਖੋ"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-pl/strings.xml b/v17/leanback/res/values-pl/strings.xml
index 7598c73..476ee0d 100644
--- a/v17/leanback/res/values-pl/strings.xml
+++ b/v17/leanback/res/values-pl/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Wyłącz wysoką jakość"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Włącz napisy"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Wyłącz napisy"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Włącz tryb obrazu w obrazie"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Zakończ"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Dalej"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt-rBR/strings.xml b/v17/leanback/res/values-pt-rBR/strings.xml
index 12c9533..90c213c 100644
--- a/v17/leanback/res/values-pt-rBR/strings.xml
+++ b/v17/leanback/res/values-pt-rBR/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Picture in Picture"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt-rPT/strings.xml b/v17/leanback/res/values-pt-rPT/strings.xml
index 7b12f5e..99ac8dd 100644
--- a/v17/leanback/res/values-pt-rPT/strings.xml
+++ b/v17/leanback/res/values-pt-rPT/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar legendas"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar legendas"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Imagem na imagem"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-pt/strings.xml b/v17/leanback/res/values-pt/strings.xml
index 12c9533..90c213c 100644
--- a/v17/leanback/res/values-pt/strings.xml
+++ b/v17/leanback/res/values-pt/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Desativar alta qualidade"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Ativar closed captioning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Desativar closed captioning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Entrar no modo Picture in Picture"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Concluir"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuar"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ro/strings.xml b/v17/leanback/res/values-ro/strings.xml
index b4f9ee3..de3a4b2 100644
--- a/v17/leanback/res/values-ro/strings.xml
+++ b/v17/leanback/res/values-ro/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Dezactivează calitatea înaltă"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Activează subtitrările"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Dezactivează subtitrările"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Activați modul Picture-in-Picture"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Finalizați"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Continuați"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ru/strings.xml b/v17/leanback/res/values-ru/strings.xml
index 864054a..ab74710 100644
--- a/v17/leanback/res/values-ru/strings.xml
+++ b/v17/leanback/res/values-ru/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Отключить высокое качество."</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Включить субтитры."</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Отключить субтитры."</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Включить режим \"Картинка в картинке\"."</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Готово"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Далее"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-si-rLK/strings.xml b/v17/leanback/res/values-si-rLK/strings.xml
index 3db1293..3dba675 100644
--- a/v17/leanback/res/values-si-rLK/strings.xml
+++ b/v17/leanback/res/values-si-rLK/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"උපරිම ගුණත්වය අබල කරන ලදි"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"වැසුණු ශිර්ෂ කිරීම සබල කරන ලදි"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"වැසුණු ශිර්ෂ කිරීම අබල කරන ලදි"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"පින්තූරය-තුළ-පින්තූරය ප්‍රකාරයට ඇතුළු වන්න"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"අවසානය"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"දිගටම කර ගෙන යන්න"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sk/strings.xml b/v17/leanback/res/values-sk/strings.xml
index a317d18..631bc13 100644
--- a/v17/leanback/res/values-sk/strings.xml
+++ b/v17/leanback/res/values-sk/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Zakázať médiá vo vysokej kvalite"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Zapnúť skryté titulky"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Vypnúť skryté titulky"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vložiť obrázok v režime obrázka"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončiť"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Pokračovať"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sl/strings.xml b/v17/leanback/res/values-sl/strings.xml
index 7b16952..7ae1e51 100644
--- a/v17/leanback/res/values-sl/strings.xml
+++ b/v17/leanback/res/values-sl/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Onemogoči visoko kakovost"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Omogoči podnapise"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Onemogoči podnapise"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vklop načina za sliko v sliki"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Dokončaj"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Naprej"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sq-rAL/strings.xml b/v17/leanback/res/values-sq-rAL/strings.xml
index 723b90c..27949ad 100644
--- a/v17/leanback/res/values-sq-rAL/strings.xml
+++ b/v17/leanback/res/values-sq-rAL/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Çaktivizo \"Cilësinë e lartë\""</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivizo titrat"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Çaktivizo titrat me sekuencë kohore"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Fut një fotografi në modalitetin e fotografisë"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Përfundo"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Vazhdo"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sr/strings.xml b/v17/leanback/res/values-sr/strings.xml
index df6c6b1..2097ef8 100644
--- a/v17/leanback/res/values-sr/strings.xml
+++ b/v17/leanback/res/values-sr/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Онемогући висок квалитет"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Омогући титлове"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Онемогући титлове"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Уђи у режим Слика у слици"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Доврши"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Настави"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sv/strings.xml b/v17/leanback/res/values-sv/strings.xml
index 9b874ca..ed08c18 100644
--- a/v17/leanback/res/values-sv/strings.xml
+++ b/v17/leanback/res/values-sv/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Inaktivera hög kvalitet"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Aktivera textning"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Inaktivera textning"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Ange läget Bild-i-bild"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Slutför"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Fortsätt"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-sw/strings.xml b/v17/leanback/res/values-sw/strings.xml
index 53ef95a..d83977c 100644
--- a/v17/leanback/res/values-sw/strings.xml
+++ b/v17/leanback/res/values-sw/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Zima Ubora wa Juu"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Washa manukuu"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Zima manukuu"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Weka Picha Katika Hali ya Picha"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Kamilisha"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Endelea"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ta-rIN/strings.xml b/v17/leanback/res/values-ta-rIN/strings.xml
index 1cc2eea..b3efd12 100644
--- a/v17/leanback/res/values-ta-rIN/strings.xml
+++ b/v17/leanback/res/values-ta-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"உயர் தரத்தை முடக்கு"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"விரிவான வசனங்களை இயக்கு"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"விரிவான வசனங்களை முடக்கு"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"பிக்ச்சர் இன் பிக்ச்சர் பயன்முறைக்குச் செல்"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"முடி"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"தொடர்க"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-te-rIN/strings.xml b/v17/leanback/res/values-te-rIN/strings.xml
index 32d311d..75818db 100644
--- a/v17/leanback/res/values-te-rIN/strings.xml
+++ b/v17/leanback/res/values-te-rIN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"అధిక నాణ్యతను నిలిపివేయి"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"సంవృత శీర్షికలను ప్రారంభించు"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"సంవృత శీర్షికలను నిలిపివేయి"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"చిత్రంలో చిత్రం మోడ్‌లోకి ప్రవేశించండి"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"ముగించు"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"కొనసాగించు"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-th/strings.xml b/v17/leanback/res/values-th/strings.xml
index d3eb2a3..8f94ddf 100644
--- a/v17/leanback/res/values-th/strings.xml
+++ b/v17/leanback/res/values-th/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"ปิดใช้คุณภาพสูง"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"เปิดใช้คำบรรยาย"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"ปิดใช้คำบรรยาย"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"เข้าสู่โหมดการแสดงผลหลายแหล่งพร้อมกัน"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"เสร็จสิ้น"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"ต่อไป"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-tl/strings.xml b/v17/leanback/res/values-tl/strings.xml
index f50b4d1..928465c 100644
--- a/v17/leanback/res/values-tl/strings.xml
+++ b/v17/leanback/res/values-tl/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"I-disable ang Mataas na Kalidad"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"I-enable ang Paglalagay ng Subtitle"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"I-disable ang Paglalagay ng Subtitle"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Pumasok sa Picture In Picture Mode"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tapusin"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Magpatuloy"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-tr/strings.xml b/v17/leanback/res/values-tr/strings.xml
index 814cb29..2fce09c 100644
--- a/v17/leanback/res/values-tr/strings.xml
+++ b/v17/leanback/res/values-tr/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yüksek Kalitede Oynatmayı Devre Dışı Bırak"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Altyazıları Etkinleştir"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Altyazıları Devre Dışı Bırak"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Resim İçinde Resim Moduna Geç"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Son"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Devam"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-uk/strings.xml b/v17/leanback/res/values-uk/strings.xml
index a38db30..a3c9ce9 100644
--- a/v17/leanback/res/values-uk/strings.xml
+++ b/v17/leanback/res/values-uk/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Вимкнути високу якість"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Увімкнути субтитри"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Вимкнути субтитри"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Перейти в режим \"Картинка в картинці\""</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Закінчити"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Продовжити"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"."</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-ur-rPK/strings.xml b/v17/leanback/res/values-ur-rPK/strings.xml
index 666cf71..8020198 100644
--- a/v17/leanback/res/values-ur-rPK/strings.xml
+++ b/v17/leanback/res/values-ur-rPK/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"اعلی معیار کو غیر فعال کریں"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"سب ٹائٹلز کو فعال کریں"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"سب ٹائٹلز کو غیر فعال کریں"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"\'تصویر میں تصویر موڈ\' میں داخل ہوں"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"مکمل کریں"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"جاری رکھیں"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-uz-rUZ/strings.xml b/v17/leanback/res/values-uz-rUZ/strings.xml
index d81d8de..5c3b611 100644
--- a/v17/leanback/res/values-uz-rUZ/strings.xml
+++ b/v17/leanback/res/values-uz-rUZ/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Yuqori sifatni o‘chirib qo‘yish"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Taglavhalarni yoqish"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Taglavhalarni o‘chirib qo‘yish"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Tasvir ichida tasvir rejimiga kirish"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Tugatish"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Davom etish"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-vi/strings.xml b/v17/leanback/res/values-vi/strings.xml
index 881734b..37c6122 100644
--- a/v17/leanback/res/values-vi/strings.xml
+++ b/v17/leanback/res/values-vi/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Tắt chế độ chất lượng cao"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Bật phụ đề"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Tắt phụ đề"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Vào ảnh ở chế độ ảnh"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Hoàn tất"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Tiếp tục"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rCN/strings.xml b/v17/leanback/res/values-zh-rCN/strings.xml
index fc3fa10..0339796 100644
--- a/v17/leanback/res/values-zh-rCN/strings.xml
+++ b/v17/leanback/res/values-zh-rCN/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"关闭高画质模式"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"开启字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"关闭字幕"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"进入画中画模式"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"继续"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rHK/strings.xml b/v17/leanback/res/values-zh-rHK/strings.xml
index 7cba4b5..660aa5d 100644
--- a/v17/leanback/res/values-zh-rHK/strings.xml
+++ b/v17/leanback/res/values-zh-rHK/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"停用高畫質"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"進入「畫中畫模式」"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-zh-rTW/strings.xml b/v17/leanback/res/values-zh-rTW/strings.xml
index dcca2db..a2bd20e 100644
--- a/v17/leanback/res/values-zh-rTW/strings.xml
+++ b/v17/leanback/res/values-zh-rTW/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"停用高品質播放"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"啟用字幕"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"停用字幕"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"進入子母畫面模式"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"完成"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"繼續"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values-zu/strings.xml b/v17/leanback/res/values-zu/strings.xml
index f4c589d..23b62ba 100644
--- a/v17/leanback/res/values-zu/strings.xml
+++ b/v17/leanback/res/values-zu/strings.xml
@@ -46,6 +46,9 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"Khubaza ikhwalithi ephezulu"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"Nika amandla imibhalo engezansi"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"Khubaza imihbalo engezansi"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"Ngena isithombe kumodi yesithombe"</string>
     <string name="lb_guidedaction_finish_title" msgid="4015190340667946245">"Qeda"</string>
     <string name="lb_guidedaction_continue_title" msgid="8842094924543063706">"Qhubeka"</string>
+    <string name="lb_date_separator" msgid="2440386660906697298">"/"</string>
+    <string name="lb_time_separator" msgid="2763247350845477227">":"</string>
 </resources>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index cde7a2d..cc99dde 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -166,6 +166,7 @@
         <attr name="shuffle" format="reference"/>
         <attr name="high_quality" format="reference"/>
         <attr name="closed_captioning" format="reference"/>
+        <attr name="picture_in_picture" format="reference"/>
     </declare-styleable>
 
     <declare-styleable name="lbSlide">
diff --git a/v17/leanback/res/values/ids.xml b/v17/leanback/res/values/ids.xml
index 98ac1fd..ab7f568 100644
--- a/v17/leanback/res/values/ids.xml
+++ b/v17/leanback/res/values/ids.xml
@@ -34,5 +34,6 @@
      <item type="id" name="lb_control_shuffle" />
      <item type="id" name="lb_control_high_quality" />
      <item type="id" name="lb_control_closed_captioning" />
+     <item type="id" name="lb_control_picture_in_picture" />
 
 </resources>
diff --git a/v17/leanback/res/values/strings.xml b/v17/leanback/res/values/strings.xml
index 7ae7a00..bd08e87 100644
--- a/v17/leanback/res/values/strings.xml
+++ b/v17/leanback/res/values/strings.xml
@@ -74,6 +74,8 @@
     <string name="lb_playback_controls_closed_captioning_enable">Enable Closed Captioning</string>
     <!-- Talkback label for the control button to disable closed captioning -->
     <string name="lb_playback_controls_closed_captioning_disable">Disable Closed Captioning</string>
+    <!-- Talkback label for the control button to enter picture in picture mode -->
+    <string name="lb_playback_controls_picture_in_picture">Enter Picture In Picture Mode</string>
 
     <!-- Title of standard Finish action for GuidedStepFragment -->
     <string name="lb_guidedaction_finish_title">Finish</string>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 6d7c74b..b5f7a08 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -371,6 +371,7 @@
         <item name="shuffle">@drawable/lb_ic_shuffle</item>
         <item name="high_quality">@drawable/lb_ic_hq</item>
         <item name="closed_captioning">@drawable/lb_ic_cc</item>
+        <item name="picture_in_picture">@drawable/lb_ic_pip</item>
     </style>
 
     <!-- Style for the main container view in a GuidanceStylist's default layout. -->
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index 46e91c8..861c9c8 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -295,6 +295,17 @@
         }
     }
 
+    /**
+     * Fades out the playback overlay immediately.
+     */
+    public void fadeOut() {
+        if (!mFadingEnabled) {
+            return;
+        }
+        mHandler.removeMessages(START_FADE_OUT);
+        fade(false);
+    }
+
     private boolean areControlsHidden() {
         return mFadingStatus == IDLE && mBgAlpha == 0;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index 58433ef..a2306a3 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -297,6 +297,17 @@
         }
     }
 
+    /**
+     * Fades out the playback overlay immediately.
+     */
+    public void fadeOut() {
+        if (!mFadingEnabled) {
+            return;
+        }
+        mHandler.removeMessages(START_FADE_OUT);
+        fade(false);
+    }
+
     private boolean areControlsHidden() {
         return mFadingStatus == IDLE && mBgAlpha == 0;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
index 60327f2..b1cf2f9 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -321,6 +321,23 @@
     }
 
     /**
+     * An action displaying an icon for picture-in-picture.
+     */
+    public static class PictureInPictureAction extends Action {
+        /**
+         * Constructor
+         * @param context Context used for loading resources.
+         */
+        public PictureInPictureAction(Context context) {
+            super(R.id.lb_control_picture_in_picture);
+            setIcon(getStyledDrawable(context,
+                    R.styleable.lbPlaybackControlsActionIcons_picture_in_picture));
+            setLabel1(context.getString(R.string.lb_playback_controls_picture_in_picture));
+            addKeyCode(KeyEvent.KEYCODE_WINDOW);
+        }
+    }
+
+    /**
      * An action displaying an icon for "more actions".
      */
     public static class MoreActions extends Action {
diff --git a/v4/Android.mk b/v4/Android.mk
index 5296369..3962b8d 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -210,7 +210,7 @@
 # A helper sub-library that makes direct use of V22 APIs.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4-api22
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 22
 LOCAL_SRC_FILES := $(call all-java-files-under, api22)
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api21
 include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -222,7 +222,7 @@
 # A helper sub-library that makes direct use of V23 APIs.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4-api23
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 23
 LOCAL_SRC_FILES := $(call all-java-files-under, api23)
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api22
 include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -231,6 +231,18 @@
 
 # -----------------------------------------------------------------------
 
+# A helper sub-library that makes direct use of V24 APIs.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v4-api24
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api24)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api23
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+support_module_src_files += $(LOCAL_SRC_FILES)
+
+# -----------------------------------------------------------------------
+
 # Here is the final static library that apps can link against.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4
@@ -238,7 +250,7 @@
 LOCAL_AIDL_INCLUDES := frameworks/support/v4/java
 LOCAL_SRC_FILES := $(call all-java-files-under, java) \
     $(call all-Iaidl-files-under, java)
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api23
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api24
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 support_module_src_files += $(LOCAL_SRC_FILES)
diff --git a/v4/NOTICES.md b/v4/NOTICES.md
new file mode 100644
index 0000000..8665d5d
--- /dev/null
+++ b/v4/NOTICES.md
@@ -0,0 +1,12 @@
+# Change Log
+
+## [23.0.1](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/) (2015-09-24)
+
+**Breakage and deprecation notices:**
+
+- ExploreByTouchHelper
+  - Several public methods that are only meant to be called by app developers (and not internally by
+    the helper itself) became final. Any code that depends on overriding these methods should be
+    moved elsewhere.
+  - The concept of keyboard and accessibility focus have been clarified. As a result, the
+    getFocusedVirtualView() method has been deprecated and will be removed in a subsequent release.
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 19debd9..e98bfaa 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -1765,6 +1765,10 @@
     method public static android.os.AsyncTask<Params, Progress, Result> executeParallel(android.os.AsyncTask<Params, Progress, Result>, Params...);
   }
 
+  public class BuildCompat {
+    method public static boolean isAtLeastN();
+  }
+
   public final class CancellationSignal {
     ctor public CancellationSignal();
     method public void cancel();
@@ -1804,8 +1808,7 @@
 
   public class UserManagerCompat {
     ctor public UserManagerCompat();
-    method public static boolean isUserRunningAndLocked(android.content.Context);
-    method public static boolean isUserRunningAndUnlocked(android.content.Context);
+    method public static boolean isUserUnlocked(android.content.Context);
   }
 
 }
@@ -2121,6 +2124,15 @@
     method public abstract void onActionProviderVisibilityChanged(boolean);
   }
 
+  public final class AsyncLayoutInflater {
+    ctor public AsyncLayoutInflater(android.content.Context);
+    method public void inflate(int, android.view.ViewGroup, android.support.v4.view.AsyncLayoutInflater.OnInflateFinishedListener);
+  }
+
+  public static abstract interface AsyncLayoutInflater.OnInflateFinishedListener {
+    method public abstract void onInflateFinished(android.view.View, int, android.view.ViewGroup);
+  }
+
   public final class GestureDetectorCompat {
     ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
     ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
@@ -3251,17 +3263,26 @@
 
   public abstract class ExploreByTouchHelper extends android.support.v4.view.AccessibilityDelegateCompat {
     ctor public ExploreByTouchHelper(android.view.View);
-    method public boolean dispatchHoverEvent(android.view.MotionEvent);
-    method public int getFocusedVirtualView();
+    method public final boolean clearKeyboardFocusForVirtualView(int);
+    method public final boolean dispatchHoverEvent(android.view.MotionEvent);
+    method public final boolean dispatchKeyEvent(android.view.KeyEvent);
+    method public final int getAccessibilityFocusedVirtualViewId();
+    method public deprecated int getFocusedVirtualView();
+    method public final int getKeyboardFocusedVirtualViewId();
     method protected abstract int getVirtualViewAt(float, float);
     method protected abstract void getVisibleVirtualViews(java.util.List<java.lang.Integer>);
-    method public void invalidateRoot();
-    method public void invalidateVirtualView(int);
+    method public final void invalidateRoot();
+    method public final void invalidateVirtualView(int);
+    method public final void invalidateVirtualView(int, int);
+    method public final void onFocusChanged(boolean, int, android.graphics.Rect);
     method protected abstract boolean onPerformActionForVirtualView(int, int, android.os.Bundle);
-    method protected abstract void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
-    method public void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
+    method protected void onPopulateEventForHost(android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateEventForVirtualView(int, android.view.accessibility.AccessibilityEvent);
+    method protected void onPopulateNodeForHost(android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
     method protected abstract void onPopulateNodeForVirtualView(int, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat);
-    method public boolean sendEventForVirtualView(int, int);
+    method protected void onVirtualViewKeyboardFocusChanged(int, boolean);
+    method public final boolean requestKeyboardFocusForVirtualView(int);
+    method public final boolean sendEventForVirtualView(int, int);
     field public static final int HOST_ID = -1; // 0xffffffff
     field public static final int INVALID_ID = -2147483648; // 0x80000000
   }
diff --git a/v4/api/removed.txt b/v4/api/removed.txt
index e69de29..217aba3 100644
--- a/v4/api/removed.txt
+++ b/v4/api/removed.txt
@@ -0,0 +1,9 @@
+package android.support.v4.os {
+
+  public class UserManagerCompat {
+    method public static deprecated boolean isUserRunningAndLocked(android.content.Context);
+    method public static deprecated boolean isUserRunningAndUnlocked(android.content.Context);
+  }
+
+}
+
diff --git a/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java b/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
index ddcff2e..37c4b97 100644
--- a/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/v4/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -92,6 +92,36 @@
         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,
@@ -104,35 +134,42 @@
      *  capturing the final state of the Transition.</p>
      */
     public static void addTransitionTargets(Object enterTransitionObject,
-            Object sharedElementTransitionObject, final View container,
+            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 Map<String, View> namedViews,
-            final Map<String, View> renamedViews, final ArrayList<View> sharedElementTargets) {
+            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) {
-            final Transition enterTransition = (Transition) enterTransitionObject;
             if (enterTransition != null) {
                 enterTransition.addTarget(nonExistentView);
             }
             if (sharedElementTransitionObject != null) {
-                setSharedElementTargets(sharedElementTransitionObject, nonExistentView,
+                setSharedElementTargets(sharedElementTransition, nonExistentView,
                         namedViews, sharedElementTargets);
+                excludeViews(enterTransition, sharedElementTransition, sharedElementTargets, true);
+                excludeViews(exitTransition, sharedElementTransition, sharedElementTargets, true);
             }
 
-            if (inFragment != null) {
-                container.getViewTreeObserver().addOnPreDrawListener(
-                        new ViewTreeObserver.OnPreDrawListener() {
-                            public boolean onPreDraw() {
-                                container.getViewTreeObserver().removeOnPreDrawListener(this);
-                                if (enterTransition != null) {
-                                    enterTransition.removeTarget(nonExistentView);
-                                }
+            container.getViewTreeObserver().addOnPreDrawListener(
+                    new ViewTreeObserver.OnPreDrawListener() {
+                        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()) {
+                                        for (Map.Entry<String, String> entry : nameOverrides
+                                                .entrySet()) {
                                             String to = entry.getValue();
                                             View view = renamedViews.get(to);
                                             if (view != null) {
@@ -148,10 +185,12 @@
                                         addTargets(enterTransition, enteringViews);
                                     }
                                 }
-                                return true;
                             }
-                        });
-            }
+                            excludeViews(exitTransition, enterTransition, enteringViews, true);
+
+                            return true;
+                        }
+                    });
             setSharedElementEpicenter(enterTransition, epicenterView);
         }
     }
@@ -355,9 +394,15 @@
                     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);
diff --git a/v4/api24/android/support/v4/app/NotificationCompatApi24.java b/v4/api24/android/support/v4/app/NotificationCompatApi24.java
new file mode 100644
index 0000000..464f399
--- /dev/null
+++ b/v4/api24/android/support/v4/app/NotificationCompatApi24.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+
+class NotificationCompatApi24 {
+
+    public static class Builder implements NotificationBuilderWithBuilderAccessor,
+            NotificationBuilderWithActions {
+        private Notification.Builder b;
+
+        public Builder(Context context, Notification n,
+                CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
+                RemoteViews tickerView, int number,
+                PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
+                int progressMax, int progress, boolean progressIndeterminate, boolean showWhen,
+                boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
+                String category, ArrayList<String> people, Bundle extras, int color,
+                int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
+                String sortKey, NotificationCompatBase.Topic topic) {
+            b = new Notification.Builder(context)
+                    .setWhen(n.when)
+                    .setShowWhen(showWhen)
+                    .setSmallIcon(n.icon, n.iconLevel)
+                    .setContent(n.contentView)
+                    .setTicker(n.tickerText, tickerView)
+                    .setSound(n.sound, n.audioStreamType)
+                    .setVibrate(n.vibrate)
+                    .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+                    .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                    .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+                    .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+                    .setDefaults(n.defaults)
+                    .setContentTitle(contentTitle)
+                    .setContentText(contentText)
+                    .setSubText(subText)
+                    .setContentInfo(contentInfo)
+                    .setContentIntent(contentIntent)
+                    .setDeleteIntent(n.deleteIntent)
+                    .setFullScreenIntent(fullScreenIntent,
+                            (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+                    .setLargeIcon(largeIcon)
+                    .setNumber(number)
+                    .setUsesChronometer(useChronometer)
+                    .setPriority(priority)
+                    .setProgress(progressMax, progress, progressIndeterminate)
+                    .setLocalOnly(localOnly)
+                    .setExtras(extras)
+                    .setGroup(groupKey)
+                    .setGroupSummary(groupSummary)
+                    .setSortKey(sortKey)
+                    .setCategory(category)
+                    .setColor(color)
+                    .setVisibility(visibility)
+                    .setPublicVersion(publicVersion)
+                    .setTopic(new Notification.Topic(topic.getId(), topic.getLabel()));
+            for (String person: people) {
+                b.addPerson(person);
+            }
+        }
+
+        @Override
+        public void addAction(NotificationCompatBase.Action action) {
+            NotificationCompatApi20.addAction(b, action);
+        }
+
+        @Override
+        public Notification.Builder getBuilder() {
+            return b;
+        }
+
+        @Override
+        public Notification build() {
+            return b.build();
+        }
+    }
+
+    public static NotificationCompatBase.Topic getTopic(Notification notif,
+            NotificationCompatBase.Topic.Factory factory) {
+        Notification.Topic topic = notif.getTopic();
+        return factory.build(topic.getId(), topic.getLabel());
+    }
+}
diff --git a/v4/api24/android/support/v4/content/ContextCompatApi24.java b/v4/api24/android/support/v4/content/ContextCompatApi24.java
new file mode 100644
index 0000000..cc48e67
--- /dev/null
+++ b/v4/api24/android/support/v4/content/ContextCompatApi24.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.content;
+
+import android.content.Context;
+
+/** {@hide} */
+public class ContextCompatApi24 {
+    public static Context createDeviceEncryptedStorageContext(Context context) {
+        return context.createDeviceEncryptedStorageContext();
+    }
+
+    public static boolean isDeviceEncryptedStorage(Context context) {
+        return context.isDeviceEncryptedStorage();
+    }
+}
diff --git a/v4/api24/android/support/v4/net/TrafficStatsCompatApi24.java b/v4/api24/android/support/v4/net/TrafficStatsCompatApi24.java
new file mode 100644
index 0000000..f525dea
--- /dev/null
+++ b/v4/api24/android/support/v4/net/TrafficStatsCompatApi24.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.net;
+
+import android.net.TrafficStats;
+
+import java.net.DatagramSocket;
+import java.net.SocketException;
+
+/** {@hide} */
+public class TrafficStatsCompatApi24 {
+    public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
+        TrafficStats.tagDatagramSocket(socket);
+    }
+
+    public static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
+        TrafficStats.untagDatagramSocket(socket);
+    }
+}
diff --git a/v4/api24/android/support/v4/os/UserManagerCompatApi24.java b/v4/api24/android/support/v4/os/UserManagerCompatApi24.java
new file mode 100644
index 0000000..7067e01
--- /dev/null
+++ b/v4/api24/android/support/v4/os/UserManagerCompatApi24.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.os;
+
+import android.content.Context;
+import android.os.UserManager;
+
+/** {@hide} */
+public class UserManagerCompatApi24 {
+    public static boolean isUserUnlocked(Context context) {
+        return context.getSystemService(UserManager.class).isUserUnlocked();
+    }
+}
diff --git a/v4/build.gradle b/v4/build.gradle
index 1bb53ca..b60e587 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -30,8 +30,8 @@
 def api20SS        = createApiSourceset('api20',        'api20',         '20',      kitkatSS)
 def api21SS        = createApiSourceset('api21',        'api21',         '21',      api20SS)
 def api22SS        = createApiSourceset('api22',        'api22',         '22',      api21SS)
-def api23SS        = createApiSourceset('api23',        'api23',         'current', api22SS)
-
+def api23SS        = createApiSourceset('api23',        'api23',         '23',      api22SS)
+def api24SS        = createApiSourceset('api24',        'api24',         'current', api23SS)
 
 def createApiSourceset(String name, String folder, String apiLevel, SourceSet previousSource) {
     def sourceSet = sourceSets.create(name)
@@ -74,6 +74,9 @@
     testCompile 'junit:junit:4.12'
 }
 
+sourceCompatibility = JavaVersion.VERSION_1_7
+targetCompatibility = JavaVersion.VERSION_1_7
+
 android {
     compileSdkVersion 4
 
diff --git a/v4/java/android/support/v4/app/ActivityCompat.java b/v4/java/android/support/v4/app/ActivityCompat.java
index 0926d49..4e699d4 100644
--- a/v4/java/android/support/v4/app/ActivityCompat.java
+++ b/v4/java/android/support/v4/app/ActivityCompat.java
@@ -300,6 +300,12 @@
      * When checking whether you have a permission you should use {@link
      * #checkSelfPermission(android.content.Context, String)}.
      * </p>
+     * <p>
+     * Calling this API for permissions already granted to your app would show UI
+     * to the user to decided whether the app can still hold these permissions. This
+     * can be useful if the way your app uses the data guarded by the permissions
+     * changes significantly.
+     * </p>
      *
      * @param activity The target activity.
      * @param permissions The requested permissions.
diff --git a/v4/java/android/support/v4/app/BackStackRecord.java b/v4/java/android/support/v4/app/BackStackRecord.java
index 9117bd3..f08190f 100644
--- a/v4/java/android/support/v4/app/BackStackRecord.java
+++ b/v4/java/android/support/v4/app/BackStackRecord.java
@@ -1187,7 +1187,8 @@
                     callback.onSharedElementStart(names, views, null);
                 }
                 prepareSharedElementTransition(state, sceneRoot, sharedElementTransition,
-                        inFragment, outFragment, isBack, sharedElementTargets);
+                        inFragment, outFragment, isBack, sharedElementTargets, enterTransition,
+                        exitTransition);
             }
         }
         if (enterTransition == null && sharedElementTransition == null &&
@@ -1234,9 +1235,9 @@
 
         if (transition != null) {
             FragmentTransitionCompat21.addTransitionTargets(enterTransition,
-                    sharedElementTransition, sceneRoot, viewRetriever, state.nonExistentView,
-                    state.enteringEpicenterView, state.nameOverrides, enteringViews,
-                    namedViews, renamedViews, sharedElementTargets);
+                    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
@@ -1258,16 +1259,22 @@
     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) {
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+            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);
 
-                if (sharedElementTransition != null) {
+                    // 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(
@@ -1279,11 +1286,14 @@
 
                     callSharedElementEnd(state, inFragment, outFragment, isBack,
                             namedViews);
-                }
 
-                return true;
-            }
-        });
+                    // Exclude the shared elements from the entering fragment.
+                    FragmentTransitionCompat21.excludeSharedElementViews(enterTransition,
+                            exitTransition, sharedElementTransition, sharedElementTargets, true);
+                    return true;
+                }
+            });
+        }
     }
 
     private void callSharedElementEnd(TransitionState state, Fragment inFragment,
diff --git a/v4/java/android/support/v4/app/Fragment.java b/v4/java/android/support/v4/app/Fragment.java
index af99b1c..156f607 100644
--- a/v4/java/android/support/v4/app/Fragment.java
+++ b/v4/java/android/support/v4/app/Fragment.java
@@ -59,6 +59,7 @@
     final boolean mRetainInstance;
     final boolean mDetached;
     final Bundle mArguments;
+    final boolean mHidden;
     
     Bundle mSavedFragmentState;
     
@@ -74,6 +75,7 @@
         mRetainInstance = frag.mRetainInstance;
         mDetached = frag.mDetached;
         mArguments = frag.mArguments;
+        mHidden = frag.mHidden;
     }
     
     public FragmentState(Parcel in) {
@@ -86,6 +88,7 @@
         mRetainInstance = in.readInt() != 0;
         mDetached = in.readInt() != 0;
         mArguments = in.readBundle();
+        mHidden = in.readInt() != 0;
         mSavedFragmentState = in.readBundle();
     }
 
@@ -113,6 +116,7 @@
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
         mInstance.mDetached = mDetached;
+        mInstance.mHidden = mHidden;
         mInstance.mFragmentManager = host.mFragmentManager;
 
         if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
@@ -135,6 +139,7 @@
         dest.writeInt(mRetainInstance ? 1 : 0);
         dest.writeInt(mDetached ? 1 : 0);
         dest.writeBundle(mArguments);
+        dest.writeInt(mHidden? 1 : 0);
         dest.writeBundle(mSavedFragmentState);
     }
     
@@ -994,6 +999,12 @@
      * android.content.Context#checkSelfPermission(String)}.
      * </p>
      * <p>
+     * Calling this API for permissions already granted to your app would show UI
+     * to the user to decided whether the app can still hold these permissions. This
+     * can be useful if the way your app uses the data guarded by the permissions
+     * changes significantly.
+     * </p>
+     * <p>
      * A sample permissions request looks like this:
      * </p>
      * <code><pre><p>
@@ -1193,12 +1204,26 @@
      * on things like the activity's content view hierarchy being initialized
      * at this point.  If you want to do work once the activity itself is
      * created, see {@link #onActivityCreated(Bundle)}.
+     *
+     * <p>Any restored child fragments will be created before the base
+     * <code>Fragment.onCreate</code> method returns.</p>
      * 
      * @param savedInstanceState If the fragment is being re-created from
      * a previous saved state, this is the state.
      */
     public void onCreate(@Nullable Bundle savedInstanceState) {
         mCalled = true;
+        if (savedInstanceState != null) {
+            Parcelable p = savedInstanceState.getParcelable(
+                    FragmentActivity.FRAGMENTS_TAG);
+            if (p != null) {
+                if (mChildFragmentManager == null) {
+                    instantiateChildFragmentManager();
+                }
+                mChildFragmentManager.restoreAllState(p, null);
+                mChildFragmentManager.dispatchCreate();
+            }
+        }
     }
 
     /**
@@ -1953,17 +1978,6 @@
             throw new SuperNotCalledException("Fragment " + this
                     + " did not call through to super.onCreate()");
         }
-        if (savedInstanceState != null) {
-            Parcelable p = savedInstanceState.getParcelable(
-                    FragmentActivity.FRAGMENTS_TAG);
-            if (p != null) {
-                if (mChildFragmentManager == null) {
-                    instantiateChildFragmentManager();
-                }
-                mChildFragmentManager.restoreAllState(p, null);
-                mChildFragmentManager.dispatchCreate();
-            }
-        }
     }
 
     View performCreateView(LayoutInflater inflater, ViewGroup container,
diff --git a/v4/java/android/support/v4/app/FragmentTransaction.java b/v4/java/android/support/v4/app/FragmentTransaction.java
index 48f2088..b128efc 100644
--- a/v4/java/android/support/v4/app/FragmentTransaction.java
+++ b/v4/java/android/support/v4/app/FragmentTransaction.java
@@ -162,7 +162,7 @@
     public static final int TRANSIT_EXIT_MASK = 0x2000;
 
     /** @hide */
-    @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE})
+    @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
     @Retention(RetentionPolicy.SOURCE)
     private @interface Transit {}
 
@@ -212,7 +212,7 @@
     /**
      * Select a standard transition animation for this transaction.  May be
      * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
-     * or {@link #TRANSIT_FRAGMENT_CLOSE}
+     * {@link #TRANSIT_FRAGMENT_CLOSE}, or {@link #TRANSIT_FRAGMENT_FADE}.
      */
     public abstract FragmentTransaction setTransition(@Transit int transit);
 
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
index 3510daf..3e73b4e 100644
--- a/v4/java/android/support/v4/app/NotificationCompat.java
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -793,6 +793,27 @@
         }
     }
 
+    static class NotificationCompatImplApi24 extends NotificationCompatImplApi21 {
+        @Override
+        public Notification build(Builder b, BuilderExtender extender) {
+            NotificationCompatApi24.Builder builder = new NotificationCompatApi24.Builder(
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
+                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
+                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate, b.mShowWhen,
+                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
+                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
+                    b.mGroupKey, b.mGroupSummary, b.mSortKey, b.mTopic);
+            addActionsToBuilder(builder, b.mActions);
+            addStyleToBuilderJellybean(builder, b.mStyle);
+            return extender.build(b, builder);
+        }
+
+        @Override
+        public NotificationCompatBase.Topic getTopic(Notification notification) {
+            return NotificationCompatApi24.getTopic(notification, Topic.FACTORY);
+        }
+    }
+
     private static void addActionsToBuilder(NotificationBuilderWithActions builder,
             ArrayList<Action> actions) {
         for (Action action : actions) {
@@ -831,7 +852,9 @@
     }
 
     static {
-        if (Build.VERSION.SDK_INT >= 21) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            IMPL = new NotificationCompatImplApi24();
+        } else if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new NotificationCompatImplApi21();
         } else if (Build.VERSION.SDK_INT >= 20) {
             IMPL = new NotificationCompatImplApi20();
diff --git a/v4/java/android/support/v4/content/ContextCompat.java b/v4/java/android/support/v4/content/ContextCompat.java
index 9ebd2f9..3daaa42 100644
--- a/v4/java/android/support/v4/content/ContextCompat.java
+++ b/v4/java/android/support/v4/content/ContextCompat.java
@@ -26,6 +26,7 @@
 import android.os.Environment;
 import android.os.Process;
 import android.support.annotation.NonNull;
+import android.support.v4.os.BuildCompat;
 import android.support.v4.os.EnvironmentCompat;
 import android.util.Log;
 
@@ -463,6 +464,11 @@
      * Because device-encrypted data is available before user authentication,
      * you should carefully consider what data you store using this Context.
      * <p>
+     * If the underlying device does not have the ability to store
+     * device-encrypted and credential-encrypted data using different keys, then
+     * both storage areas will become available at the same time. They remain
+     * two distinct storage areas, and only the window of availability changes.
+     * <p>
      * Each call to this method returns a new instance of a Context object;
      * Context objects are not shared, however common state (ClassLoader, other
      * Resources for the same configuration) may be so the Context itself can be
@@ -473,7 +479,11 @@
      * @see ContextCompat#isDeviceEncryptedStorage(Context)
      */
     public static Context createDeviceEncryptedStorageContext(Context context) {
-        return null;
+        if (BuildCompat.isAtLeastN()) {
+            return ContextCompatApi24.createDeviceEncryptedStorageContext(context);
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -483,6 +493,10 @@
      * @see ContextCompat#createDeviceEncryptedStorageContext(Context)
      */
     public static boolean isDeviceEncryptedStorage(Context context) {
-        return false;
+        if (BuildCompat.isAtLeastN()) {
+            return ContextCompatApi24.isDeviceEncryptedStorage(context);
+        } else {
+            return false;
+        }
     }
 }
diff --git a/v4/java/android/support/v4/content/FileProvider.java b/v4/java/android/support/v4/content/FileProvider.java
index 515ce1c..9d6d803 100644
--- a/v4/java/android/support/v4/content/FileProvider.java
+++ b/v4/java/android/support/v4/content/FileProvider.java
@@ -365,7 +365,7 @@
      *
      * @param context A {@link Context} for the current component.
      * @param authority The authority of a {@link FileProvider} defined in a
-     *            {@code &lt;provider&gt;} element in your app's manifest.
+     *            {@code <provider>} element in your app's manifest.
      * @param file A {@link File} pointing to the filename for which you want a
      * <code>content</code> {@link Uri}.
      * @return A content URI for the file.
@@ -547,7 +547,7 @@
 
     /**
      * Parse and return {@link PathStrategy} for given authority as defined in
-     * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code &lt;meta-data>}.
+     * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.
      *
      * @see #getPathStrategy(Context, String)
      */
diff --git a/v4/java/android/support/v4/content/res/TypedArrayUtils.java b/v4/java/android/support/v4/content/res/TypedArrayUtils.java
index 79e4ac8..0cc5885 100644
--- a/v4/java/android/support/v4/content/res/TypedArrayUtils.java
+++ b/v4/java/android/support/v4/content/res/TypedArrayUtils.java
@@ -15,10 +15,12 @@
  */
 package android.support.v4.content.res;
 
+import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.AnyRes;
 import android.support.annotation.StyleableRes;
+import android.util.TypedValue;
 
 /**
  * Compat methods for accessing TypedArray values.
@@ -70,4 +72,13 @@
         }
         return val;
     }
+
+    public static int getAttr(Context context, int attr, int fallbackAttr) {
+        TypedValue value = new TypedValue();
+        context.getTheme().resolveAttribute(attr, value, true);
+        if (value.resourceId != 0) {
+            return attr;
+        }
+        return fallbackAttr;
+    }
 }
diff --git a/v4/java/android/support/v4/net/TrafficStatsCompat.java b/v4/java/android/support/v4/net/TrafficStatsCompat.java
index 05acb79..57b8a7e 100644
--- a/v4/java/android/support/v4/net/TrafficStatsCompat.java
+++ b/v4/java/android/support/v4/net/TrafficStatsCompat.java
@@ -139,10 +139,24 @@
         }
     }
 
+    static class Api24TrafficStatsCompatImpl extends IcsTrafficStatsCompatImpl {
+        @Override
+        public void tagDatagramSocket(DatagramSocket socket) throws SocketException {
+            TrafficStatsCompatApi24.tagDatagramSocket(socket);
+        }
+
+        @Override
+        public void untagDatagramSocket(DatagramSocket socket) throws SocketException {
+            TrafficStatsCompatApi24.untagDatagramSocket(socket);
+        }
+    }
+
     private static final TrafficStatsCompatImpl IMPL;
 
     static {
-        if (Build.VERSION.SDK_INT >= 14) {
+        if ("N".equals(Build.VERSION.CODENAME)) {
+            IMPL = new Api24TrafficStatsCompatImpl();
+        } else if (Build.VERSION.SDK_INT >= 14) {
             IMPL = new IcsTrafficStatsCompatImpl();
         } else {
             IMPL = new BaseTrafficStatsCompatImpl();
diff --git a/v4/java/android/support/v4/os/BuildCompat.java b/v4/java/android/support/v4/os/BuildCompat.java
new file mode 100644
index 0000000..513e153
--- /dev/null
+++ b/v4/java/android/support/v4/os/BuildCompat.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.support.v4.os;
+
+import android.os.Build.VERSION;
+
+/**
+ * BuildCompat contains additional platform version checking methods for
+ * testing compatibility with new features.
+ */
+public class BuildCompat {
+    private BuildCompat() {
+    }
+
+    /**
+     * Check if the device is running on the Android N release or newer.
+     * This method is suitable for use with preview SDKs and associated
+     * prerelease device builds.
+     *
+     * @return true if N APIs are available for use
+     */
+    public static boolean isAtLeastN() {
+        return "N".equals(VERSION.CODENAME);
+    }
+}
diff --git a/v4/java/android/support/v4/os/UserManagerCompat.java b/v4/java/android/support/v4/os/UserManagerCompat.java
index 74009c0..436bfc8 100644
--- a/v4/java/android/support/v4/os/UserManagerCompat.java
+++ b/v4/java/android/support/v4/os/UserManagerCompat.java
@@ -28,9 +28,25 @@
      * unlocked only after they've entered their credentials (such as a lock
      * pattern or PIN), and credential-encrypted private app data storage is
      * available.
+     *
+     * @removed
      */
+    @Deprecated
     public static boolean isUserRunningAndLocked(Context context) {
-        return false;
+        return !isUserUnlocked(context);
+    }
+
+    /**
+     * Return whether the calling user is running in an "unlocked" state. A user
+     * is unlocked only after they've entered their credentials (such as a lock
+     * pattern or PIN), and credential-encrypted private app data storage is
+     * available.
+     *
+     * @removed
+     */
+    @Deprecated
+    public static boolean isUserRunningAndUnlocked(Context context) {
+        return isUserUnlocked(context);
     }
 
     /**
@@ -39,7 +55,11 @@
      * pattern or PIN), and credential-encrypted private app data storage is
      * available.
      */
-    public static boolean isUserRunningAndUnlocked(Context context) {
-        return true;
+    public static boolean isUserUnlocked(Context context) {
+        if (BuildCompat.isAtLeastN()) {
+            return UserManagerCompatApi24.isUserUnlocked(context);
+        } else {
+            return true;
+        }
     }
 }
diff --git a/v4/java/android/support/v4/util/SimpleArrayMap.java b/v4/java/android/support/v4/util/SimpleArrayMap.java
index 1b1577a..c7d4b5d 100644
--- a/v4/java/android/support/v4/util/SimpleArrayMap.java
+++ b/v4/java/android/support/v4/util/SimpleArrayMap.java
@@ -522,17 +522,42 @@
     /**
      * {@inheritDoc}
      *
-     * <p>This implementation returns false if the object is not a map, or
-     * if the maps have different sizes. Otherwise, for each key in this map,
-     * values of both maps are compared. If the values for any key are not
-     * equal, the method returns false, otherwise it returns true.
+     * <p>This implementation returns false if the object is not a Map or
+     * SimpleArrayMap, or if the maps have different sizes. Otherwise, for each
+     * key in this map, values of both maps are compared. If the values for any
+     * key are not equal, the method returns false, otherwise it returns true.
      */
     @Override
     public boolean equals(Object object) {
         if (this == object) {
             return true;
         }
-        if (object instanceof Map) {
+        if (object instanceof SimpleArrayMap) {
+            SimpleArrayMap<?, ?> map = (SimpleArrayMap<?, ?>) object;
+            if (size() != map.size()) {
+                return false;
+            }
+
+            try {
+                for (int i=0; i<mSize; i++) {
+                    K key = keyAt(i);
+                    V mine = valueAt(i);
+                    Object theirs = map.get(key);
+                    if (mine == null) {
+                        if (theirs != null || !map.containsKey(key)) {
+                            return false;
+                        }
+                    } else if (!mine.equals(theirs)) {
+                        return false;
+                    }
+                }
+            } catch (NullPointerException ignored) {
+                return false;
+            } catch (ClassCastException ignored) {
+                return false;
+            }
+            return true;
+        } else if (object instanceof Map) {
             Map<?, ?> map = (Map<?, ?>) object;
             if (size() != map.size()) {
                 return false;
diff --git a/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java b/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
index 0ea755e..4d0281e 100644
--- a/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
+++ b/v4/java/android/support/v4/view/AccessibilityDelegateCompat.java
@@ -27,6 +27,18 @@
 /**
  * Helper for accessing {@link View.AccessibilityDelegate} introduced after
  * API level 4 in a backwards compatible fashion.
+ * <p>
+ * <strong>Note:</strong> On platform versions prior to
+ * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+ * views in the {@code android.widget.*} package are called <i>before</i>
+ * host methods. This prevents certain properties such as class name from
+ * being modified by overriding
+ * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
+ * as any changes will be overwritten by the host class.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+ * methods are called <i>after</i> host methods, which all properties to be
+ * modified without being overwritten by the host class.
  */
 public class AccessibilityDelegateCompat {
 
diff --git a/v4/java/android/support/v4/view/AsyncLayoutInflater.java b/v4/java/android/support/v4/view/AsyncLayoutInflater.java
new file mode 100644
index 0000000..6e88f59
--- /dev/null
+++ b/v4/java/android/support/v4/view/AsyncLayoutInflater.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.view;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.util.Pools.SynchronizedPool;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * <p>Helper class for inflating layouts asynchronously. To use, construct
+ * an instance of {@link AsyncLayoutInflater} on the UI thread and call
+ * {@link #inflate(int, ViewGroup, OnInflateFinishedListener)}. The
+ * {@link OnInflateFinishedListener} will be invoked on the UI thread
+ * when the inflate request has completed.
+ *
+ * <p>This is intended for parts of the UI that are created lazily or in
+ * response to user interactions. This allows the UI thread to continue
+ * to be responsive & animate while the relatively heavy inflate
+ * is being performed.
+ *
+ * <p>For a layout to be inflated asynchronously it needs to have a parent
+ * whose {@link ViewGroup#generateLayoutParams(AttributeSet)} is thread-safe
+ * and all the Views being constructed as part of inflation must not create
+ * any {@link Handler}s or otherwise call {@link Looper#myLooper()}. If the
+ * layout that is trying to be inflated cannot be constructed
+ * asynchronously for whatever reason, {@link AsyncLayoutInflater} will
+ * automatically fall back to inflating on the UI thread.
+ *
+ * <p>NOTE that the inflated View hierarchy is NOT added to the parent. It is
+ * equivalent to calling {@link LayoutInflater#inflate(int, ViewGroup, boolean)}
+ * with attachToRoot set to false. Callers will likely want to call
+ * {@link ViewGroup#addView(View)} in the {@link OnInflateFinishedListener}
+ * callback at a minimum.
+ *
+ * <p>This inflater does not support setting a {@link LayoutInflater.Factory}
+ * nor {@link LayoutInflater.Factory2}. Similarly it does not support inflating
+ * layouts that contain fragments.
+ */
+public final class AsyncLayoutInflater {
+    private static final String TAG = "AsyncLayoutInflater";
+
+    private LayoutInflater mInflater;
+    private Handler mHandler;
+    private InflateThread mInflateThread;
+
+    public AsyncLayoutInflater(@NonNull Context context) {
+        mInflater = new BasicInflater(context);
+        mHandler = new Handler(mHandlerCallback);
+        mInflateThread = InflateThread.getInstance();
+    }
+
+    @UiThread
+    public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
+            @NonNull OnInflateFinishedListener callback) {
+        if (callback == null) {
+            throw new NullPointerException("callback argument may not be null!");
+        }
+        InflateRequest request = mInflateThread.obtainRequest();
+        request.inflater = this;
+        request.resid = resid;
+        request.parent = parent;
+        request.callback = callback;
+        mInflateThread.enqueue(request);
+    }
+
+    private Callback mHandlerCallback = new Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            InflateRequest request = (InflateRequest) msg.obj;
+            if (request.view == null) {
+                request.view = mInflater.inflate(
+                        request.resid, request.parent, false);
+            }
+            request.callback.onInflateFinished(
+                    request.view, request.resid, request.parent);
+            mInflateThread.releaseRequest(request);
+            return true;
+        }
+    };
+
+    public interface OnInflateFinishedListener {
+        public void onInflateFinished(View view, int resid, ViewGroup parent);
+    }
+
+    private static class InflateRequest {
+        AsyncLayoutInflater inflater;
+        ViewGroup parent;
+        int resid;
+        View view;
+        OnInflateFinishedListener callback;
+    }
+
+    private static class BasicInflater extends LayoutInflater {
+        private static final String[] sClassPrefixList = {
+            "android.widget.",
+            "android.webkit.",
+            "android.app."
+        };
+
+        public BasicInflater(Context context) {
+            super(context);
+        }
+
+        @Override
+        public LayoutInflater cloneInContext(Context newContext) {
+            return new BasicInflater(newContext);
+        }
+
+        @Override
+        protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+            for (String prefix : sClassPrefixList) {
+                try {
+                    View view = createView(name, prefix, attrs);
+                    if (view != null) {
+                        return view;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // In this case we want to let the base class take a crack
+                    // at it.
+                }
+            }
+
+            return super.onCreateView(name, attrs);
+        }
+    }
+
+    private static class InflateThread extends Thread {
+        private static final InflateThread sInstance;
+        static {
+            sInstance = new InflateThread();
+            sInstance.start();
+        }
+
+        public static InflateThread getInstance() {
+            return sInstance;
+        }
+
+        private ArrayBlockingQueue<InflateRequest> mQueue
+                = new ArrayBlockingQueue<>(10);
+        private SynchronizedPool<InflateRequest> mRequestPool
+                = new SynchronizedPool<>(10);
+
+        @Override
+        public void run() {
+            while (true) {
+                InflateRequest request;
+                try {
+                    request = mQueue.take();
+                } catch (InterruptedException ex) {
+                    // Odd, just continue
+                    Log.w(TAG, ex);
+                    continue;
+                }
+
+                try {
+                    request.view = request.inflater.mInflater.inflate(
+                            request.resid, request.parent, false);
+                } catch (RuntimeException ex) {
+                    // Probably a Looper failure, retry on the UI thread
+                    Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI thread",
+                            ex);
+                }
+                Message.obtain(request.inflater.mHandler, 0, request)
+                        .sendToTarget();
+            }
+        }
+
+        public InflateRequest obtainRequest() {
+            InflateRequest obj = mRequestPool.acquire();
+            if (obj == null) {
+                obj = new InflateRequest();
+            }
+            return obj;
+        }
+
+        public void releaseRequest(InflateRequest obj) {
+            obj.callback = null;
+            obj.inflater = null;
+            obj.parent = null;
+            obj.resid = 0;
+            obj.view = null;
+            mRequestPool.release(obj);
+        }
+
+        public void enqueue(InflateRequest request) {
+            try {
+                mQueue.put(request);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(
+                        "Failed to enqueue async inflate request", e);
+            }
+        }
+    }
+}
diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java
index 330c84a..dc859ed 100644
--- a/v4/java/android/support/v4/view/ViewCompat.java
+++ b/v4/java/android/support/v4/view/ViewCompat.java
@@ -53,6 +53,21 @@
 public final class ViewCompat {
     private static final String TAG = "ViewCompat";
 
+    /** @hide */
+    @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN,
+            View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FocusDirection {}
+
+    /** @hide */
+    @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FocusRealDirection {}
+
+    /** @hide */
+    @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FocusRelativeDirection {}
 
     /** @hide */
     @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER})
@@ -293,36 +308,36 @@
     /**
      * Scroll indicator direction for the top edge of the view.
      *
-     * @see #setScrollIndicators(int)
-     * @see #setScrollIndicators(int, int)
-     * @see #getScrollIndicators()
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
      */
     public static final int SCROLL_INDICATOR_TOP = 0x1;
 
     /**
      * Scroll indicator direction for the bottom edge of the view.
      *
-     * @see #setScrollIndicators(int)
-     * @see #setScrollIndicators(int, int)
-     * @see #getScrollIndicators()
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
      */
     public static final int SCROLL_INDICATOR_BOTTOM = 0x2;
 
     /**
      * Scroll indicator direction for the left edge of the view.
      *
-     * @see #setScrollIndicators(int)
-     * @see #setScrollIndicators(int, int)
-     * @see #getScrollIndicators()
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
      */
     public static final int SCROLL_INDICATOR_LEFT = 0x4;
 
     /**
      * Scroll indicator direction for the right edge of the view.
      *
-     * @see #setScrollIndicators(int)
-     * @see #setScrollIndicators(int, int)
-     * @see #getScrollIndicators()
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
      */
     public static final int SCROLL_INDICATOR_RIGHT = 0x8;
 
@@ -338,93 +353,93 @@
     /**
      * Scroll indicator direction for the ending edge of the view.
      *
-     * @see #setScrollIndicators(int)
-     * @see #setScrollIndicators(int, int)
-     * @see #getScrollIndicators()
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
      */
     public static final int SCROLL_INDICATOR_END = 0x20;
 
     interface ViewCompatImpl {
-        public boolean canScrollHorizontally(View v, int direction);
-        public boolean canScrollVertically(View v, int direction);
-        public int getOverScrollMode(View v);
-        public void setOverScrollMode(View v, int mode);
-        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
-        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
-        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
-        public void setAccessibilityDelegate(View v, @Nullable AccessibilityDelegateCompat delegate);
-        public boolean hasAccessibilityDelegate(View v);
-        public boolean hasTransientState(View view);
-        public void setHasTransientState(View view, boolean hasTransientState);
-        public void postInvalidateOnAnimation(View view);
-        public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
-        public void postOnAnimation(View view, Runnable action);
-        public void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
-        public int getImportantForAccessibility(View view);
-        public void setImportantForAccessibility(View view, int mode);
-        public boolean isImportantForAccessibility(View view);
-        public boolean performAccessibilityAction(View view, int action, Bundle arguments);
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
-        public float getAlpha(View view);
-        public void setLayerType(View view, int layerType, Paint paint);
-        public int getLayerType(View view);
-        public int getLabelFor(View view);
-        public void setLabelFor(View view, int id);
-        public void setLayerPaint(View view, Paint paint);
-        public int getLayoutDirection(View view);
-        public void setLayoutDirection(View view, int layoutDirection);
-        public ViewParent getParentForAccessibility(View view);
-        public boolean isOpaque(View view);
-        public int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
-        public int getMeasuredWidthAndState(View view);
-        public int getMeasuredHeightAndState(View view);
-        public int getMeasuredState(View view);
-        public int getAccessibilityLiveRegion(View view);
-        public void setAccessibilityLiveRegion(View view, int mode);
-        public int getPaddingStart(View view);
-        public int getPaddingEnd(View view);
-        public void setPaddingRelative(View view, int start, int top, int end, int bottom);
-        public void dispatchStartTemporaryDetach(View view);
-        public void dispatchFinishTemporaryDetach(View view);
-        public float getX(View view);
-        public float getY(View view);
-        public float getRotation(View view);
-        public float getRotationX(View view);
-        public float getRotationY(View view);
-        public float getScaleX(View view);
-        public float getScaleY(View view);
-        public float getTranslationX(View view);
-        public float getTranslationY(View view);
-        public int getMinimumWidth(View view);
-        public int getMinimumHeight(View view);
-        public ViewPropertyAnimatorCompat animate(View view);
-        public void setRotation(View view, float value);
-        public void setRotationX(View view, float value);
-        public void setRotationY(View view, float value);
-        public void setScaleX(View view, float value);
-        public void setScaleY(View view, float value);
-        public void setTranslationX(View view, float value);
-        public void setTranslationY(View view, float value);
-        public void setX(View view, float value);
-        public void setY(View view, float value);
-        public void setAlpha(View view, float value);
-        public void setPivotX(View view, float value);
-        public void setPivotY(View view, float value);
-        public float getPivotX(View view);
-        public float getPivotY(View view);
-        public void setElevation(View view, float elevation);
-        public float getElevation(View view);
-        public void setTranslationZ(View view, float translationZ);
-        public float getTranslationZ(View view);
-        public void setClipBounds(View view, Rect clipBounds);
-        public Rect getClipBounds(View view);
-        public void setTransitionName(View view, String transitionName);
-        public String getTransitionName(View view);
-        public int getWindowSystemUiVisibility(View view);
-        public void requestApplyInsets(View view);
-        public void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled);
-        public boolean getFitsSystemWindows(View view);
-        public boolean hasOverlappingRendering(View view);
+        boolean canScrollHorizontally(View v, int direction);
+        boolean canScrollVertically(View v, int direction);
+        int getOverScrollMode(View v);
+        void setOverScrollMode(View v, int mode);
+        void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
+        void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
+        void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
+        void setAccessibilityDelegate(View v, @Nullable AccessibilityDelegateCompat delegate);
+        boolean hasAccessibilityDelegate(View v);
+        boolean hasTransientState(View view);
+        void setHasTransientState(View view, boolean hasTransientState);
+        void postInvalidateOnAnimation(View view);
+        void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom);
+        void postOnAnimation(View view, Runnable action);
+        void postOnAnimationDelayed(View view, Runnable action, long delayMillis);
+        int getImportantForAccessibility(View view);
+        void setImportantForAccessibility(View view, int mode);
+        boolean isImportantForAccessibility(View view);
+        boolean performAccessibilityAction(View view, int action, Bundle arguments);
+        AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view);
+        float getAlpha(View view);
+        void setLayerType(View view, int layerType, Paint paint);
+        int getLayerType(View view);
+        int getLabelFor(View view);
+        void setLabelFor(View view, int id);
+        void setLayerPaint(View view, Paint paint);
+        int getLayoutDirection(View view);
+        void setLayoutDirection(View view, int layoutDirection);
+        ViewParent getParentForAccessibility(View view);
+        boolean isOpaque(View view);
+        int resolveSizeAndState(int size, int measureSpec, int childMeasuredState);
+        int getMeasuredWidthAndState(View view);
+        int getMeasuredHeightAndState(View view);
+        int getMeasuredState(View view);
+        int getAccessibilityLiveRegion(View view);
+        void setAccessibilityLiveRegion(View view, int mode);
+        int getPaddingStart(View view);
+        int getPaddingEnd(View view);
+        void setPaddingRelative(View view, int start, int top, int end, int bottom);
+        void dispatchStartTemporaryDetach(View view);
+        void dispatchFinishTemporaryDetach(View view);
+        float getX(View view);
+        float getY(View view);
+        float getRotation(View view);
+        float getRotationX(View view);
+        float getRotationY(View view);
+        float getScaleX(View view);
+        float getScaleY(View view);
+        float getTranslationX(View view);
+        float getTranslationY(View view);
+        int getMinimumWidth(View view);
+        int getMinimumHeight(View view);
+        ViewPropertyAnimatorCompat animate(View view);
+        void setRotation(View view, float value);
+        void setRotationX(View view, float value);
+        void setRotationY(View view, float value);
+        void setScaleX(View view, float value);
+        void setScaleY(View view, float value);
+        void setTranslationX(View view, float value);
+        void setTranslationY(View view, float value);
+        void setX(View view, float value);
+        void setY(View view, float value);
+        void setAlpha(View view, float value);
+        void setPivotX(View view, float value);
+        void setPivotY(View view, float value);
+        float getPivotX(View view);
+        float getPivotY(View view);
+        void setElevation(View view, float elevation);
+        float getElevation(View view);
+        void setTranslationZ(View view, float translationZ);
+        float getTranslationZ(View view);
+        void setClipBounds(View view, Rect clipBounds);
+        Rect getClipBounds(View view);
+        void setTransitionName(View view, String transitionName);
+        String getTransitionName(View view);
+        int getWindowSystemUiVisibility(View view);
+        void requestApplyInsets(View view);
+        void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled);
+        boolean getFitsSystemWindows(View view);
+        boolean hasOverlappingRendering(View view);
         void setFitsSystemWindows(View view, boolean fitSystemWindows);
         void jumpDrawablesToCurrentState(View v);
         void setOnApplyWindowInsetsListener(View view, OnApplyWindowInsetsListener listener);
@@ -450,12 +465,12 @@
         boolean dispatchNestedPreFling(View view, float velocityX, float velocityY);
         boolean isLaidOut(View view);
         int combineMeasuredStates(int curState, int newState);
-        public float getZ(View view);
-        public boolean isAttachedToWindow(View view);
-        public boolean hasOnClickListeners(View view);
-        public void setScrollIndicators(View view, int indicators);
-        public void setScrollIndicators(View view, int indicators, int mask);
-        public int getScrollIndicators(View view);
+        float getZ(View view);
+        boolean isAttachedToWindow(View view);
+        boolean hasOnClickListeners(View view);
+        void setScrollIndicators(View view, int indicators);
+        void setScrollIndicators(View view, int indicators, int mask);
+        int getScrollIndicators(View view);
     }
 
     static class BaseViewCompatImpl implements ViewCompatImpl {
@@ -1297,8 +1312,7 @@
         @Override
         public ViewPropertyAnimatorCompat animate(View view) {
             if (mViewPropertyAnimatorCompatMap == null) {
-                mViewPropertyAnimatorCompatMap =
-                        new WeakHashMap<View, ViewPropertyAnimatorCompat>();
+                mViewPropertyAnimatorCompatMap = new WeakHashMap<>();
             }
             ViewPropertyAnimatorCompat vpa = mViewPropertyAnimatorCompatMap.get(view);
             if (vpa == null) {
@@ -1706,6 +1720,7 @@
      */
     @OverScroll
     public static int getOverScrollMode(View v) {
+        //noinspection ResourceType
         return IMPL.getOverScrollMode(v);
     }
 
@@ -1742,10 +1757,9 @@
      *     event.getText().add(selectedDateUtterance);
      * }</pre>
      * <p>
-     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
-     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
-     * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View,
-     *  AccessibilityEvent)}
+     * If an {@link AccessibilityDelegateCompat} has been specified via calling
+     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
+     * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)}
      * is responsible for handling this call.
      * </p>
      * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
@@ -1775,15 +1789,10 @@
      *     event.setPassword(true);
      * }</pre>
      * <p>
-     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
-     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
-     * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View,
-     *  AccessibilityEvent)}
+     * If an {@link AccessibilityDelegateCompat} has been specified via calling
+     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
+     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)}
      * is responsible for handling this call.
-     * </p>
-     * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
-     * information to the event, in case the default implementation has basic information to add.
-     * </p>
      *
      * @param v The View against which to invoke the method.
      * @param event The event to initialize.
@@ -1796,33 +1805,27 @@
     }
 
     /**
-     * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information
+     * Initializes an {@link AccessibilityNodeInfoCompat} with information
      * about this view. The base implementation sets:
      * <ul>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li>
-     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li>
+     * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li>
      * </ul>
      * <p>
-     * Subclasses should override this method, call the super implementation,
-     * and set additional attributes.
-     * </p>
-     * <p>
-     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
-     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
-     * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View,
-     *  android.view.accessibility.AccessibilityNodeInfo)}
-     * is responsible for handling this call.
-     * </p>
+     * If an {@link AccessibilityDelegateCompat} has been specified via calling
+     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
+     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
+     * method is responsible for handling this call.
      *
      * @param v The View against which to invoke the method.
      * @param info The instance to initialize.
@@ -1832,15 +1835,27 @@
     }
 
     /**
-     * Sets a delegate for implementing accessibility support via compositon as
-     * opposed to inheritance. The delegate's primary use is for implementing
-     * backwards compatible widgets. For more details see
-     * {@link android.view.View.AccessibilityDelegate}.
+     * Sets a delegate for implementing accessibility support via composition
+     * (as opposed to inheritance). For more details, see
+     * {@link AccessibilityDelegateCompat}.
+     * <p>
+     * On platform versions prior to API 14, this method is a no-op.
+     * <p>
+     * <strong>Note:</strong> On platform versions prior to
+     * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+     * views in the {@code android.widget.*} package are called <i>before</i>
+     * host methods. This prevents certain properties such as class name from
+     * being modified by overriding
+     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
+     * as any changes will be overwritten by the host class.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+     * methods are called <i>after</i> host methods, which all properties to be
+     * modified without being overwritten by the host class.
      *
-     * @param v The View against which to invoke the method.
-     * @param delegate The delegate instance.
-     *
-     * @see android.view.View.AccessibilityDelegate
+     * @param delegate the object to which accessibility method calls should be
+     *                 delegated
+     * @see AccessibilityDelegateCompat
      */
     public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
         IMPL.setAccessibilityDelegate(v, delegate);
@@ -1956,6 +1971,7 @@
      */
     @ImportantForAccessibility
     public static int getImportantForAccessibility(View view) {
+        //noinspection ResourceType
         return IMPL.getImportantForAccessibility(view);
     }
 
@@ -2063,13 +2079,6 @@
      * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
      * for more information on when and how to use layers.</p>
      *
-     * @param layerType The ype of layer to use with this view, must be one of
-     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
-     *        {@link #LAYER_TYPE_HARDWARE}
-     * @param paint The paint used to compose the layer. This argument is optional
-     *        and can be null. It is ignored when the layer type is
-     *        {@link #LAYER_TYPE_NONE}
-     *
      * @param view View to set the layer type for
      * @param layerType The type of layer to use with this view, must be one of
      *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
@@ -2100,6 +2109,7 @@
      */
     @LayerType
     public static int getLayerType(View view) {
+        //noinspection ResourceType
         return IMPL.getLayerType(view);
     }
 
@@ -2171,6 +2181,7 @@
      */
     @ResolvedLayoutDirectionMode
     public static int getLayoutDirection(View view) {
+        //noinspection ResourceType
         return IMPL.getLayoutDirection(view);
     }
 
@@ -2297,6 +2308,7 @@
      */
     @AccessibilityLiveRegion
     public static int getAccessibilityLiveRegion(View view) {
+        //noinspection ResourceType
         return IMPL.getAccessibilityLiveRegion(view);
     }
 
diff --git a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
index 64f6634..ddfc5d6 100644
--- a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -19,7 +19,11 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.SparseArrayCompat;
 import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.KeyEventCompat;
 import android.support.v4.view.MotionEventCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.ViewParentCompat;
@@ -28,15 +32,22 @@
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
 
+import android.support.v4.view.ViewCompat.FocusDirection;
+import android.support.v4.view.ViewCompat.FocusRealDirection;
+
+import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static android.support.v4.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES;
+
 /**
  * ExploreByTouchHelper is a utility class for implementing accessibility
  * support in custom {@link View}s that represent a collection of View-like
@@ -47,8 +58,36 @@
  * <p>
  * Clients should override abstract methods on this class and attach it to the
  * host view using {@link ViewCompat#setAccessibilityDelegate}:
- *
+ * <p>
  * <pre>
+ * class MyCustomView extends View {
+ *     private MyVirtualViewHelper mVirtualViewHelper;
+ *
+ *     public MyCustomView(Context context, ...) {
+ *         ...
+ *         mVirtualViewHelper = new MyVirtualViewHelper(this);
+ *         ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper);
+ *     }
+ *
+ *     &#64;Override
+ *     public boolean dispatchHoverEvent(MotionEvent event) {
+ *       return mHelper.dispatchHoverEvent(this, event)
+ *           || super.dispatchHoverEvent(event);
+ *     }
+ *
+ *     &#64;Override
+ *     public boolean dispatchKeyEvent(KeyEvent event) {
+ *       return mHelper.dispatchKeyEvent(event)
+ *           || super.dispatchKeyEvent(event);
+ *     }
+ *
+ *     &#64;Override
+ *     public boolean onFocusChanged(boolean gainFocus, int direction,
+ *         Rect previouslyFocusedRect) {
+ *       super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ *       mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ *     }
+ * }
  * mAccessHelper = new MyExploreByTouchHelper(someView);
  * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
  * </pre>
@@ -61,7 +100,7 @@
     public static final int HOST_ID = View.NO_ID;
 
     /** Default class name used for virtual views. */
-    private static final String DEFAULT_CLASS_NAME = View.class.getName();
+    private static final String DEFAULT_CLASS_NAME = "android.view.View";
 
     // Temporary, reusable data structures.
     private final Rect mTempScreenRect = new Rect();
@@ -69,70 +108,79 @@
     private final Rect mTempVisibleRect = new Rect();
     private final int[] mTempGlobalRect = new int[2];
 
+    /** Cache of accessibility nodes. This is populated on-demand. */
+    private final SparseArrayCompat<AccessibilityNodeInfoCompat> mCachedNodes =
+            new SparseArrayCompat<>();
+
     /** System accessibility manager, used to check state and send events. */
     private final AccessibilityManager mManager;
 
     /** View whose internal structure is exposed through this helper. */
-    private final View mView;
+    private final View mHost;
 
-    /** Node provider that handles creating nodes and performing actions. */
-    private ExploreByTouchNodeProvider mNodeProvider;
+    /** Virtual node provider used to expose logical structure to services. */
+    private MyNodeProvider mNodeProvider;
 
-    /** Virtual view id for the currently focused logical item. */
-    private int mFocusedVirtualViewId = INVALID_ID;
+    /** Identifier for the virtual view that holds accessibility focus. */
+    private int mAccessibilityFocusedVirtualViewId = INVALID_ID;
 
-    /** Virtual view id for the currently hovered logical item. */
+    /** Identifier for the virtual view that holds keyboard focus. */
+    private int mKeyboardFocusedVirtualViewId = INVALID_ID;
+
+    /** Identifier for the virtual view that is currently hovered. */
     private int mHoveredVirtualViewId = INVALID_ID;
 
     /**
-     * Factory method to create a new {@link ExploreByTouchHelper}.
+     * Constructs a new helper that can expose a virtual view hierarchy for the
+     * specified host view.
      *
-     * @param forView View whose logical children are exposed by this helper.
+     * @param host view whose virtual view hierarchy is exposed by this helper
      */
-    public ExploreByTouchHelper(View forView) {
-        if (forView == null) {
+    public ExploreByTouchHelper(View host) {
+        if (host == null) {
             throw new IllegalArgumentException("View may not be null");
         }
 
-        mView = forView;
-        final Context context = forView.getContext();
+        mHost = host;
+
+        final Context context = host.getContext();
         mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+        // Host view must be focusable so that we can delegate to virtual
+        // views.
+        host.setFocusable(true);
+        if (ViewCompat.getImportantForAccessibility(host) == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            ViewCompat.setImportantForAccessibility(host, IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
-    /**
-     * Returns the {@link AccessibilityNodeProviderCompat} for this helper.
-     *
-     * @param host View whose logical children are exposed by this helper.
-     * @return The accessibility node provider for this helper.
-     */
     @Override
     public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
         if (mNodeProvider == null) {
-            mNodeProvider = new ExploreByTouchNodeProvider();
+            mNodeProvider = new MyNodeProvider();
         }
         return mNodeProvider;
     }
 
     /**
+     * Delegates hover events from the host view.
+     * <p>
      * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
      * the Explore by Touch feature is enabled.
      * <p>
-     * This method should be called by overriding
-     * {@link View#dispatchHoverEvent}:
-     *
+     * This method should be called by overriding the host view's
+     * {@link View#dispatchHoverEvent(MotionEvent)} method:
      * <pre>&#64;Override
      * public boolean dispatchHoverEvent(MotionEvent event) {
-     *   if (mHelper.dispatchHoverEvent(this, event) {
-     *     return true;
-     *   }
-     *   return super.dispatchHoverEvent(event);
+     *   return mHelper.dispatchHoverEvent(this, event)
+     *       || super.dispatchHoverEvent(event);
      * }
      * </pre>
      *
      * @param event The hover event to dispatch to the virtual view hierarchy.
      * @return Whether the hover event was handled.
      */
-    public boolean dispatchHoverEvent(MotionEvent event) {
+    public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
         if (!mManager.isEnabled()
                 || !AccessibilityManagerCompat.isTouchExplorationEnabled(mManager)) {
             return false;
@@ -145,7 +193,7 @@
                 updateHoveredVirtualView(virtualViewId);
                 return (virtualViewId != INVALID_ID);
             case MotionEventCompat.ACTION_HOVER_EXIT:
-                if (mFocusedVirtualViewId != INVALID_ID) {
+                if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
                     updateHoveredVirtualView(INVALID_ID);
                     return true;
                 }
@@ -156,78 +204,412 @@
     }
 
     /**
+     * Delegates key events from the host view.
+     * <p>
+     * This method should be called by overriding the host view's
+     * {@link View#dispatchKeyEvent(KeyEvent)} method:
+     * <pre>&#64;Override
+     * public boolean dispatchKeyEvent(KeyEvent event) {
+     *   return mHelper.dispatchKeyEvent(event)
+     *       || super.dispatchKeyEvent(event);
+     * }
+     * </pre>
+     */
+    public final boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+        boolean handled = false;
+
+        final int action = event.getAction();
+        if (action != KeyEvent.ACTION_UP) {
+            final int keyCode = event.getKeyCode();
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                case KeyEvent.KEYCODE_DPAD_UP:
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    if (KeyEventCompat.hasNoModifiers(event)) {
+                        final int direction = keyToDirection(keyCode);
+                        final int count = 1 + event.getRepeatCount();
+                        for (int i = 0; i < count; i++) {
+                            if (moveFocus(direction, null)) {
+                                handled = true;
+                            } else {
+                                break;
+                            }
+                        }
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_CENTER:
+                case KeyEvent.KEYCODE_ENTER:
+                    if (KeyEventCompat.hasNoModifiers(event)) {
+                        if (event.getRepeatCount() == 0) {
+                            clickKeyboardFocusedVirtualView();
+                            handled = true;
+                        }
+                    }
+                    break;
+                case KeyEvent.KEYCODE_TAB:
+                    if (KeyEventCompat.hasNoModifiers(event)) {
+                        handled = moveFocus(View.FOCUS_FORWARD, null);
+                    } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
+                        handled = moveFocus(View.FOCUS_BACKWARD, null);
+                    }
+                    break;
+            }
+        }
+
+        return handled;
+    }
+
+    /**
+     * Delegates focus changes from the host view.
+     * <p>
+     * This method should be called by overriding the host view's
+     * {@link View#onFocusChanged(boolean, int, Rect)} method:
+     * <pre>&#64;Override
+     * public boolean onFocusChanged(boolean gainFocus, int direction,
+     *     Rect previouslyFocusedRect) {
+     *   super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+     *   mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+     * }
+     * </pre>
+     */
+    public final void onFocusChanged(boolean gainFocus, int direction,
+            @Nullable Rect previouslyFocusedRect) {
+        if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+            clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
+        }
+
+        if (gainFocus) {
+            moveFocus(direction, previouslyFocusedRect);
+        }
+    }
+
+    /**
+     * @return the identifier of the virtual view that has accessibility focus
+     *         or {@link #INVALID_ID} if no virtual view has accessibility
+     *         focus
+     */
+    public final int getAccessibilityFocusedVirtualViewId() {
+        return mAccessibilityFocusedVirtualViewId;
+    }
+
+    /**
+     * @return the identifier of the virtual view that has keyboard focus
+     *         or {@link #INVALID_ID} if no virtual view has keyboard focus
+     */
+    public final int getKeyboardFocusedVirtualViewId() {
+        return mKeyboardFocusedVirtualViewId;
+    }
+
+    /**
+     * Maps key event codes to focus directions.
+     *
+     * @param keyCode the key event code
+     * @return the corresponding focus direction
+     */
+    @FocusRealDirection
+    private static int keyToDirection(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                return View.FOCUS_LEFT;
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return View.FOCUS_UP;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return View.FOCUS_RIGHT;
+            default:
+                return View.FOCUS_DOWN;
+        }
+    }
+
+    /**
+     * Obtains the bounds for the specified virtual view.
+     *
+     * @param virtualViewId the identifier of the virtual view
+     * @param outBounds the rect to populate with virtual view bounds
+     */
+    private void getBoundsInParent(int virtualViewId, Rect outBounds) {
+        final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
+        node.getBoundsInParent(outBounds);
+    }
+
+    /**
+     * Adapts AccessibilityNodeInfoCompat for obtaining bounds.
+     */
+    private static final FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat> NODE_ADAPTER =
+            new FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat>() {
+                @Override
+                public void obtainBounds(AccessibilityNodeInfoCompat node, Rect outBounds) {
+                    node.getBoundsInParent(outBounds);
+                }
+            };
+
+    /**
+     * Adapts SparseArrayCompat for iterating through values.
+     */
+    private static final FocusStrategy.CollectionAdapter<SparseArrayCompat<
+            AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat> SPARSE_VALUES_ADAPTER =
+            new FocusStrategy.CollectionAdapter<SparseArrayCompat<
+                    AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat>() {
+                @Override
+                public AccessibilityNodeInfoCompat get(
+                        SparseArrayCompat<AccessibilityNodeInfoCompat> collection, int index) {
+                    return collection.valueAt(index);
+                }
+
+                @Override
+                public int size(SparseArrayCompat<AccessibilityNodeInfoCompat> collection) {
+                    return collection.size();
+                }
+            };
+
+    /**
+     * Attempts to move keyboard focus in the specified direction.
+     *
+     * @param direction the direction in which to move keyboard focus
+     * @param previouslyFocusedRect the bounds of the previously focused item,
+     *                              or {@code null} if not available
+     * @return {@code true} if keyboard focus moved to a virtual view managed
+     *         by this helper, or {@code false} otherwise
+     */
+    private boolean moveFocus(@FocusDirection int direction, @Nullable Rect previouslyFocusedRect) {
+        final int focusedNodeId = mKeyboardFocusedVirtualViewId;
+        final AccessibilityNodeInfoCompat focusedNode =
+                focusedNodeId == INVALID_ID ? null : mCachedNodes.get(focusedNodeId);
+
+        final AccessibilityNodeInfoCompat nextFocusedNode;
+        switch (direction) {
+            case View.FOCUS_FORWARD:
+            case View.FOCUS_BACKWARD:
+                final boolean isLayoutRtl =
+                        ViewCompat.getLayoutDirection(mHost) == ViewCompat.LAYOUT_DIRECTION_RTL;
+                nextFocusedNode = FocusStrategy.findNextFocusInRelativeDirection(mCachedNodes,
+                        SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, direction, isLayoutRtl,
+                        false);
+                break;
+            case View.FOCUS_LEFT:
+            case View.FOCUS_UP:
+            case View.FOCUS_RIGHT:
+            case View.FOCUS_DOWN:
+                final Rect selectedRect = new Rect();
+                if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+                    // Focus is moving from a virtual view within the host.
+                    getBoundsInParent(mKeyboardFocusedVirtualViewId, selectedRect);
+                } else if (previouslyFocusedRect != null) {
+                    // Focus is moving from a real view outside the host.
+                    selectedRect.set(previouslyFocusedRect);
+                } else {
+                    // Focus is moving from... somewhere? Make a guess.
+                    // Usually this happens when another view was too lazy
+                    // to pass the previously focused rect (ex. ScrollView
+                    // when moving UP or DOWN).
+                    guessPreviouslyFocusedRect(mHost, direction, selectedRect);
+                }
+                nextFocusedNode = FocusStrategy.findNextFocusInAbsoluteDirection(mCachedNodes,
+                        SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, selectedRect, direction);
+                break;
+            default:
+                throw new IllegalArgumentException("direction must be one of "
+                        + "{FOCUS_FORWARD, FOCUS_BACKWARD, FOCUS_UP, FOCUS_DOWN, "
+                        + "FOCUS_LEFT, FOCUS_RIGHT}.");
+        }
+
+        final int nextFocusedNodeId;
+        if (nextFocusedNode == null) {
+            nextFocusedNodeId = INVALID_ID;
+        } else {
+            final int index = mCachedNodes.indexOfValue(nextFocusedNode);
+            nextFocusedNodeId = mCachedNodes.keyAt(index);
+        }
+
+        return requestKeyboardFocusForVirtualView(nextFocusedNodeId);
+    }
+
+    /**
+     * Obtains a best guess for the previously focused rect for keyboard focus
+     * moving in the specified direction.
+     *
+     * @param host the view into which focus is moving
+     * @param direction the absolute direction in which focus is moving
+     * @param outBounds the rect to populate with the best-guess bounds for the
+     *                  previous focus rect
+     */
+    private static Rect guessPreviouslyFocusedRect(@NonNull View host,
+            @FocusRealDirection int direction, @NonNull Rect outBounds) {
+        final int w = host.getWidth();
+        final int h = host.getHeight();
+
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                outBounds.set(w, 0, w, h);
+                break;
+            case View.FOCUS_UP:
+                outBounds.set(0, h, w, h);
+                break;
+            case View.FOCUS_RIGHT:
+                outBounds.set(-1, 0, -1, h);
+                break;
+            case View.FOCUS_DOWN:
+                outBounds.set(0, -1, w, -1);
+                break;
+            default:
+                throw new IllegalArgumentException("direction must be one of "
+                        + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+        }
+
+        return outBounds;
+    }
+
+    /**
+     * Performs a click action on the keyboard focused virtual view, if any.
+     *
+     * @return {@code true} if the click action was performed successfully or
+     *         {@code false} otherwise
+     */
+    private boolean clickKeyboardFocusedVirtualView() {
+        return mKeyboardFocusedVirtualViewId != INVALID_ID && onPerformActionForVirtualView(
+                mKeyboardFocusedVirtualViewId, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+    }
+
+    /**
      * Populates an event of the specified type with information about an item
      * and attempts to send it up through the view hierarchy.
      * <p>
      * You should call this method after performing a user action that normally
      * fires an accessibility event, such as clicking on an item.
-     *
+     * <p>
      * <pre>public void performItemClick(T item) {
      *   ...
      *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
      * }
      * </pre>
      *
-     * @param virtualViewId The virtual view id for which to send an event.
-     * @param eventType The type of event to send.
-     * @return true if the event was sent successfully.
+     * @param virtualViewId the identifier of the virtual view for which to
+     *                      send an event
+     * @param eventType the type of event to send
+     * @return {@code true} if the event was sent successfully, {@code false}
+     *         otherwise
      */
-    public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
+    public final boolean sendEventForVirtualView(int virtualViewId, int eventType) {
         if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
             return false;
         }
 
-        final ViewParent parent = mView.getParent();
+        final ViewParent parent = mHost.getParent();
         if (parent == null) {
             return false;
         }
 
         final AccessibilityEvent event = createEvent(virtualViewId, eventType);
-        return ViewParentCompat.requestSendAccessibilityEvent(parent, mView, event);
+        return ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
     }
 
     /**
      * Notifies the accessibility framework that the properties of the parent
      * view have changed.
      * <p>
-     * You <b>must</b> call this method after adding or removing items from the
-     * parent view.
+     * You <strong>must</strong> call this method after adding or removing
+     * items from the parent view.
      */
-    public void invalidateRoot() {
-        invalidateVirtualView(HOST_ID);
+    public final void invalidateRoot() {
+        invalidateVirtualView(HOST_ID, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE);
     }
 
     /**
      * Notifies the accessibility framework that the properties of a particular
      * item have changed.
      * <p>
-     * You <b>must</b> call this method after changing any of the properties set
-     * in {@link #onPopulateNodeForVirtualView}.
+     * You <strong>must</strong> call this method after changing any of the
+     * properties set in
+     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
      *
-     * @param virtualViewId The virtual view id to invalidate.
+     * @param virtualViewId the virtual view id to invalidate, or
+     *                      {@link #HOST_ID} to invalidate the root view
+     * @see #invalidateVirtualView(int, int)
      */
-    public void invalidateVirtualView(int virtualViewId) {
-        sendEventForVirtualView(
-                virtualViewId, AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+    public final void invalidateVirtualView(int virtualViewId) {
+        invalidateVirtualView(virtualViewId,
+                AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
-     * Returns the virtual view id for the currently focused item,
+     * Notifies the accessibility framework that the properties of a particular
+     * item have changed.
+     * <p>
+     * You <strong>must</strong> call this method after changing any of the
+     * properties set in
+     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
      *
-     * @return A virtual view id, or {@link #INVALID_ID} if no item is
-     *         currently focused.
+     * @param virtualViewId the virtual view id to invalidate, or
+     *                      {@link #HOST_ID} to invalidate the root view
+     * @param changeTypes the bit mask of change types. May be {@code 0} for the
+     *                    default (undefined) change type or one or more of:
+     *         <ul>
+     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_SUBTREE}
+     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_TEXT}
+     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_UNDEFINED}
+     *         </ul>
      */
+    public final void invalidateVirtualView(int virtualViewId, int changeTypes) {
+        final SparseArrayCompat<AccessibilityNodeInfoCompat> cachedNodes = mCachedNodes;
+        if (virtualViewId == HOST_ID
+                && (changeTypes & AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+            for (int i = 0, count = cachedNodes.size(); i < count; i++) {
+                cachedNodes.valueAt(i).recycle();
+            }
+            cachedNodes.clear();
+        } else {
+            final int index = cachedNodes.indexOfKey(virtualViewId);
+            if (index >= 0) {
+                cachedNodes.valueAt(index).recycle();
+                cachedNodes.removeAt(index);
+            }
+        }
+
+        if (virtualViewId != INVALID_ID && mManager.isEnabled()) {
+            final ViewParent parent = mHost.getParent();
+            if (parent != null) {
+                // Send events up the hierarchy so they can be coalesced.
+                final AccessibilityEvent event = createEvent(virtualViewId,
+                        AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+                AccessibilityEventCompat.setContentChangeTypes(event, changeTypes);
+                ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
+            }
+        }
+    }
+
+    /**
+     * Returns the virtual view ID for the currently accessibility focused
+     * item.
+     *
+     * @return the identifier of the virtual view that has accessibility focus
+     *         or {@link #INVALID_ID} if no virtual view has accessibility
+     *         focus
+     * @deprecated Use {@link #getAccessibilityFocusedVirtualViewId()}.
+     */
+    @Deprecated
     public int getFocusedVirtualView() {
-        return mFocusedVirtualViewId;
+        return getAccessibilityFocusedVirtualViewId();
+    }
+
+    /**
+     * Called when the focus state of a virtual view changes.
+     *
+     * @param virtualViewId the virtual view identifier
+     * @param hasFocus      {@code true} if the view has focus, {@code false}
+     *                      otherwise
+     */
+    protected void onVirtualViewKeyboardFocusChanged(int virtualViewId, boolean hasFocus) {
+        // Stub method.
     }
 
     /**
      * Sets the currently hovered item, sending hover accessibility events as
      * necessary to maintain the correct state.
      *
-     * @param virtualViewId The virtual view id for the item currently being
-     *            hovered, or {@link #INVALID_ID} if no item is hovered within
-     *            the parent view.
+     * @param virtualViewId the virtual view id for the item currently being
+     *                      hovered, or {@link #INVALID_ID} if no item is
+     *                      hovered within the parent view
      */
     private void updateHoveredVirtualView(int virtualViewId) {
         if (mHoveredVirtualViewId == virtualViewId) {
@@ -248,11 +630,11 @@
      * Constructs and returns an {@link AccessibilityEvent} for the specified
      * virtual view id, which includes the host view ({@link #HOST_ID}).
      *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct an event.
-     * @param eventType The type of event to construct.
-     * @return An {@link AccessibilityEvent} populated with information about
-     *         the specified item.
+     * @param virtualViewId the virtual view id for the item for which to
+     *                      construct an event
+     * @param eventType the type of event to construct
+     * @return an {@link AccessibilityEvent} populated with information about
+     *         the specified item
      */
     private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
         switch (virtualViewId) {
@@ -266,13 +648,17 @@
     /**
      * Constructs and returns an {@link AccessibilityEvent} for the host node.
      *
-     * @param eventType The type of event to construct.
-     * @return An {@link AccessibilityEvent} populated with information about
-     *         the specified item.
+     * @param eventType the type of event to construct
+     * @return an {@link AccessibilityEvent} populated with information about
+     *         the specified item
      */
     private AccessibilityEvent createEventForHost(int eventType) {
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        ViewCompat.onInitializeAccessibilityEvent(mView, event);
+        ViewCompat.onInitializeAccessibilityEvent(mHost, event);
+
+        // Allow the client to populate the event.
+        onPopulateEventForHost(event);
+
         return event;
     }
 
@@ -280,16 +666,24 @@
      * Constructs and returns an {@link AccessibilityEvent} populated with
      * information about the specified item.
      *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct an event.
-     * @param eventType The type of event to construct.
-     * @return An {@link AccessibilityEvent} populated with information about
-     *         the specified item.
+     * @param virtualViewId the virtual view id for the item for which to
+     *                      construct an event
+     * @param eventType the type of event to construct
+     * @return an {@link AccessibilityEvent} populated with information about
+     *         the specified item
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        event.setEnabled(true);
-        event.setClassName(DEFAULT_CLASS_NAME);
+        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
+        final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
+
+        // Allow the client to override these properties,
+        record.getText().add(node.getText());
+        record.setContentDescription(node.getContentDescription());
+        record.setScrollable(node.isScrollable());
+        record.setPassword(node.isPassword());
+        record.setEnabled(node.isEnabled());
+        record.setChecked(node.isChecked());
 
         // Allow the client to populate the event.
         onPopulateEventForVirtualView(virtualViewId, event);
@@ -301,52 +695,66 @@
         }
 
         // Don't allow the client to override these properties.
-        event.setPackageName(mView.getContext().getPackageName());
-
-        final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
-        record.setSource(mView, virtualViewId);
+        record.setClassName(node.getClassName());
+        record.setSource(mHost, virtualViewId);
+        event.setPackageName(mHost.getContext().getPackageName());
 
         return event;
     }
 
     /**
-     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
-     * specified virtual view id, which includes the host view
-     * ({@link #HOST_ID}).
+     * Obtains a populated {@link AccessibilityNodeInfoCompat} for the
+     * virtual view with the specified identifier.
+     * <p>
+     * This method may be called with identifier {@link #HOST_ID} to obtain a
+     * node for the host view.
      *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            construct a node.
-     * @return An {@link AccessibilityNodeInfoCompat} populated with information
-     *         about the specified item.
+     * @param virtualViewId the identifier of the virtual view for which to
+     *                      construct a node
+     * @return an {@link AccessibilityNodeInfoCompat} populated with information
+     *         about the specified item
      */
-    private AccessibilityNodeInfoCompat createNode(int virtualViewId) {
-        switch (virtualViewId) {
-            case HOST_ID:
-                return createNodeForHost();
-            default:
-                return createNodeForChild(virtualViewId);
+    @NonNull
+    private AccessibilityNodeInfoCompat obtainAccessibilityNodeInfo(int virtualViewId) {
+        final AccessibilityNodeInfoCompat node;
+        final int cacheIndex = mCachedNodes.indexOfKey(virtualViewId);
+        if (cacheIndex >= 0) {
+            node = mCachedNodes.valueAt(cacheIndex);
+        } else if (virtualViewId == HOST_ID) {
+            node = createNodeForHost();
+        } else {
+            node = createNodeForChild(virtualViewId);
         }
+
+        mCachedNodes.put(virtualViewId, node);
+
+        return node;
     }
 
     /**
      * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
      * host view populated with its virtual descendants.
      *
-     * @return An {@link AccessibilityNodeInfoCompat} for the parent node.
+     * @return an {@link AccessibilityNodeInfoCompat} for the parent node
      */
+    @NonNull
     private AccessibilityNodeInfoCompat createNodeForHost() {
-        final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mView);
-        ViewCompat.onInitializeAccessibilityNodeInfo(mView, node);
+        final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain(mHost);
+        ViewCompat.onInitializeAccessibilityNodeInfo(mHost, node);
+        final int realNodeCount = node.getChildCount();
 
         // Allow the client to populate the host node.
         onPopulateNodeForHost(node);
 
         // Add the virtual descendants.
-        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
+        final ArrayList<Integer> virtualViewIds = new ArrayList<>();
         getVisibleVirtualViews(virtualViewIds);
+        if (realNodeCount > 0 && virtualViewIds.size() > 0) {
+            throw new RuntimeException("Views cannot have both real and virtual children");
+        }
 
-        for (Integer childVirtualViewId : virtualViewIds) {
-            node.addChild(mView, childVirtualViewId);
+        for (int i = 0, count = virtualViewIds.size(); i < count; i++) {
+            node.addChild(mHost, virtualViewIds.get(i));
         }
 
         return node;
@@ -376,15 +784,17 @@
      * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
      * </ul>
      *
-     * @param virtualViewId The virtual view id for item for which to construct
-     *            a node.
-     * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
+     * @param virtualViewId the virtual view id for item for which to construct
+     *                      a node
+     * @return an {@link AccessibilityNodeInfoCompat} for the specified item
      */
+    @NonNull
     private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
         final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
 
         // Ensure the client has good defaults.
         node.setEnabled(true);
+        node.setFocusable(true);
         node.setClassName(DEFAULT_CLASS_NAME);
 
         // Allow the client to populate the node.
@@ -413,12 +823,12 @@
         }
 
         // Don't allow the client to override these properties.
-        node.setPackageName(mView.getContext().getPackageName());
-        node.setSource(mView, virtualViewId);
-        node.setParent(mView);
+        node.setPackageName(mHost.getContext().getPackageName());
+        node.setSource(mHost, virtualViewId);
+        node.setParent(mHost);
 
         // Manage internal accessibility focus state.
-        if (mFocusedVirtualViewId == virtualViewId) {
+        if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
             node.setAccessibilityFocused(true);
             node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
         } else {
@@ -426,6 +836,15 @@
             node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
         }
 
+        // Manage internal keyboard focus state.
+        final boolean isFocused = mKeyboardFocusedVirtualViewId == virtualViewId;
+        if (isFocused) {
+            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS);
+        } else if (node.isFocusable()) {
+            node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS);
+        }
+        node.setFocused(isFocused);
+
         // Set the visibility based on the parent bound.
         if (intersectVisibleToUser(mTempParentRect)) {
             node.setVisibleToUser(true);
@@ -433,7 +852,7 @@
         }
 
         // Calculate screen-relative bound.
-        mView.getLocationOnScreen(mTempGlobalRect);
+        mHost.getLocationOnScreen(mTempGlobalRect);
         final int offsetX = mTempGlobalRect[0];
         final int offsetY = mTempGlobalRect[1];
         mTempScreenRect.set(mTempParentRect);
@@ -453,27 +872,21 @@
     }
 
     private boolean performActionForHost(int action, Bundle arguments) {
-        return ViewCompat.performAccessibilityAction(mView, action, arguments);
+        return ViewCompat.performAccessibilityAction(mHost, action, arguments);
     }
 
     private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
         switch (action) {
             case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
-            case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                return manageFocusForChild(virtualViewId, action, arguments);
-            default:
-                return onPerformActionForVirtualView(virtualViewId, action, arguments);
-        }
-    }
-
-    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
-        switch (action) {
-            case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
                 return requestAccessibilityFocus(virtualViewId);
             case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
                 return clearAccessibilityFocus(virtualViewId);
+            case AccessibilityNodeInfoCompat.ACTION_FOCUS:
+                return requestKeyboardFocusForVirtualView(virtualViewId);
+            case AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS:
+                return clearKeyboardFocusForVirtualView(virtualViewId);
             default:
-                return false;
+                return onPerformActionForVirtualView(virtualViewId, action, arguments);
         }
     }
 
@@ -482,8 +895,8 @@
      * portion of its parent {@link View}. Modifies {@code localRect} to contain
      * only the visible portion.
      *
-     * @param localRect A rectangle in local (parent) coordinates.
-     * @return Whether the specified {@link Rect} is visible on the screen.
+     * @param localRect a rectangle in local (parent) coordinates
+     * @return whether the specified {@link Rect} is visible on the screen
      */
     private boolean intersectVisibleToUser(Rect localRect) {
         // Missing or empty bounds mean this view is not visible.
@@ -492,12 +905,12 @@
         }
 
         // Attached to invisible window means this view is not visible.
-        if (mView.getWindowVisibility() != View.VISIBLE) {
+        if (mHost.getWindowVisibility() != View.VISIBLE) {
             return false;
         }
 
         // An invisible predecessor means that this view is not visible.
-        ViewParent viewParent = mView.getParent();
+        ViewParent viewParent = mHost.getParent();
         while (viewParent instanceof View) {
             final View view = (View) viewParent;
             if ((ViewCompat.getAlpha(view) <= 0) || (view.getVisibility() != View.VISIBLE)) {
@@ -512,7 +925,7 @@
         }
 
         // If no portion of the parent is visible, this view is not visible.
-        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
+        if (!mHost.getLocalVisibleRect(mTempVisibleRect)) {
             return false;
         }
 
@@ -521,15 +934,6 @@
     }
 
     /**
-     * Returns whether this virtual view is accessibility focused.
-     *
-     * @return True if the view is accessibility focused.
-     */
-    private boolean isAccessibilityFocused(int virtualViewId) {
-        return (mFocusedVirtualViewId == virtualViewId);
-    }
-
-    /**
      * Attempts to give accessibility focus to a virtual view.
      * <p>
      * A virtual view will not actually take focus if
@@ -537,9 +941,9 @@
      * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
      * or the view already has accessibility focus.
      *
-     * @param virtualViewId The id of the virtual view on which to place
-     *            accessibility focus.
-     * @return Whether this virtual view actually took accessibility focus.
+     * @param virtualViewId the identifier of the virtual view on which to
+     *                      place accessibility focus
+     * @return whether this virtual view actually took accessibility focus
      */
     private boolean requestAccessibilityFocus(int virtualViewId) {
         if (!mManager.isEnabled()
@@ -547,18 +951,17 @@
             return false;
         }
         // TODO: Check virtual view visibility.
-        if (!isAccessibilityFocused(virtualViewId)) {
+        if (mAccessibilityFocusedVirtualViewId != virtualViewId) {
             // Clear focus from the previously focused view, if applicable.
-            if (mFocusedVirtualViewId != INVALID_ID) {
-                sendEventForVirtualView(mFocusedVirtualViewId,
-                        AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
+                clearAccessibilityFocus(mAccessibilityFocusedVirtualViewId);
             }
 
             // Set focus on the new view.
-            mFocusedVirtualViewId = virtualViewId;
+            mAccessibilityFocusedVirtualViewId = virtualViewId;
 
             // TODO: Only invalidate virtual view bounds.
-            mView.invalidate();
+            mHost.invalidate();
             sendEventForVirtualView(virtualViewId,
                     AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
             return true;
@@ -569,14 +972,14 @@
     /**
      * Attempts to clear accessibility focus from a virtual view.
      *
-     * @param virtualViewId The id of the virtual view from which to clear
-     *            accessibility focus.
-     * @return Whether this virtual view actually cleared accessibility focus.
+     * @param virtualViewId the identifier of the virtual view from which to
+     *                      clear accessibility focus
+     * @return whether this virtual view actually cleared accessibility focus
      */
     private boolean clearAccessibilityFocus(int virtualViewId) {
-        if (isAccessibilityFocused(virtualViewId)) {
-            mFocusedVirtualViewId = INVALID_ID;
-            mView.invalidate();
+        if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
+            mAccessibilityFocusedVirtualViewId = INVALID_ID;
+            mHost.invalidate();
             sendEventForVirtualView(virtualViewId,
                     AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
             return true;
@@ -585,6 +988,57 @@
     }
 
     /**
+     * Attempts to give keyboard focus to a virtual view.
+     *
+     * @param virtualViewId the identifier of the virtual view on which to
+     *                      place keyboard focus
+     * @return whether this virtual view actually took keyboard focus
+     */
+    public final boolean requestKeyboardFocusForVirtualView(int virtualViewId) {
+        if (!mHost.isFocused() && !mHost.requestFocus()) {
+            // Host must have real keyboard focus.
+            return false;
+        }
+
+        if (mKeyboardFocusedVirtualViewId == virtualViewId) {
+            // The virtual view already has focus.
+            return false;
+        }
+
+        if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
+            clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
+        }
+
+        mKeyboardFocusedVirtualViewId = virtualViewId;
+
+        onVirtualViewKeyboardFocusChanged(virtualViewId, true);
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
+
+        return true;
+    }
+
+    /**
+     * Attempts to clear keyboard focus from a virtual view.
+     *
+     * @param virtualViewId the identifier of the virtual view from which to
+     *                      clear keyboard focus
+     * @return whether this virtual view actually cleared keyboard focus
+     */
+    public final boolean clearKeyboardFocusForVirtualView(int virtualViewId) {
+        if (mKeyboardFocusedVirtualViewId != virtualViewId) {
+            // The virtual view is not focused.
+            return false;
+        }
+
+        mKeyboardFocusedVirtualViewId = INVALID_ID;
+
+        onVirtualViewKeyboardFocusChanged(virtualViewId, false);
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
+
+        return true;
+    }
+
+    /**
      * Provides a mapping between view-relative coordinates and logical
      * items.
      *
@@ -609,22 +1063,25 @@
      * Populates an {@link AccessibilityEvent} with information about the
      * specified item.
      * <p>
-     * Implementations <b>must</b> populate the following required fields:
+     * The helper class automatically populates the following fields based on
+     * the values set by
+     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)},
+     * but implementations may optionally override them:
      * <ul>
-     * <li>event text, see {@link AccessibilityEvent#getText} or
-     * {@link AccessibilityEvent#setContentDescription}
-     * </ul>
-     * <p>
-     * The helper class automatically populates the following fields with
-     * default values, but implementations may optionally override them:
-     * <ul>
-     * <li>item class name, set to android.view.View, see
-     * {@link AccessibilityEvent#setClassName}
+     * <li>event text, see {@link AccessibilityEvent#getText()}
+     * <li>content description, see
+     * {@link AccessibilityEvent#setContentDescription(CharSequence)}
+     * <li>scrollability, see {@link AccessibilityEvent#setScrollable(boolean)}
+     * <li>password state, see {@link AccessibilityEvent#setPassword(boolean)}
+     * <li>enabled state, see {@link AccessibilityEvent#setEnabled(boolean)}
+     * <li>checked state, see {@link AccessibilityEvent#setChecked(boolean)}
      * </ul>
      * <p>
      * The following required fields are automatically populated by the
      * helper class and may not be overridden:
      * <ul>
+     * <li>item class name, set to the value used in
+     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}
      * <li>package name, set to the package of the host view's
      * {@link Context}, see {@link AccessibilityEvent#setPackageName}
      * <li>event source, set to the host view and virtual view identifier,
@@ -635,55 +1092,76 @@
      *            populate the event
      * @param event The event to populate
      */
-    protected abstract void onPopulateEventForVirtualView(
-            int virtualViewId, AccessibilityEvent event);
+    protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+        // Default implementation is no-op.
+    }
+
+    /**
+     * Populates an {@link AccessibilityEvent} with information about the host
+     * view.
+     * <p>
+     * The default implementation is a no-op.
+     *
+     * @param event the event to populate with information about the host view
+     */
+    protected void onPopulateEventForHost(AccessibilityEvent event) {
+        // Default implementation is no-op.
+    }
 
     /**
      * Populates an {@link AccessibilityNodeInfoCompat} with information
      * about the specified item.
      * <p>
-     * Implementations <b>must</b> populate the following required fields:
+     * Implementations <strong>must</strong> populate the following required
+     * fields:
      * <ul>
-     * <li>event text, see {@link AccessibilityNodeInfoCompat#setText} or
-     * {@link AccessibilityNodeInfoCompat#setContentDescription}
+     * <li>event text, see
+     * {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or
+     * {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)}
      * <li>bounds in parent coordinates, see
-     * {@link AccessibilityNodeInfoCompat#setBoundsInParent}
+     * {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}
      * </ul>
      * <p>
      * The helper class automatically populates the following fields with
      * default values, but implementations may optionally override them:
      * <ul>
-     * <li>enabled state, set to true, see
-     * {@link AccessibilityNodeInfoCompat#setEnabled}
-     * <li>item class name, identical to the class name set by
-     * {@link #onPopulateEventForVirtualView}, see
-     * {@link AccessibilityNodeInfoCompat#setClassName}
+     * <li>enabled state, set to {@code true}, see
+     * {@link AccessibilityNodeInfoCompat#setEnabled(boolean)}
+     * <li>keyboard focusability, set to {@code true}, see
+     * {@link AccessibilityNodeInfoCompat#setFocusable(boolean)}
+     * <li>item class name, set to {@code android.view.View}, see
+     * {@link AccessibilityNodeInfoCompat#setClassName(CharSequence)}
      * </ul>
      * <p>
      * The following required fields are automatically populated by the
      * helper class and may not be overridden:
      * <ul>
      * <li>package name, identical to the package name set by
-     * {@link #onPopulateEventForVirtualView}, see
+     * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
      * {@link AccessibilityNodeInfoCompat#setPackageName}
      * <li>node source, identical to the event source set in
-     * {@link #onPopulateEventForVirtualView}, see
+     * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
      * {@link AccessibilityNodeInfoCompat#setSource(View, int)}
      * <li>parent view, set to the host view, see
      * {@link AccessibilityNodeInfoCompat#setParent(View)}
      * <li>visibility, computed based on parent-relative bounds, see
-     * {@link AccessibilityNodeInfoCompat#setVisibleToUser}
+     * {@link AccessibilityNodeInfoCompat#setVisibleToUser(boolean)}
      * <li>accessibility focus, computed based on internal helper state, see
-     * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused}
+     * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused(boolean)}
+     * <li>keyboard focus, computed based on internal helper state, see
+     * {@link AccessibilityNodeInfoCompat#setFocused(boolean)}
      * <li>bounds in screen coordinates, computed based on host view bounds,
-     * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen}
+     * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
      * </ul>
      * <p>
-     * Additionally, the helper class automatically handles accessibility
-     * focus management by adding the appropriate
-     * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS} or
+     * Additionally, the helper class automatically handles keyboard focus and
+     * accessibility focus management by adding the appropriate
+     * {@link AccessibilityNodeInfoCompat#ACTION_FOCUS},
+     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_FOCUS},
+     * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}, or
      * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * action. Implementations must <b>never</b> manually add these actions.
+     * actions. Implementations must <strong>never</strong> manually add these
+     * actions.
      * <p>
      * The helper class also automatically modifies parent- and
      * screen-relative bounds to reflect the portion of the item visible
@@ -700,10 +1178,11 @@
      * Populates an {@link AccessibilityNodeInfoCompat} with information
      * about the host view.
      * <p>
-     * The following required fields are automatically populated by the
-     * helper class and may not be overridden:
+     * The default implementation is a no-op.
+     *
+     * @param node the node to populate with information about the host view
      */
-    public void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
+    protected void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
         // Default implementation is no-op.
     }
 
@@ -713,8 +1192,9 @@
      * {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
      * more information.
      * <p>
-     * Implementations <b>must</b> handle any actions added manually in
-     * {@link #onPopulateNodeForVirtualView}.
+     * Implementations <strong>must</strong> handle any actions added manually
+     * in
+     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
      * <p>
      * The helper class automatically handles focus management resulting
      * from {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}
@@ -733,13 +1213,16 @@
             int virtualViewId, int action, Bundle arguments);
 
     /**
-     * Exposes a virtual view hierarchy to the accessibility framework. Only
-     * used in API 16+.
+     * Exposes a virtual view hierarchy to the accessibility framework.
      */
-    private class ExploreByTouchNodeProvider extends AccessibilityNodeProviderCompat {
+    private class MyNodeProvider extends AccessibilityNodeProviderCompat {
         @Override
         public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
-            return ExploreByTouchHelper.this.createNode(virtualViewId);
+            // The caller takes ownership of the node and is expected to
+            // recycle it when done, so always return a copy.
+            final AccessibilityNodeInfoCompat node =
+                    ExploreByTouchHelper.this.obtainAccessibilityNodeInfo(virtualViewId);
+            return AccessibilityNodeInfoCompat.obtain(node);
         }
 
         @Override
diff --git a/v4/java/android/support/v4/widget/FocusStrategy.java b/v4/java/android/support/v4/widget/FocusStrategy.java
new file mode 100644
index 0000000..8be9f1a
--- /dev/null
+++ b/v4/java/android/support/v4/widget/FocusStrategy.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.widget;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import android.support.v4.view.ViewCompat.FocusRealDirection;
+import android.support.v4.view.ViewCompat.FocusRelativeDirection;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * Implements absolute and relative focus movement strategies. Adapted from
+ * android.view.FocusFinder to work with generic collections of bounded items.
+ */
+class FocusStrategy {
+    public static <L,T> T findNextFocusInRelativeDirection(@NonNull L focusables,
+            @NonNull CollectionAdapter<L,T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
+            @Nullable T focused, @FocusRelativeDirection int direction, boolean isLayoutRtl,
+            boolean wrap) {
+        final int count = collectionAdapter.size(focusables);
+        final ArrayList<T> sortedFocusables = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            sortedFocusables.add(collectionAdapter.get(focusables, i));
+        }
+
+        final SequentialComparator<T> comparator = new SequentialComparator<>(isLayoutRtl, adapter);
+        Collections.sort(sortedFocusables, comparator);
+
+        switch (direction) {
+            case View.FOCUS_FORWARD:
+                return getNextFocusable(focused, sortedFocusables, wrap);
+            case View.FOCUS_BACKWARD:
+                return getPreviousFocusable(focused, sortedFocusables, wrap);
+            default:
+                throw new IllegalArgumentException("direction must be one of "
+                        + "{FOCUS_FORWARD, FOCUS_BACKWARD}.");
+        }
+    }
+
+    private static <T> T getNextFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
+        final int count = focusables.size();
+
+        // The position of the next focusable item, which is the first item if
+        // no item is currently focused.
+        final int position = (focused == null ? -1 : focusables.lastIndexOf(focused)) + 1;
+        if (position < count) {
+            return focusables.get(position);
+        } else if (wrap && count > 0) {
+            return focusables.get(0);
+        } else {
+            return null;
+        }
+    }
+
+    private static <T> T getPreviousFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
+        final int count = focusables.size();
+
+        // The position of the previous focusable item, which is the last item
+        // if no item is currently focused.
+        final int position = (focused == null ? count : focusables.indexOf(focused)) - 1;
+        if (position >= 0) {
+            return focusables.get(position);
+        } else if (wrap && count > 0) {
+            return focusables.get(count - 1);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Sorts views according to their visual layout and geometry for default tab order.
+     * This is used for sequential focus traversal.
+     */
+    private static class SequentialComparator<T> implements Comparator<T> {
+        private final Rect mTemp1 = new Rect();
+        private final Rect mTemp2 = new Rect();
+
+        private final boolean mIsLayoutRtl;
+        private final BoundsAdapter<T> mAdapter;
+
+        public SequentialComparator(boolean isLayoutRtl, BoundsAdapter<T> adapter) {
+            mIsLayoutRtl = isLayoutRtl;
+            mAdapter = adapter;
+        }
+
+        public int compare(T first, T second) {
+            final Rect firstRect = mTemp1;
+            final Rect secondRect = mTemp2;
+
+            mAdapter.obtainBounds(first, firstRect);
+            mAdapter.obtainBounds(second, secondRect);
+
+            if (firstRect.top < secondRect.top) {
+                return -1;
+            } else if (firstRect.top > secondRect.top) {
+                return 1;
+            } else if (firstRect.left < secondRect.left) {
+                return mIsLayoutRtl ? 1 : -1;
+            } else if (firstRect.left > secondRect.left) {
+                return mIsLayoutRtl ? -1 : 1;
+            } else if (firstRect.bottom < secondRect.bottom) {
+                return -1;
+            } else if (firstRect.bottom > secondRect.bottom) {
+                return 1;
+            } else if (firstRect.right < secondRect.right) {
+                return mIsLayoutRtl ? 1 : -1;
+            } else if (firstRect.right > secondRect.right) {
+                return mIsLayoutRtl ? -1 : 1;
+            } else {
+                // The view are distinct but completely coincident so we
+                // consider them equal for our purposes. Since the sort is
+                // stable, this means that the views will retain their
+                // layout order relative to one another.
+                return 0;
+            }
+        }
+    }
+
+    public static <L,T> T findNextFocusInAbsoluteDirection(@NonNull L focusables,
+            @NonNull CollectionAdapter<L,T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
+            @Nullable T focused, @NonNull Rect focusedRect, int direction) {
+        // Initialize the best candidate to something impossible so that
+        // the first plausible view will become the best choice.
+        final Rect bestCandidateRect = new Rect(focusedRect);
+
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                bestCandidateRect.offset(focusedRect.width() + 1, 0);
+                break;
+            case View.FOCUS_RIGHT:
+                bestCandidateRect.offset(-(focusedRect.width() + 1), 0);
+                break;
+            case View.FOCUS_UP:
+                bestCandidateRect.offset(0, focusedRect.height() + 1);
+                break;
+            case View.FOCUS_DOWN:
+                bestCandidateRect.offset(0, -(focusedRect.height() + 1));
+                break;
+            default:
+                throw new IllegalArgumentException("direction must be one of "
+                        + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+        }
+
+        T closest = null;
+
+        final int count = collectionAdapter.size(focusables);
+        final Rect focusableRect = new Rect();
+        for (int i = 0; i < count; i++) {
+            final T focusable = collectionAdapter.get(focusables, i);
+            if (focusable == focused) {
+                continue;
+            }
+
+            // get focus bounds of other view
+            adapter.obtainBounds(focusable, focusableRect);
+            if (isBetterCandidate(direction, focusedRect, focusableRect, bestCandidateRect)) {
+                bestCandidateRect.set(focusableRect);
+                closest = focusable;
+            }
+        }
+
+        return closest;
+    }
+
+    /**
+     * Is candidate a better candidate than currentBest for a focus search
+     * in a particular direction from a source rect? This is the core
+     * routine that determines the order of focus searching.
+     *
+     * @param direction   the direction (up, down, left, right)
+     * @param source      the source from which we are searching
+     * @param candidate   the candidate rectangle
+     * @param currentBest the current best rectangle
+     * @return {@code true} if the candidate rectangle is a better than the
+     * current best rectangle, {@code false} otherwise
+     */
+    private static boolean isBetterCandidate(
+            @FocusRealDirection int direction, @NonNull Rect source,
+            @NonNull Rect candidate, @NonNull Rect currentBest) {
+        // To be a better candidate, need to at least be a candidate in the
+        // first place. :)
+        if (!isCandidate(source, candidate, direction)) {
+            return false;
+        }
+
+        // We know that candidateRect is a candidate. If currentBest is not
+        // a candidate, candidateRect is better.
+        if (!isCandidate(source, currentBest, direction)) {
+            return true;
+        }
+
+        // If candidateRect is better by beam, it wins.
+        if (beamBeats(direction, source, candidate, currentBest)) {
+            return true;
+        }
+
+        // If currentBest is better, then candidateRect cant' be. :)
+        if (beamBeats(direction, source, currentBest, candidate)) {
+            return false;
+        }
+
+        // Otherwise, do fudge-tastic comparison of the major and minor
+        // axis.
+        final int candidateDist = getWeightedDistanceFor(
+                majorAxisDistance(direction, source, candidate),
+                minorAxisDistance(direction, source, candidate));
+        final int currentBestDist = getWeightedDistanceFor(
+                majorAxisDistance(direction, source, currentBest),
+                minorAxisDistance(direction, source, currentBest));
+        return candidateDist < currentBestDist;
+    }
+
+    /**
+     * One rectangle may be another candidate than another by virtue of
+     * being exclusively in the beam of the source rect.
+     *
+     * @return whether rect1 is a better candidate than rect2 by virtue of
+     * it being in source's beam
+     */
+    private static boolean beamBeats(@FocusRealDirection int direction,
+            @NonNull Rect source, @NonNull Rect rect1, @NonNull Rect rect2) {
+        final boolean rect1InSrcBeam = beamsOverlap(direction, source, rect1);
+        final boolean rect2InSrcBeam = beamsOverlap(direction, source, rect2);
+
+        // If rect1 isn't exclusively in the src beam, it doesn't win.
+        if (rect2InSrcBeam || !rect1InSrcBeam) {
+            return false;
+        }
+
+        // We know rect1 is in the beam, and rect2 is not.
+
+        // If rect1 is to the direction of, and rect2 is not, rect1 wins.
+        // For example, for direction left, if rect1 is to the left of the
+        // source and rect2 is below, then we always prefer the in beam
+        // rect1, since rect2 could be reached by going down.
+        if (!isToDirectionOf(direction, source, rect2)) {
+            return true;
+        }
+
+        // For horizontal directions, being exclusively in beam always
+        // wins.
+        if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {
+            return true;
+        }
+
+        // For vertical directions, beams only beat up to a point: now, as
+        // long as rect2 isn't completely closer, rect1 wins, e.g. for
+        // direction down, completely closer means for rect2's top edge to
+        // be closer to the source's top edge than rect1's bottom edge.
+        return majorAxisDistance(direction, source, rect1)
+                < majorAxisDistanceToFarEdge(direction, source, rect2);
+    }
+
+    /**
+     * Fudge-factor opportunity: how to calculate distance given major and
+     * minor axis distances.
+     * <p/>
+     * Warning: this fudge factor is finely tuned, be sure to run all focus
+     * tests if you dare tweak it.
+     */
+    private static int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
+        return 13 * majorAxisDistance * majorAxisDistance
+                + minorAxisDistance * minorAxisDistance;
+    }
+
+    /**
+     * Is destRect a candidate for the next focus given the direction? This
+     * checks whether the dest is at least partially to the direction of
+     * (e.g. left of) from source.
+     * <p/>
+     * Includes an edge case for an empty rect,which is used in some cases
+     * when searching from a point on the screen.
+     */
+    private static boolean isCandidate(@NonNull Rect srcRect, @NonNull Rect destRect,
+            @FocusRealDirection int direction) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return (srcRect.right > destRect.right || srcRect.left >= destRect.right)
+                        && srcRect.left > destRect.left;
+            case View.FOCUS_RIGHT:
+                return (srcRect.left < destRect.left || srcRect.right <= destRect.left)
+                        && srcRect.right < destRect.right;
+            case View.FOCUS_UP:
+                return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom)
+                        && srcRect.top > destRect.top;
+            case View.FOCUS_DOWN:
+                return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top)
+                        && srcRect.bottom < destRect.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+
+    /**
+     * Do the "beams" w.r.t the given direction's axis of rect1 and rect2 overlap?
+     *
+     * @param direction the direction (up, down, left, right)
+     * @param rect1     the first rectangle
+     * @param rect2     the second rectangle
+     * @return whether the beams overlap
+     */
+    private static boolean beamsOverlap(@FocusRealDirection int direction,
+            @NonNull Rect rect1, @NonNull Rect rect2) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+            case View.FOCUS_RIGHT:
+                return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);
+            case View.FOCUS_UP:
+            case View.FOCUS_DOWN:
+                return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * e.g for left, is 'to left of'
+     */
+    private static boolean isToDirectionOf(@FocusRealDirection int direction,
+            @NonNull Rect src, @NonNull Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return src.left >= dest.right;
+            case View.FOCUS_RIGHT:
+                return src.right <= dest.left;
+            case View.FOCUS_UP:
+                return src.top >= dest.bottom;
+            case View.FOCUS_DOWN:
+                return src.bottom <= dest.top;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * @return the distance from the edge furthest in the given direction
+     * of source to the edge nearest in the given direction of
+     * dest. If the dest is not in the direction from source,
+     * returns 0.
+     */
+    private static int majorAxisDistance(@FocusRealDirection int direction,
+            @NonNull Rect source, @NonNull Rect dest) {
+        return Math.max(0, majorAxisDistanceRaw(direction, source, dest));
+    }
+
+    private static int majorAxisDistanceRaw(@FocusRealDirection int direction,
+            @NonNull Rect source, @NonNull Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return source.left - dest.right;
+            case View.FOCUS_RIGHT:
+                return dest.left - source.right;
+            case View.FOCUS_UP:
+                return source.top - dest.bottom;
+            case View.FOCUS_DOWN:
+                return dest.top - source.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * @return the distance along the major axis w.r.t the direction from
+     * the edge of source to the far edge of dest. If the dest is
+     * not in the direction from source, returns 1 to break ties
+     * with {@link #majorAxisDistance}.
+     */
+    private static int majorAxisDistanceToFarEdge(@FocusRealDirection int direction,
+            @NonNull Rect source, @NonNull Rect dest) {
+        return Math.max(1, majorAxisDistanceToFarEdgeRaw(direction, source, dest));
+    }
+
+    private static int majorAxisDistanceToFarEdgeRaw(
+            @FocusRealDirection int direction, @NonNull Rect source,
+            @NonNull Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return source.left - dest.left;
+            case View.FOCUS_RIGHT:
+                return dest.right - source.right;
+            case View.FOCUS_UP:
+                return source.top - dest.top;
+            case View.FOCUS_DOWN:
+                return dest.bottom - source.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * Finds the distance on the minor axis w.r.t the direction to the
+     * nearest edge of the destination rectangle.
+     *
+     * @param direction the direction (up, down, left, right)
+     * @param source the source rect
+     * @param dest the destination rect
+     * @return the distance
+     */
+    private static int minorAxisDistance(@FocusRealDirection int direction, @NonNull Rect source,
+            @NonNull Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+            case View.FOCUS_RIGHT:
+                // the distance between the center verticals
+                return Math.abs(
+                        ((source.top + source.height() / 2) -
+                                ((dest.top + dest.height() / 2))));
+            case View.FOCUS_UP:
+            case View.FOCUS_DOWN:
+                // the distance between the center horizontals
+                return Math.abs(
+                        ((source.left + source.width() / 2) -
+                                ((dest.left + dest.width() / 2))));
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * Adapter used to obtain bounds from a generic data type.
+     */
+    public interface BoundsAdapter<T> {
+        void obtainBounds(T data, Rect outBounds);
+    }
+
+    /**
+     * Adapter used to obtain items from a generic collection type.
+     */
+    public interface CollectionAdapter<T, V> {
+        V get(T collection, int index);
+        int size(T collection);
+    }
+}
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index 1bfb318..11d707f 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -377,6 +377,7 @@
     field public static int alertDialogStyle;
     field public static int alertDialogTheme;
     field public static int allowStacking;
+    field public static int alpha;
     field public static int arrowHeadLength;
     field public static int arrowShaftLength;
     field public static int autoCompleteTextViewStyle;
@@ -392,6 +393,7 @@
     field public static int buttonBarNeutralButtonStyle;
     field public static int buttonBarPositiveButtonStyle;
     field public static int buttonBarStyle;
+    field public static int buttonGravity;
     field public static int buttonPanelSideLayout;
     field public static int buttonStyle;
     field public static int buttonStyleSmall;
@@ -525,11 +527,12 @@
     field public static int thickness;
     field public static int thumbTextPadding;
     field public static int title;
+    field public static int titleMargin;
     field public static int titleMarginBottom;
     field public static int titleMarginEnd;
     field public static int titleMarginStart;
     field public static int titleMarginTop;
-    field public static int titleMargins;
+    field public static deprecated int titleMargins;
     field public static int titleTextAppearance;
     field public static int titleTextColor;
     field public static int titleTextStyle;
@@ -552,11 +555,8 @@
   public static final class R.bool {
     ctor public R.bool();
     field public static int abc_action_bar_embed_tabs;
-    field public static int abc_action_bar_embed_tabs_pre_jb;
-    field public static int abc_action_bar_expanded_action_views_exclusive;
     field public static int abc_allow_stacked_button_bar;
     field public static int abc_config_actionMenuItemAllCaps;
-    field public static int abc_config_allowActionMenuItemTextWithIcon;
     field public static int abc_config_closeDialogWhenTouchOutside;
     field public static int abc_config_showMenuShortcutsWhenKeyboardPresent;
   }
@@ -577,6 +577,13 @@
     field public static int abc_search_url_text_selected;
     field public static int abc_secondary_text_material_dark;
     field public static int abc_secondary_text_material_light;
+    field public static int abc_tint_btn_checkable;
+    field public static int abc_tint_default;
+    field public static int abc_tint_edittext;
+    field public static int abc_tint_seek_thumb;
+    field public static int abc_tint_spinner;
+    field public static int abc_tint_switch_thumb;
+    field public static int abc_tint_switch_track;
     field public static int accent_material_dark;
     field public static int accent_material_light;
     field public static int background_floating_material_dark;
@@ -682,7 +689,6 @@
     field public static int abc_list_item_padding_horizontal_material;
     field public static int abc_panel_menu_list_width;
     field public static int abc_search_view_preferred_width;
-    field public static int abc_search_view_text_min_width;
     field public static int abc_seekbar_track_background_height_material;
     field public static int abc_seekbar_track_progress_height_material;
     field public static int abc_select_dialog_padding_start_material;
@@ -815,6 +821,7 @@
     field public static int alertTitle;
     field public static int always;
     field public static int beginning;
+    field public static int bottom;
     field public static int buttonPanel;
     field public static int cancel_action;
     field public static int checkbox;
@@ -884,6 +891,7 @@
     field public static int time;
     field public static int title;
     field public static int title_template;
+    field public static int top;
     field public static int topPanel;
     field public static int up;
     field public static int useLogo;
@@ -895,7 +903,6 @@
     ctor public R.integer();
     field public static int abc_config_activityDefaultDur;
     field public static int abc_config_activityShortDur;
-    field public static int abc_max_action_buttons;
     field public static int cancel_button_image_alpha;
     field public static int status_bar_notification_info_maxnum;
   }
@@ -1346,6 +1353,10 @@
     field public static int AppCompatTextView_android_textAppearance;
     field public static int AppCompatTextView_textAllCaps;
     field public static int ButtonBarLayout_allowStacking;
+    field public static final int[] ColorStateListItem;
+    field public static int ColorStateListItem_alpha;
+    field public static int ColorStateListItem_android_alpha;
+    field public static int ColorStateListItem_android_color;
     field public static final int[] CompoundButton;
     field public static int CompoundButton_android_button;
     field public static int CompoundButton_buttonTint;
@@ -1574,9 +1585,10 @@
     field public static int Theme_windowMinWidthMajor;
     field public static int Theme_windowMinWidthMinor;
     field public static int Theme_windowNoTitle;
-    field public static final int[] Toolbar;
+    field public static final deprecated int[] Toolbar;
     field public static int Toolbar_android_gravity;
     field public static int Toolbar_android_minHeight;
+    field public static int Toolbar_buttonGravity;
     field public static int Toolbar_collapseContentDescription;
     field public static int Toolbar_collapseIcon;
     field public static int Toolbar_contentInsetEnd;
@@ -1593,11 +1605,12 @@
     field public static int Toolbar_subtitleTextAppearance;
     field public static int Toolbar_subtitleTextColor;
     field public static int Toolbar_title;
+    field public static int Toolbar_titleMargin;
     field public static int Toolbar_titleMarginBottom;
     field public static int Toolbar_titleMarginEnd;
     field public static int Toolbar_titleMarginStart;
     field public static int Toolbar_titleMarginTop;
-    field public static int Toolbar_titleMargins;
+    field public static deprecated int Toolbar_titleMargins;
     field public static int Toolbar_titleTextAppearance;
     field public static int Toolbar_titleTextColor;
     field public static final int[] View;
@@ -2059,6 +2072,10 @@
     method public int getPopupTheme();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
+    method public int getTitleMarginBottom();
+    method public int getTitleMarginEnd();
+    method public int getTitleMarginStart();
+    method public int getTitleMarginTop();
     method public boolean hasExpandedActionView();
     method public boolean hideOverflowMenu();
     method public void inflateMenu(int);
@@ -2084,6 +2101,11 @@
     method public void setSubtitleTextColor(int);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
+    method public void setTitleMargin(int, int, int, int);
+    method public void setTitleMarginBottom(int);
+    method public void setTitleMarginEnd(int);
+    method public void setTitleMarginStart(int);
+    method public void setTitleMarginTop(int);
     method public void setTitleTextAppearance(android.content.Context, int);
     method public void setTitleTextColor(int);
     method public boolean showOverflowMenu();
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index fdb7ff5..b4985e5 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -50,6 +50,7 @@
      <public type="attr" name="actionViewClass"/>
      <public type="attr" name="alertDialogStyle"/>
      <public type="attr" name="alertDialogTheme"/>
+     <public type="attr" name="alpha"/>
      <public type="attr" name="arrowHeadLength"/>
      <public type="attr" name="arrowShaftLength"/>
      <public type="attr" name="autoCompleteTextViewStyle"/>
@@ -65,6 +66,7 @@
      <public type="attr" name="buttonBarNeutralButtonStyle"/>
      <public type="attr" name="buttonBarPositiveButtonStyle"/>
      <public type="attr" name="buttonBarStyle"/>
+     <public type="attr" name="buttonGravity" />
      <public type="attr" name="buttonStyle"/>
      <public type="attr" name="buttonStyleSmall"/>
      <public type="attr" name="buttonTint"/>
@@ -126,6 +128,7 @@
      <public type="attr" name="listPreferredItemPaddingRight"/>
      <public type="attr" name="logo"/>
      <public type="attr" name="logoDescription"/>
+     <public type="attr" name="maxButtonHeight" />
      <public type="attr" name="measureWithLargestChild"/>
      <public type="attr" name="middleBarArrowSize"/>
      <public type="attr" name="navigationContentDescription"/>
@@ -184,6 +187,7 @@
      <public type="attr" name="title"/>
      <public type="attr" name="titleMarginBottom"/>
      <public type="attr" name="titleMarginEnd"/>
+     <public type="attr" name="titleMargin"/>
      <public type="attr" name="titleMargins"/>
      <public type="attr" name="titleMarginStart"/>
      <public type="attr" name="titleMarginTop"/>
diff --git a/v7/appcompat/res/color/abc_tint_btn_checkable.xml b/v7/appcompat/res/color/abc_tint_btn_checkable.xml
new file mode 100644
index 0000000..0c663f6
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_btn_checkable.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+    <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+    <item android:color="?attr/colorControlNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_default.xml b/v7/appcompat/res/color/abc_tint_default.xml
new file mode 100644
index 0000000..8d7c391
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_default.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+    <item android:state_focused="true" android:color="?attr/colorControlActivated"/>
+    <item android:state_pressed="true" android:color="?attr/colorControlActivated"/>
+    <item android:state_activated="true" android:color="?attr/colorControlActivated"/>
+    <item android:state_selected="true" android:color="?attr/colorControlActivated"/>
+    <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+    <item android:color="?attr/colorControlNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_edittext.xml b/v7/appcompat/res/color/abc_tint_edittext.xml
new file mode 100644
index 0000000..536d77f
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_edittext.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorControlNormal"
+          app:alpha="?android:disabledAlpha"/>
+    <item android:state_pressed="false" android:state_focused="false"
+          android:color="?attr/colorControlNormal"/>
+    <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w480dp/bools.xml b/v7/appcompat/res/color/abc_tint_seek_thumb.xml
similarity index 61%
rename from v7/appcompat/res/values-w480dp/bools.xml
rename to v7/appcompat/res/color/abc_tint_seek_thumb.xml
index 470f89b..cb53788 100644
--- a/v7/appcompat/res/values-w480dp/bools.xml
+++ b/v7/appcompat/res/color/abc_tint_seek_thumb.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!--
+     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.
@@ -13,6 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
-    <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorControlActivated" app:alpha="?android:attr/disabledAlpha"/>
+    <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_spinner.xml b/v7/appcompat/res/color/abc_tint_spinner.xml
new file mode 100644
index 0000000..44333dd
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_spinner.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorControlNormal" app:alpha="?android:disabledAlpha"/>
+    <item android:state_pressed="false" android:state_focused="false" android:color="?attr/colorControlNormal"/>
+    <item android:color="?attr/colorControlActivated"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_switch_thumb.xml b/v7/appcompat/res/color/abc_tint_switch_thumb.xml
new file mode 100644
index 0000000..fc8bd24
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_switch_thumb.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?attr/colorSwitchThumbNormal" app:alpha="?android:attr/disabledAlpha"/>
+    <item android:state_checked="true" android:color="?attr/colorControlActivated"/>
+    <item android:color="?attr/colorSwitchThumbNormal"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/color/abc_tint_switch_track.xml b/v7/appcompat/res/color/abc_tint_switch_track.xml
new file mode 100644
index 0000000..22322f8
--- /dev/null
+++ b/v7/appcompat/res/color/abc_tint_switch_track.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_enabled="false" android:color="?android:attr/colorForeground" app:alpha="0.1"/>
+    <item android:state_checked="true" android:color="?attr/colorControlActivated" app:alpha="0.3"/>
+    <item android:color="?android:attr/colorForeground" app:alpha="0.3"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
index d32ad10..08adfd1 100644
--- a/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
+++ b/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml
@@ -28,7 +28,6 @@
     android:paddingTop="4dp"
     android:paddingBottom="4dp"
     android:gravity="bottom"
-    app:allowStacking="@bool/abc_allow_stacked_button_bar"
     style="?attr/buttonBarStyle">
 
     <Button
diff --git a/v7/appcompat/res/layout/abc_search_view.xml b/v7/appcompat/res/layout/abc_search_view.xml
index a7446e3..b496f5d 100644
--- a/v7/appcompat/res/layout/abc_search_view.xml
+++ b/v7/appcompat/res/layout/abc_search_view.xml
@@ -80,7 +80,6 @@
                   android:layout_height="36dip"
                   android:layout_width="0dp"
                   android:layout_weight="1"
-                  android:minWidth="@dimen/abc_search_view_text_min_width"
                   android:layout_gravity="bottom"
                   android:paddingLeft="@dimen/abc_dropdownitem_text_padding_left"
                   android:paddingRight="@dimen/abc_dropdownitem_text_padding_right"
diff --git a/v7/appcompat/res/values-b+sr+Latn/strings.xml b/v7/appcompat/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..fc92231
--- /dev/null
+++ b/v7/appcompat/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gotovo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Odlazak na Početnu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Kretanje nagore"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Još opcija"</string>
+    <string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Skupi"</string>
+    <string name="abc_action_bar_home_description_format" msgid="1397052879051804371">"%1$s, %2$s"</string>
+    <string name="abc_action_bar_home_subtitle_description_format" msgid="6623331958280229229">"%1$s, %2$s, %3$s"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pretraga"</string>
+    <string name="abc_search_hint" msgid="7723749260725869598">"Pretražite..."</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Upit za pretragu"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Brisanje upita"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Slanje upita"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Glasovna pretraga"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Izbor aplikacije"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Prikaži sve"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Deli sa aplikacijom %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Deli sa"</string>
+    <string name="status_bar_notification_info_overflow" msgid="2869576371154716097">"&gt;999"</string>
+    <string name="abc_capital_on" msgid="3405795526292276155">"UKLJUČI"</string>
+    <string name="abc_capital_off" msgid="121134116657445385">"ISKLJUČI"</string>
+</resources>
diff --git a/v7/appcompat/res/values-land/bools.xml b/v7/appcompat/res/values-land/bools.xml
deleted file mode 100644
index 7d1a1af..0000000
--- a/v7/appcompat/res/values-land/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-land/config.xml b/v7/appcompat/res/values-land/config.xml
deleted file mode 100644
index d0d990d..0000000
--- a/v7/appcompat/res/values-land/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-large/bools.xml b/v7/appcompat/res/values-large/bools.xml
deleted file mode 100644
index 7d1a1af..0000000
--- a/v7/appcompat/res/values-large/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <bool name="abc_action_bar_embed_tabs_pre_jb">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-large/config.xml b/v7/appcompat/res/values-large/config.xml
index c4f04a3..58e34a0 100644
--- a/v7/appcompat/res/values-large/config.xml
+++ b/v7/appcompat/res/values-large/config.xml
@@ -20,11 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Whether action menu items should obey the "withText" showAsAction.
-         This may be set to false for situations where space is
-         extremely limited. -->
-    <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-
     <!-- see comment in values/config.xml -->
     <dimen name="abc_config_prefDialogWidth">440dp</dimen>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v7/appcompat/res/values-large/dimens.xml b/v7/appcompat/res/values-large/dimens.xml
index 16bb4f6..5afdda4 100644
--- a/v7/appcompat/res/values-large/dimens.xml
+++ b/v7/appcompat/res/values-large/dimens.xml
@@ -15,13 +15,6 @@
 -->
 
 <resources>
-    <!-- Minimum width of the search view text entry area. -->
-    <dimen name="abc_search_view_text_min_width">192dip</dimen>
-    <!-- The maximum number of action buttons that should be permitted within
-     an action bar/action mode. This will be used to determine how many
-     showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">4</integer>
-
     <item type="dimen" name="abc_dialog_fixed_width_major">60%</item>
     <item type="dimen" name="abc_dialog_fixed_width_minor">90%</item>
     <item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
diff --git a/v7/appcompat/res/values-sw600dp/dimens.xml b/v7/appcompat/res/values-sw600dp/dimens.xml
index a4bc455..a83abb1 100644
--- a/v7/appcompat/res/values-sw600dp/dimens.xml
+++ b/v7/appcompat/res/values-sw600dp/dimens.xml
@@ -15,12 +15,6 @@
 -->
 
 <resources>
-
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">5</integer>
-
     <!-- Use the default title sizes on tablets. -->
     <dimen name="abc_text_size_title_material_toolbar">20dp</dimen>
     <!-- Use the default subtitle sizes on tablets. -->
diff --git a/v7/appcompat/res/values-tr/strings.xml b/v7/appcompat/res/values-tr/strings.xml
index 56aecf6..185cd4d 100644
--- a/v7/appcompat/res/values-tr/strings.xml
+++ b/v7/appcompat/res/values-tr/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="abc_action_mode_done" msgid="4076576682505996667">"Tamamlandı"</string>
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Bitti"</string>
     <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ana ekrana git"</string>
     <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Yukarı git"</string>
     <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Diğer seçenekler"</string>
diff --git a/v7/appcompat/res/values-w360dp/dimens.xml b/v7/appcompat/res/values-w360dp/dimens.xml
deleted file mode 100644
index e5b2456..0000000
--- a/v7/appcompat/res/values-w360dp/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">3</integer>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w480dp/config.xml b/v7/appcompat/res/values-w480dp/config.xml
deleted file mode 100644
index e95b6ff..0000000
--- a/v7/appcompat/res/values-w480dp/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
-    <bool name="abc_config_allowActionMenuItemTextWithIcon">true</bool>
-</resources>
diff --git a/v7/appcompat/res/values-w500dp/dimens.xml b/v7/appcompat/res/values-w500dp/dimens.xml
deleted file mode 100644
index dd6458b..0000000
--- a/v7/appcompat/res/values-w500dp/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">4</integer>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w600dp/dimens.xml b/v7/appcompat/res/values-w600dp/dimens.xml
deleted file mode 100644
index 252ba6a..0000000
--- a/v7/appcompat/res/values-w600dp/dimens.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">5</integer>
-
-    <!-- Minimum width of the search view text entry area. -->
-    <dimen name="abc_search_view_text_min_width">192dip</dimen>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-w720dp/bools.xml b/v7/appcompat/res/values-w720dp/bools.xml
deleted file mode 100644
index 05c5aab..0000000
--- a/v7/appcompat/res/values-w720dp/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <bool name="abc_action_bar_expanded_action_views_exclusive">false</bool>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-xlarge-land/dimens.xml b/v7/appcompat/res/values-xlarge-land/dimens.xml
deleted file mode 100644
index dea6c74..0000000
--- a/v7/appcompat/res/values-xlarge-land/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-
-    <!-- Minimum width of the search view text entry area. -->
-    <dimen name="abc_search_view_text_min_width">256dip</dimen>
-
-</resources>
diff --git a/v7/appcompat/res/values-xlarge/bools.xml b/v7/appcompat/res/values-xlarge/bools.xml
deleted file mode 100644
index 05c5aab..0000000
--- a/v7/appcompat/res/values-xlarge/bools.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <bool name="abc_action_bar_expanded_action_views_exclusive">false</bool>
-</resources>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-xlarge/dimens.xml b/v7/appcompat/res/values-xlarge/dimens.xml
index 0dd244a..f0d560d 100644
--- a/v7/appcompat/res/values-xlarge/dimens.xml
+++ b/v7/appcompat/res/values-xlarge/dimens.xml
@@ -15,15 +15,6 @@
 -->
 
 <resources>
-
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">5</integer>
-
-    <!-- Minimum width of the search view text entry area. -->
-    <dimen name="abc_search_view_text_min_width">192dip</dimen>
-
     <item type="dimen" name="abc_dialog_fixed_width_major">50%</item>
     <item type="dimen" name="abc_dialog_fixed_width_minor">70%</item>
     <item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index f3e496e..a2360aa 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -801,17 +801,39 @@
         <attr name="title" />
         <attr name="subtitle" />
         <attr name="android:gravity" />
-        <attr name="titleMargins" format="dimension" />
+        <!--  Specifies extra space on the left, start, right and end sides
+              of the toolbar's title. Margin values should be positive. -->
+        <attr name="titleMargin" format="dimension" />
+        <!--  Specifies extra space on the start side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginStart" format="dimension" />
+        <!--  Specifies extra space on the end side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginEnd" format="dimension" />
+        <!--  Specifies extra space on the top side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginTop" format="dimension" />
+        <!--  Specifies extra space on the bottom side of the toolbar's title.
+              If both this attribute and titleMargin are specified, then this
+              attribute takes precedence. Margin values should be positive. -->
         <attr name="titleMarginBottom" format="dimension" />
+        <!-- {@deprecated Use titleMargin} -->
+        <attr name="titleMargins" format="dimension" />
         <attr name="contentInsetStart" />
         <attr name="contentInsetEnd" />
         <attr name="contentInsetLeft" />
         <attr name="contentInsetRight" />
         <attr name="maxButtonHeight" format="dimension" />
-
+        <attr name="buttonGravity">
+            <!-- Push object to the top of its container, not changing its size. -->
+            <flag name="top" value="0x30" />
+            <!-- Push object to the bottom of its container, not changing its size. -->
+            <flag name="bottom" value="0x50" />
+        </attr>
+        <!-- Icon drawable to use for the collapse button. -->
         <attr name="collapseIcon" format="reference" />
         <!-- Text to set as the content description for the collapse button. -->
         <attr name="collapseContentDescription" format="string" />
@@ -824,10 +846,6 @@
         <!-- Text to set as the content description for the navigation button
              located at the start of the toolbar. -->
         <attr name="navigationContentDescription" format="string" />
-
-        <!-- Allows us to read in the minHeight attr pre-v16 -->
-        <attr name="android:minHeight" />
-
         <!-- Drawable to set as the logo that appears at the starting side of
              the Toolbar, just after the navigation button. -->
         <attr name="logo" />
@@ -838,6 +856,7 @@
         <attr name="titleTextColor" format="color" />
         <!-- A color to apply to the subtitle string. -->
         <attr name="subtitleTextColor" format="color" />
+        <attr name="android:minHeight" />
     </declare-styleable>
 
     <declare-styleable name="PopupWindowBackgroundState">
@@ -965,6 +984,15 @@
         <attr name="allowStacking" format="boolean" />
     </declare-styleable>
 
+    <!-- Attributes that can be assigned to a ColorStateList item. -->
+    <declare-styleable name="ColorStateListItem">
+        <!-- Base color for this state. -->
+        <attr name="android:color" />
+        <!-- Alpha multiplier applied to the base color. -->
+        <attr name="alpha" format="float" />
+        <attr name="android:alpha"/>
+    </declare-styleable>
+
     <declare-styleable name="AppCompatImageView">
         <attr name="android:src"/>
         <!-- TODO -->
diff --git a/v7/appcompat/res/values/bools.xml b/v7/appcompat/res/values/bools.xml
index 33d4948..825ece0 100644
--- a/v7/appcompat/res/values/bools.xml
+++ b/v7/appcompat/res/values/bools.xml
@@ -17,8 +17,6 @@
 <resources>
 
     <bool name="abc_action_bar_embed_tabs">true</bool>
-    <bool name="abc_action_bar_embed_tabs_pre_jb">false</bool>
-    <bool name="abc_action_bar_expanded_action_views_exclusive">true</bool>
 
     <bool name="abc_config_showMenuShortcutsWhenKeyboardPresent">false</bool>
 
diff --git a/v7/appcompat/res/values/config.xml b/v7/appcompat/res/values/config.xml
index e0c521b..6d986ea 100644
--- a/v7/appcompat/res/values/config.xml
+++ b/v7/appcompat/res/values/config.xml
@@ -17,11 +17,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Whether action menu items should obey the "withText" showAsAction
-    flag. This may be set to false for situations where space is
-    extremely limited. -->
-    <bool name="abc_config_allowActionMenuItemTextWithIcon">false</bool>
-
     <!-- The maximum width we would prefer dialogs to be.  0 if there is no
     maximum (let them grow as large as the screen).  Actual values are
     specified for -large and -xlarge configurations. -->
@@ -45,4 +40,4 @@
     <integer name="status_bar_notification_info_maxnum">999</integer>
 
     <integer name="cancel_button_image_alpha">127</integer>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index 37130e3..3eeeb5d 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -15,12 +15,6 @@
 -->
 
 <resources>
-
-    <!-- The maximum number of action buttons that should be permitted within
-         an action bar/action mode. This will be used to determine how many
-         showAsAction="ifRoom" items can fit. "always" items can override this. -->
-    <integer name="abc_max_action_buttons">2</integer>
-
     <!-- Maximum width for a stacked action bar tab. This prevents
          action bar tabs from becoming too wide on a wide screen when only
          a few are present. -->
@@ -33,8 +27,6 @@
 
     <dimen name="abc_panel_menu_list_width">296dp</dimen>
 
-    <!-- Minimum width of the search view text entry area. -->
-    <dimen name="abc_search_view_text_min_width">160dip</dimen>
     <!-- Preferred width of the search view. -->
     <dimen name="abc_search_view_preferred_width">320dip</dimen>
 
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index 90a7486..efedc27 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -300,8 +300,9 @@
         <item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
         <item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
         <item name="android:minHeight">?attr/actionBarSize</item>
-        <item name="titleMargins">4dp</item>
+        <item name="titleMargin">4dp</item>
         <item name="maxButtonHeight">@dimen/abc_action_bar_default_height_material</item>
+        <item name="buttonGravity">top</item>
         <item name="collapseIcon">?attr/homeAsUpIndicator</item>
         <item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
         <item name="contentInsetStart">16dp</item>
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 734f46f..353856f 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -491,16 +491,15 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        final int keyCode = event.getKeyCode();
-        if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
+        if (event.isCtrlPressed() &&
+                event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
+            // Capture the Control-< and send focus to the ActionBar
             final int action = event.getAction();
             if (action == KeyEvent.ACTION_DOWN) {
-                if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                    final ActionBar actionBar = getSupportActionBar();
-                    if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
-                        mEatKeyUpEvent = true;
-                        return true;
-                    }
+                final ActionBar actionBar = getSupportActionBar();
+                if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
+                    mEatKeyUpEvent = true;
+                    return true;
                 }
             } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
                 mEatKeyUpEvent = false;
diff --git a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
index 0e50cc1..af0736a 100644
--- a/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
+++ b/v7/appcompat/src/android/support/v7/view/ActionBarPolicy.java
@@ -17,6 +17,7 @@
 package android.support.v7.view;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
@@ -42,8 +43,34 @@
         mContext = context;
     }
 
+    /**
+     * Returns the maximum number of action buttons that should be permitted within an action
+     * bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit.
+     * "always" items can override this.
+     */
     public int getMaxActionButtons() {
-        return mContext.getResources().getInteger(R.integer.abc_max_action_buttons);
+        final Configuration config = mContext.getResources().getConfiguration();
+        final int width = config.screenWidthDp;
+        final int height = config.screenHeightDp;
+        final int smallest;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+            smallest = config.smallestScreenWidthDp;
+        } else {
+            smallest = 0;
+        }
+        if (smallest > 600 || width > 600 || (width > 960 && height > 720)
+                || (width > 720 && height > 960)) {
+            // For values-w600dp, values-sw600dp and values-xlarge.
+            return 5;
+        } else if (width >= 500 || (width > 640 && height > 480) || (width > 480 && height > 640)) {
+            // For values-w500dp and values-large.
+            return 4;
+        } else if (width >= 360) {
+            // For values-w360dp.
+            return 3;
+        } else {
+            return 2;
+        }
     }
 
     public boolean showsOverflowMenuButton() {
@@ -66,7 +93,11 @@
 
         // The embedded tabs policy changed in Jellybean; give older apps the old policy
         // so they get what they expect.
-        return mContext.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs_pre_jb);
+        final Configuration configuration = mContext.getResources().getConfiguration();
+        final int width = configuration.screenWidthDp;
+        final int height = configuration.screenHeightDp;
+        return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE ||
+                width >= 480 || (width >= 640 && height >= 480);
     }
 
     public int getTabContainerHeight() {
diff --git a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
index 45278bc..ade266f 100644
--- a/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/WindowCallbackWrapper.java
@@ -18,6 +18,7 @@
 
 import android.view.ActionMode;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -27,6 +28,8 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.List;
+
 /**
  * A simple decorator stub for Window.Callback that passes through any calls
  * to the wrapped instance as a base implementation. Call super.foo() to call into
@@ -159,4 +162,9 @@
     public void onActionModeFinished(ActionMode mode) {
         mWrapped.onActionModeFinished(mode);
     }
+
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+        mWrapped.onProvideKeyboardShortcuts(data, menu);
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
index 0b80cf2..c03226c 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItemView.java
@@ -71,8 +71,7 @@
     public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         final Resources res = context.getResources();
-        mAllowTextWithIcon = res.getBoolean(
-                R.bool.abc_config_allowActionMenuItemTextWithIcon);
+        mAllowTextWithIcon = shouldAllowTextWithIcon();
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.ActionMenuItemView, defStyle, 0);
         mMinWidth = a.getDimensionPixelSize(
@@ -93,11 +92,22 @@
             super.onConfigurationChanged(newConfig);
         }
 
-        mAllowTextWithIcon = getContext().getResources().getBoolean(
-                R.bool.abc_config_allowActionMenuItemTextWithIcon);
+        mAllowTextWithIcon = shouldAllowTextWithIcon();
         updateTextButtonVisibility();
     }
 
+    /**
+     * Whether action menu items should obey the "withText" showAsAction flag. This may be set to
+     * false for situations where space is extremely limited. -->
+     */
+    private boolean shouldAllowTextWithIcon() {
+        final Configuration configuration = getContext().getResources().getConfiguration();
+        final int width = configuration.screenWidthDp;
+        final int height = configuration.screenHeightDp;
+        return  width >= 480 || (width >= 640 && height >= 480)
+                || configuration.orientation == Configuration.ORIENTATION_LANDSCAPE;
+    }
+
     @Override
     public void setPadding(int l, int t, int r, int b) {
         mSavedPaddingLeft = l;
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
index b976655..ace7a42 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
@@ -1302,7 +1302,10 @@
         mCurrentMenuInfo = menuInfo;
     }
 
-    void setOptionalIconsVisible(boolean visible) {
+    /**
+     * @hide
+     */
+    public void setOptionalIconsVisible(boolean visible) {
         mOptionalIconsVisible = visible;
     }
 
diff --git a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
index e0e1a83..7f2a6b6 100644
--- a/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
+++ b/v7/appcompat/src/android/support/v7/widget/ActionMenuPresenter.java
@@ -132,8 +132,7 @@
 
     public void onConfigurationChanged(Configuration newConfig) {
         if (!mMaxItemsSet) {
-            mMaxItems = mContext.getResources().getInteger(
-                    R.integer.abc_max_action_buttons);
+            mMaxItems = ActionBarPolicy.get(mContext).getMaxActionButtons();
         }
         if (mMenu != null) {
             mMenu.onItemsChanged(true);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
index 62c2ab7..9f35443 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatBackgroundHelper.java
@@ -17,7 +17,6 @@
 package android.support.v7.widget;
 
 import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -43,7 +42,7 @@
     }
 
     void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        TypedArray a = mView.getContext().obtainStyledAttributes(attrs,
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
                 R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
         try {
             if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java b/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java
new file mode 100644
index 0000000..3024280
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatColorStateListInflater.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.appcompat.R;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.util.Xml;
+
+import java.io.IOException;
+
+class AppCompatColorStateListInflater {
+
+    private static final int DEFAULT_COLOR = Color.RED;
+
+    /**
+     * Creates a ColorStateList from an XML document using given a set of
+     * {@link Resources} and a {@link Theme}.
+     *
+     * @param r Resources against which the ColorStateList should be inflated.
+     * @param parser Parser for the XML document defining the ColorStateList.
+     * @param theme Optional theme to apply to the color state list, may be
+     *              {@code null}.
+     * @return A new color state list.
+     */
+    @NonNull
+    public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @Nullable Resources.Theme theme) throws XmlPullParserException, IOException {
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Seek parser to start tag.
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        return createFromXmlInner(r, parser, attrs, theme);
+    }
+
+    /**
+     * Create from inside an XML document. Called on a parser positioned at a
+     * tag in an XML document, tries to create a ColorStateList from that tag.
+     *
+     * @throws XmlPullParserException if the current tag is not &lt;selector>
+     * @return A new color state list for the current tag.
+     */
+    @NonNull
+    private static ColorStateList createFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+            @Nullable Resources.Theme theme)
+            throws XmlPullParserException, IOException {
+        final String name = parser.getName();
+        if (!name.equals("selector")) {
+            throw new XmlPullParserException(
+                    parser.getPositionDescription() + ": invalid color state list tag " + name);
+        }
+
+        return inflate(r, parser, attrs, theme);
+    }
+
+    /**
+     * Fill in this object based on the contents of an XML "selector" element.
+     */
+    private static ColorStateList inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
+            throws XmlPullParserException, IOException {
+        final int innerDepth = parser.getDepth() + 1;
+        int depth;
+        int type;
+        int defaultColor = DEFAULT_COLOR;
+
+        int[][] stateSpecList = new int[20][];
+        int[] colorList = new int[stateSpecList.length];
+        int listSize = 0;
+
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG || depth > innerDepth
+                    || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorStateListItem);
+            final int baseColor = a.getColor(R.styleable.ColorStateListItem_android_color,
+                    Color.MAGENTA);
+
+            float alphaMod = 1.0f;
+            if (a.hasValue(R.styleable.ColorStateListItem_android_alpha)) {
+                alphaMod = a.getFloat(R.styleable.ColorStateListItem_android_alpha, alphaMod);
+            } else if (a.hasValue(R.styleable.ColorStateListItem_alpha)) {
+                alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, alphaMod);
+            }
+
+            a.recycle();
+
+            // Parse all unrecognized attributes as state specifiers.
+            int j = 0;
+            final int numAttrs = attrs.getAttributeCount();
+            int[] stateSpec = new int[numAttrs];
+            for (int i = 0; i < numAttrs; i++) {
+                final int stateResId = attrs.getAttributeNameResource(i);
+                if (stateResId != android.R.attr.color && stateResId != android.R.attr.alpha
+                        && stateResId != R.attr.alpha) {
+                    // Unrecognized attribute, add to state set
+                    stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
+                            ? stateResId : -stateResId;
+                }
+            }
+            stateSpec = StateSet.trimStateSet(stateSpec, j);
+
+            // Apply alpha modulation. If we couldn't resolve the color or
+            // alpha yet, the default values leave us enough information to
+            // modulate again during applyTheme().
+            final int color = modulateColorAlpha(baseColor, alphaMod);
+            if (listSize == 0 || stateSpec.length == 0) {
+                defaultColor = color;
+            }
+
+            colorList = GrowingArrayUtils.append(colorList, listSize, color);
+            stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
+            listSize++;
+        }
+
+        int[] colors = new int[listSize];
+        int[][] stateSpecs = new int[listSize][];
+        System.arraycopy(colorList, 0, colors, 0, listSize);
+        System.arraycopy(stateSpecList, 0, stateSpecs, 0, listSize);
+
+        return new ColorStateList(stateSpecs, colors);
+    }
+
+    private static TypedArray obtainAttributes(Resources res, Resources.Theme theme,
+            AttributeSet set, int[] attrs) {
+        return theme == null ? res.obtainAttributes(set, attrs)
+                : theme.obtainStyledAttributes(set, attrs, 0, 0);
+    }
+
+    private static int modulateColorAlpha(int color, float alphaMod) {
+        return ColorUtils.setAlphaComponent(color, Math.round(Color.alpha(color) * alphaMod));
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index 6b38482..e8bd5b3 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -53,6 +53,7 @@
 import java.lang.ref.WeakReference;
 import java.util.WeakHashMap;
 
+import static android.support.v7.widget.ColorStateListUtils.getColorStateList;
 import static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
 import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
@@ -452,11 +453,11 @@
         if (tint == null) {
             // ...if the cache did not contain a color state list, try and create one
             if (resId == R.drawable.abc_edit_text_material) {
-                tint = createEditTextColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_edittext);
             } else if (resId == R.drawable.abc_switch_track_mtrl_alpha) {
-                tint = createSwitchTrackColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_switch_track);
             } else if (resId == R.drawable.abc_switch_thumb_material) {
-                tint = createSwitchThumbColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_switch_thumb);
             } else if (resId == R.drawable.abc_btn_default_mtrl_shape
                     || resId == R.drawable.abc_btn_borderless_material) {
                 tint = createDefaultButtonColorStateList(context);
@@ -464,15 +465,15 @@
                 tint = createColoredButtonColorStateList(context);
             } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
                     || resId == R.drawable.abc_spinner_textfield_background_material) {
-                tint = createSpinnerColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_spinner);
             } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
                 tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
             } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
-                tint = createDefaultColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_default);
             } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
-                tint = createCheckableButtonColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_btn_checkable);
             } else if (resId == R.drawable.abc_seekbar_thumb_material) {
-                tint = createSeekbarThumbColorStateList(context);
+                tint = getColorStateList(context, R.color.abc_tint_seek_thumb);
             }
 
             if (tint != null) {
@@ -503,164 +504,6 @@
         themeTints.append(resId, tintList);
     }
 
-    private ColorStateList createDefaultColorStateList(Context context) {
-        /**
-         * Generate the default color state list which uses the colorControl attributes.
-         * Order is important here. The default enabled state needs to go at the bottom.
-         */
-
-        final int colorControlNormal = getThemeAttrColor(context, R.attr.colorControlNormal);
-        final int colorControlActivated = getThemeAttrColor(context, R.attr.colorControlActivated);
-
-        final int[][] states = new int[7][];
-        final int[] colors = new int[7];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        states[i] = ThemeUtils.FOCUSED_STATE_SET;
-        colors[i] = colorControlActivated;
-        i++;
-
-        states[i] = ThemeUtils.ACTIVATED_STATE_SET;
-        colors[i] = colorControlActivated;
-        i++;
-
-        states[i] = ThemeUtils.PRESSED_STATE_SET;
-        colors[i] = colorControlActivated;
-        i++;
-
-        states[i] = ThemeUtils.CHECKED_STATE_SET;
-        colors[i] = colorControlActivated;
-        i++;
-
-        states[i] = ThemeUtils.SELECTED_STATE_SET;
-        colors[i] = colorControlActivated;
-        i++;
-
-        // Default enabled state
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = colorControlNormal;
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    private ColorStateList createCheckableButtonColorStateList(Context context) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        states[i] = ThemeUtils.CHECKED_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-        i++;
-
-        // Default enabled state
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    private ColorStateList createSwitchTrackColorStateList(Context context) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.1f);
-        i++;
-
-        states[i] = ThemeUtils.CHECKED_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated, 0.3f);
-        i++;
-
-        // Default enabled state
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = getThemeAttrColor(context, android.R.attr.colorForeground, 0.3f);
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    private ColorStateList createSwitchThumbColorStateList(Context context) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        final ColorStateList thumbColor = getThemeAttrColorStateList(context,
-                R.attr.colorSwitchThumbNormal);
-
-        if (thumbColor != null && thumbColor.isStateful()) {
-            // If colorSwitchThumbNormal is a valid ColorStateList, extract the default and
-            // disabled colors from it
-
-            // Disabled state
-            states[i] = ThemeUtils.DISABLED_STATE_SET;
-            colors[i] = thumbColor.getColorForState(states[i], 0);
-            i++;
-
-            states[i] = ThemeUtils.CHECKED_STATE_SET;
-            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-            i++;
-
-            // Default enabled state
-            states[i] = ThemeUtils.EMPTY_STATE_SET;
-            colors[i] = thumbColor.getDefaultColor();
-            i++;
-        } else {
-            // Else we'll use an approximation using the default disabled alpha
-
-            // Disabled state
-            states[i] = ThemeUtils.DISABLED_STATE_SET;
-            colors[i] = getDisabledThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
-            i++;
-
-            states[i] = ThemeUtils.CHECKED_STATE_SET;
-            colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-            i++;
-
-            // Default enabled state
-            states[i] = ThemeUtils.EMPTY_STATE_SET;
-            colors[i] = getThemeAttrColor(context, R.attr.colorSwitchThumbNormal);
-            i++;
-        }
-
-        return new ColorStateList(states, colors);
-    }
-
-    private ColorStateList createEditTextColorStateList(Context context) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        // Default enabled state
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
     private ColorStateList createDefaultButtonColorStateList(Context context) {
         return createButtonColorStateList(context, R.attr.colorButtonNormal);
     }
@@ -698,44 +541,6 @@
         return new ColorStateList(states, colors);
     }
 
-    private ColorStateList createSpinnerColorStateList(Context context) {
-        final int[][] states = new int[3][];
-        final int[] colors = new int[3];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        states[i] = ThemeUtils.NOT_PRESSED_OR_FOCUSED_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
-        i++;
-
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    private ColorStateList createSeekbarThumbColorStateList(Context context) {
-        final int[][] states = new int[2][];
-        final int[] colors = new int[2];
-        int i = 0;
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlActivated);
-        i++;
-
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
     private static class ColorFilterLruCache extends LruCache<Integer, PorterDuffColorFilter> {
 
         public ColorFilterLruCache(int maxSize) {
diff --git a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
index 1b8c9a5..6ddc1de 100644
--- a/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
+++ b/v7/appcompat/src/android/support/v7/widget/ButtonBarLayout.java
@@ -32,6 +32,9 @@
  * @hide
  */
 public class ButtonBarLayout extends LinearLayout {
+    // Whether to allow vertically stacked button bars. This is disabled for
+    // configurations with a small (e.g. less than 320dp) screen height. -->
+    private static final int ALLOW_STACKING_MIN_HEIGHT_DP = 320;
 
     /** Whether the current configuration allows stacking. */
     private boolean mAllowStacking;
@@ -39,8 +42,12 @@
 
     public ButtonBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
+        final boolean allowStackingDefault =
+                context.getResources().getConfiguration().screenHeightDp
+                        >= ALLOW_STACKING_MIN_HEIGHT_DP;
         final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
-        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
+        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking,
+                allowStackingDefault);
         ta.recycle();
     }
 
@@ -124,4 +131,4 @@
     private boolean isStacked() {
         return getOrientation() == LinearLayout.VERTICAL;
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java b/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java
new file mode 100644
index 0000000..d5ff6c9
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/ColorStateListUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.os.Build;
+import android.support.annotation.ColorRes;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.util.TypedValue;
+
+class ColorStateListUtils {
+
+    private static final String LOG_TAG = "ColorStateListUtils";
+    private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
+
+    private ColorStateListUtils() {}
+
+    /**
+     * Returns the {@link ColorStateList} from the given resource. The resource can include
+     * themeable attributes.
+     */
+    static ColorStateList getColorStateList(@NonNull Context context, @ColorRes int resId) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            // On M+ we can use the framework
+            return context.getColorStateList(resId);
+        }
+        // Before that, we'll try and inflate it manually
+        final ColorStateList csl = inflateColorStateList(context, resId);
+        if (csl != null) {
+            return csl;
+        }
+        // If we reach here then we couldn't inflate it, so let the framework handle it
+        return ContextCompat.getColorStateList(context, resId);
+    }
+
+    /**
+     * Inflates a {@link ColorStateList} from resources, honouring theme attributes.
+     */
+    private static ColorStateList inflateColorStateList(Context context, int resId) {
+        if (isColorInt(context, resId)) {
+            // The resource is a color int, we can't handle it so return null
+            return null;
+        }
+
+        final Resources r = context.getResources();
+        final XmlPullParser xml = r.getXml(resId);
+        try {
+            return AppCompatColorStateListInflater.createFromXml(r, xml, context.getTheme());
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Failed to inflate ColorStateList, leaving it to the framework", e);
+        }
+        return null;
+    }
+
+    static boolean isColorInt(Context context, int resId) {
+        final Resources r = context.getResources();
+
+        final TypedValue value = getTypedValue();
+        r.getValue(resId, value, true);
+
+        return value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+                && value.type <= TypedValue.TYPE_LAST_COLOR_INT;
+    }
+
+    private static TypedValue getTypedValue() {
+        TypedValue tv = TL_TYPED_VALUE.get();
+        if (tv == null) {
+            tv = new TypedValue();
+            TL_TYPED_VALUE.set(tv);
+        }
+        return tv;
+    }
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java b/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java
new file mode 100644
index 0000000..1d97400
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/GrowingArrayUtils.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import java.lang.reflect.Array;
+
+/**
+ * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
+ * arrays. Common array operations are implemented for efficient use in dynamic containers.
+ *
+ * All methods in this class assume that the length of an array is equivalent to its capacity and
+ * NOT the number of elements in the array. The current size of the array is always passed in as a
+ * parameter.
+ */
+class GrowingArrayUtils {
+
+    /**
+     * Appends an element to the end of the array, growing the array if there is no more room.
+     * @param array The array to which to append the element. This must NOT be null.
+     * @param currentSize The number of elements in the array. Must be less than or equal to
+     *                    array.length.
+     * @param element The element to append.
+     * @return the array to which the element was appended. This may be different than the given
+     *         array.
+     */
+    public static <T> T[] append(T[] array, int currentSize, T element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 > array.length) {
+            T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
+                    growSize(currentSize));
+            System.arraycopy(array, 0, newArray, 0, currentSize);
+            array = newArray;
+        }
+        array[currentSize] = element;
+        return array;
+    }
+
+    /**
+     * Primitive int version of {@link #append(Object[], int, Object)}.
+     */
+    public static int[] append(int[] array, int currentSize, int element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 > array.length) {
+            int[] newArray = new int[growSize(currentSize)];
+            System.arraycopy(array, 0, newArray, 0, currentSize);
+            array = newArray;
+        }
+        array[currentSize] = element;
+        return array;
+    }
+
+    /**
+     * Primitive long version of {@link #append(Object[], int, Object)}.
+     */
+    public static long[] append(long[] array, int currentSize, long element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 > array.length) {
+            long[] newArray = new long[growSize(currentSize)];
+            System.arraycopy(array, 0, newArray, 0, currentSize);
+            array = newArray;
+        }
+        array[currentSize] = element;
+        return array;
+    }
+
+    /**
+     * Primitive boolean version of {@link #append(Object[], int, Object)}.
+     */
+    public static boolean[] append(boolean[] array, int currentSize, boolean element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 > array.length) {
+            boolean[] newArray = new boolean[growSize(currentSize)];
+            System.arraycopy(array, 0, newArray, 0, currentSize);
+            array = newArray;
+        }
+        array[currentSize] = element;
+        return array;
+    }
+
+    /**
+     * Inserts an element into the array at the specified index, growing the array if there is no
+     * more room.
+     *
+     * @param array The array to which to append the element. Must NOT be null.
+     * @param currentSize The number of elements in the array. Must be less than or equal to
+     *                    array.length.
+     * @param element The element to insert.
+     * @return the array to which the element was appended. This may be different than the given
+     *         array.
+     */
+    public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 <= array.length) {
+            System.arraycopy(array, index, array, index + 1, currentSize - index);
+            array[index] = element;
+            return array;
+        }
+
+        T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
+                growSize(currentSize));
+        System.arraycopy(array, 0, newArray, 0, index);
+        newArray[index] = element;
+        System.arraycopy(array, index, newArray, index + 1, array.length - index);
+        return newArray;
+    }
+
+    /**
+     * Primitive int version of {@link #insert(Object[], int, int, Object)}.
+     */
+    public static int[] insert(int[] array, int currentSize, int index, int element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 <= array.length) {
+            System.arraycopy(array, index, array, index + 1, currentSize - index);
+            array[index] = element;
+            return array;
+        }
+
+        int[] newArray = new int[growSize(currentSize)];
+        System.arraycopy(array, 0, newArray, 0, index);
+        newArray[index] = element;
+        System.arraycopy(array, index, newArray, index + 1, array.length - index);
+        return newArray;
+    }
+
+    /**
+     * Primitive long version of {@link #insert(Object[], int, int, Object)}.
+     */
+    public static long[] insert(long[] array, int currentSize, int index, long element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 <= array.length) {
+            System.arraycopy(array, index, array, index + 1, currentSize - index);
+            array[index] = element;
+            return array;
+        }
+
+        long[] newArray = new long[growSize(currentSize)];
+        System.arraycopy(array, 0, newArray, 0, index);
+        newArray[index] = element;
+        System.arraycopy(array, index, newArray, index + 1, array.length - index);
+        return newArray;
+    }
+
+    /**
+     * Primitive boolean version of {@link #insert(Object[], int, int, Object)}.
+     */
+    public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
+        assert currentSize <= array.length;
+
+        if (currentSize + 1 <= array.length) {
+            System.arraycopy(array, index, array, index + 1, currentSize - index);
+            array[index] = element;
+            return array;
+        }
+
+        boolean[] newArray = new boolean[growSize(currentSize)];
+        System.arraycopy(array, 0, newArray, 0, index);
+        newArray[index] = element;
+        System.arraycopy(array, index, newArray, index + 1, array.length - index);
+        return newArray;
+    }
+
+    /**
+     * Given the current size of an array, returns an ideal size to which the array should grow.
+     * This is typically double the given size, but should not be relied upon to do so in the
+     * future.
+     */
+    public static int growSize(int currentSize) {
+        return currentSize <= 4 ? 8 : currentSize * 2;
+    }
+
+    // Uninstantiable
+    private GrowingArrayUtils() {}
+}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/widget/SearchView.java b/v7/appcompat/src/android/support/v7/widget/SearchView.java
index e4644d0..0b59400 100644
--- a/v7/appcompat/src/android/support/v7/widget/SearchView.java
+++ b/v7/appcompat/src/android/support/v7/widget/SearchView.java
@@ -48,7 +48,9 @@
 import android.text.TextWatcher;
 import android.text.style.ImageSpan;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -1648,6 +1650,14 @@
             mThreshold = getThreshold();
         }
 
+        @Override
+        protected void onFinishInflate() {
+            super.onFinishInflate();
+            DisplayMetrics metrics = getResources().getDisplayMetrics();
+            setMinWidth((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    getSearchViewTextMinWidthDp(), metrics));
+        }
+
         void setSearchView(SearchView searchView) {
             mSearchView = searchView;
         }
@@ -1743,6 +1753,23 @@
             }
             return super.onKeyPreIme(keyCode, event);
         }
+
+        /**
+         * Get minimum width of the search view text entry area.
+         */
+        private int getSearchViewTextMinWidthDp() {
+            final Configuration configuration = getResources().getConfiguration();
+            final int width = configuration.screenWidthDp;
+            final int height = configuration.screenHeightDp;
+            final int orientation = configuration.orientation;
+            if (width >= 960 && height >= 720
+                    && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+                return 256;
+            } else if (width >= 600 || (width >= 640 && height >= 480)) {
+                return 192;
+            };
+            return 160;
+        }
     }
 
     private static class AutoCompleteTextViewReflector {
@@ -1821,4 +1848,4 @@
             imm.showSoftInput(view, flags);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java b/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
index 1c0151b..02ee49a 100644
--- a/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
+++ b/v7/appcompat/src/android/support/v7/widget/ThemeUtils.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.support.v4.graphics.ColorUtils;
 import android.util.TypedValue;
@@ -61,7 +60,7 @@
 
     public static int getThemeAttrColor(Context context, int attr) {
         TEMP_ARRAY[0] = attr;
-        TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
         try {
             return a.getColor(0, 0);
         } finally {
@@ -71,7 +70,7 @@
 
     public static ColorStateList getThemeAttrColorStateList(Context context, int attr) {
         TEMP_ARRAY[0] = attr;
-        TypedArray a = context.obtainStyledAttributes(null, TEMP_ARRAY);
+        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
         try {
             return a.getColorStateList(0);
         } finally {
diff --git a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
index 50e1046..32749f3 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintTypedArray.java
@@ -118,6 +118,16 @@
     }
 
     public ColorStateList getColorStateList(int index) {
+        if (mWrapped.hasValue(index)) {
+            final int resourceId = mWrapped.getResourceId(index, 0);
+            if (resourceId != 0) {
+                final ColorStateList value =
+                        ColorStateListUtils.getColorStateList(mContext, resourceId);
+                if (value != null) {
+                    return value;
+                }
+            }
+        }
         return mWrapped.getColorStateList(index);
     }
 
diff --git a/v7/appcompat/src/android/support/v7/widget/Toolbar.java b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
index a5f9612..eae7722 100644
--- a/v7/appcompat/src/android/support/v7/widget/Toolbar.java
+++ b/v7/appcompat/src/android/support/v7/widget/Toolbar.java
@@ -105,6 +105,32 @@
  * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
  * toolbars than on their application icon. The use of application icon plus title as a standard
  * layout is discouraged on API 21 devices and newer.</p>
+ *
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_buttonGravity
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseContentDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseIcon
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetRight
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_android_gravity
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logo
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logoDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_maxButtonHeight
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_popupTheme
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitle
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextAppearance
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextColor
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_title
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextAppearance
+ * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextColor
  */
 public class Toolbar extends ViewGroup {
     private static final String TAG = "Toolbar";
@@ -206,9 +232,15 @@
         mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
         mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
         mGravity = a.getInteger(R.styleable.Toolbar_android_gravity, mGravity);
-        mButtonGravity = Gravity.TOP;
-        mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
-                a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
+        mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
+
+        // First read the correct attribute
+        int titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
+        if (a.hasValue(R.styleable.Toolbar_titleMargins)) {
+            // Now read the deprecated attribute, if it has a value
+            titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, titleMargin);
+        }
+        mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = titleMargin;
 
         final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
         if (marginStart >= 0) {
@@ -263,6 +295,7 @@
         if (!TextUtils.isEmpty(subtitle)) {
             setSubtitle(subtitle);
         }
+
         // Set the default context, since setPopupTheme() may be a no-op.
         mPopupContext = getContext();
         setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
@@ -325,6 +358,116 @@
         return mPopupTheme;
     }
 
+    /**
+     * Sets the title margin.
+     *
+     * @param start the starting title margin in pixels
+     * @param top the top title margin in pixels
+     * @param end the ending title margin in pixels
+     * @param bottom the bottom title margin in pixels
+     * @see #getTitleMarginStart()
+     * @see #getTitleMarginTop()
+     * @see #getTitleMarginEnd()
+     * @see #getTitleMarginBottom()
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
+     */
+    public void setTitleMargin(int start, int top, int end, int bottom) {
+        mTitleMarginStart = start;
+        mTitleMarginTop = top;
+        mTitleMarginEnd = end;
+        mTitleMarginBottom = bottom;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the starting title margin in pixels
+     * @see #setTitleMarginStart(int)
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+     */
+    public int getTitleMarginStart() {
+        return mTitleMarginStart;
+    }
+
+    /**
+     * Sets the starting title margin in pixels.
+     *
+     * @param margin the starting title margin in pixels
+     * @see #getTitleMarginStart()
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
+     */
+    public void setTitleMarginStart(int margin) {
+        mTitleMarginStart = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the top title margin in pixels
+     * @see #setTitleMarginTop(int)
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+     */
+    public int getTitleMarginTop() {
+        return mTitleMarginTop;
+    }
+
+    /**
+     * Sets the top title margin in pixels.
+     *
+     * @param margin the top title margin in pixels
+     * @see #getTitleMarginTop()
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
+     */
+    public void setTitleMarginTop(int margin) {
+        mTitleMarginTop = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the ending title margin in pixels
+     * @see #setTitleMarginEnd(int)
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+     */
+    public int getTitleMarginEnd() {
+        return mTitleMarginEnd;
+    }
+
+    /**
+     * Sets the ending title margin in pixels.
+     *
+     * @param margin the ending title margin in pixels
+     * @see #getTitleMarginEnd()
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
+     */
+    public void setTitleMarginEnd(int margin) {
+        mTitleMarginEnd = margin;
+
+        requestLayout();
+    }
+
+    /**
+     * @return the bottom title margin in pixels
+     * @see #setTitleMarginBottom(int)
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+     */
+    public int getTitleMarginBottom() {
+        return mTitleMarginBottom;
+    }
+
+    /**
+     * Sets the bottom title margin in pixels.
+     *
+     * @param margin the bottom title margin in pixels
+     * @see #getTitleMarginBottom()
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
+     */
+    public void setTitleMarginBottom(int margin) {
+        mTitleMarginBottom = margin;
+
+        requestLayout();
+    }
+
     public void onRtlPropertiesChanged(int layoutDirection) {
         if (Build.VERSION.SDK_INT >= 17) {
             super.onRtlPropertiesChanged(layoutDirection);
@@ -721,6 +864,8 @@
      * as screen readers or tooltips.
      *
      * @return The navigation button's content description
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
      */
     @Nullable
     public CharSequence getNavigationContentDescription() {
@@ -734,6 +879,8 @@
      *
      * @param resId Resource ID of a content description string to set, or 0 to
      *              clear the description
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
      */
     public void setNavigationContentDescription(@StringRes int resId) {
         setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
@@ -746,6 +893,8 @@
      *
      * @param description Content description to set, or <code>null</code> to
      *                    clear the content description
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
      */
     public void setNavigationContentDescription(@Nullable CharSequence description) {
         if (!TextUtils.isEmpty(description)) {
@@ -767,6 +916,8 @@
      * tooltips.</p>
      *
      * @param resId Resource ID of a drawable to set
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
      */
     public void setNavigationIcon(@DrawableRes int resId) {
         setNavigationIcon(mDrawableManager.getDrawable(getContext(), resId));
@@ -783,6 +934,8 @@
      * tooltips.</p>
      *
      * @param icon Drawable to set, may be null to clear the icon
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
      */
     public void setNavigationIcon(@Nullable Drawable icon) {
         if (icon != null) {
@@ -803,6 +956,8 @@
      * Return the current drawable used as the navigation icon.
      *
      * @return The navigation icon drawable
+     *
+     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
      */
     @Nullable
     public Drawable getNavigationIcon() {
diff --git a/v7/appcompat/tests/res/layout/toolbar_decor_content.xml b/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
index e167c9b..91d93ca 100644
--- a/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
+++ b/v7/appcompat/tests/res/layout/toolbar_decor_content.xml
@@ -27,6 +27,13 @@
     <android.support.v7.custom.FitWindowsContentLayout
             android:id="@+id/test_content"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent">
+
+        <EditText
+                android:id="@+id/editText"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"/>
+
+    </android.support.v7.custom.FitWindowsContentLayout>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
index 16c37a2..430fa73 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/KeyboardShortcutsTestCaseWithToolbar.java
@@ -16,9 +16,81 @@
 
 package android.support.v7.app;
 
+import android.os.SystemClock;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.Test;
+
 public class KeyboardShortcutsTestCaseWithToolbar
         extends BaseKeyboardShortcutsTestCase<ToolbarActionBarActivity> {
     public KeyboardShortcutsTestCaseWithToolbar() {
         super(ToolbarActionBarActivity.class);
     }
+
+    @Test
+    @SmallTest
+    public void testAccessActionBar() throws Throwable {
+        final BaseTestActivity activity = getActivity();
+
+        final View editText = activity.findViewById(R.id.editText);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                editText.requestFocus();
+            }
+        });
+
+        getInstrumentation().waitForIdleSync();
+        sendControlChar('<');
+        getInstrumentation().waitForIdleSync();
+
+        // Should jump to the action bar after control-<
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertFalse(editText.hasFocus());
+                final View toolbar = activity.findViewById(R.id.toolbar);
+                assertTrue(toolbar.hasFocus());
+            }
+        });
+        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
+        getInstrumentation().waitForIdleSync();
+
+        // Should jump to the first view again.
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(editText.hasFocus());
+            }
+        });
+        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP);
+        getInstrumentation().waitForIdleSync();
+
+        // Now it shouldn't go up to action bar -- it doesn't allow taking focus once left
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(editText.hasFocus());
+            }
+        });
+    }
+
+    private void sendControlChar(char key) throws Throwable {
+        KeyEvent tempEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
+        KeyCharacterMap map = tempEvent.getKeyCharacterMap();
+        KeyEvent[] events = map.getEvents(new char[] {key});
+        for (int i = 0; i < events.length; i++) {
+            long time = SystemClock.uptimeMillis();
+            KeyEvent event = events[i];
+            KeyEvent controlKey = new KeyEvent(time, time, event.getAction(), event.getKeyCode(),
+                    event.getRepeatCount(), event.getMetaState() | KeyEvent.META_CTRL_ON);
+            getInstrumentation().sendKeySync(controlKey);
+        }
+    }
+
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java b/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java
new file mode 100644
index 0000000..09ed7ab
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/MenuBuilderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.app;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.view.menu.MenuBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class MenuBuilderTest {
+
+    @Test
+    public void setOptionalIconsVisibleMethodShouldRemainPublic() throws Exception {
+        // This test is to verify workaround for bug in the ROM of Explay Fresh devices with 4.2.2 ROM.
+        // Manufacturer has modified ROM and added a public method setOptionalIconsVisible
+        // to android.view.Menu interface. Because of that the runtime can't load MenuBuilder class
+        // because it had no such public method (it was package local)
+        Method method = MenuBuilder.class
+                .getMethod("setOptionalIconsVisible", boolean.class);
+        assertNotNull(method);
+        assertTrue(Modifier.isPublic(method.getModifiers()));
+    }
+}
+
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index 8636474..5f15f66 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -27,6 +27,11 @@
         androidTest.java.srcDir 'tests/java'
     }
 
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
diff --git a/v7/gridlayout/Android.mk b/v7/gridlayout/Android.mk
index 27ddfbd..38186cb 100644
--- a/v7/gridlayout/Android.mk
+++ b/v7/gridlayout/Android.mk
@@ -35,3 +35,5 @@
 support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES) android-support-v7-gridlayout
 support_module_java_packages := android.support.v7.widget
 include $(SUPPORT_API_CHECK)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/v7/mediarouter/Android.mk b/v7/mediarouter/Android.mk
index 9bd9960..3b2b18f 100644
--- a/v7/mediarouter/Android.mk
+++ b/v7/mediarouter/Android.mk
@@ -61,6 +61,16 @@
 
 support_module_src_files += $(LOCAL_SRC_FILES)
 
+# A helper sub-library that makes direct use of V24 APIs.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-mediarouter-api24
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api24)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr2
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+support_module_src_files += $(LOCAL_SRC_FILES)
+
 # 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
@@ -69,7 +79,7 @@
 LOCAL_MODULE := android-support-v7-mediarouter
 LOCAL_SDK_VERSION := 7
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-jellybean-mr2
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-mediarouter-api24
 LOCAL_JAVA_LIBRARIES := android-support-v4 android-support-v7-mediarouter-res \
     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
new file mode 100644
index 0000000..3734b59
--- /dev/null
+++ b/v7/mediarouter/api24/android/support/v7/media/MediaRouterApi24.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.media;
+
+final class MediaRouterApi24 {
+    public static final class RouteInfo {
+        public static int getDeviceType(Object routeObj) {
+            return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType();
+        }
+    }
+}
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 37224f0..883f939 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -16,6 +16,7 @@
     jellybean.java.srcDir 'jellybean'
     jellybeanmr1.java.srcDir 'jellybean-mr1'
     jellybeanmr2.java.srcDir 'jellybean-mr2'
+    api24.java.srcDir 'api24'
 }
 
 // create a jar task for the code above
@@ -23,6 +24,7 @@
     from sourceSets.jellybean.output
     from sourceSets.jellybeanmr1.output
     from sourceSets.jellybeanmr2.output
+    from sourceSets.api24.output
     baseName "internal_impl"
 }
 
@@ -33,10 +35,15 @@
     jellybeanmr1Compile getAndroidPrebuilt('17')
     jellybeanmr1Compile sourceSets.jellybean.output
 
-    jellybeanmr2Compile getAndroidPrebuilt('current')
+    jellybeanmr2Compile getAndroidPrebuilt('18')
     jellybeanmr2Compile sourceSets.jellybean.output
     jellybeanmr2Compile sourceSets.jellybeanmr1.output
 
+    api24Compile getAndroidPrebuilt('current')
+    api24Compile sourceSets.jellybean.output
+    api24Compile sourceSets.jellybeanmr1.output
+    api24Compile sourceSets.jellybeanmr2.output
+
     compile files(jar.archivePath)
 }
 
@@ -105,6 +112,8 @@
     sourcesJarTask.from project.sourceSets.jellybeanmr1.java.srcDirs
     javadocTask.source  project.sourceSets.jellybeanmr2.java
     sourcesJarTask.from project.sourceSets.jellybeanmr2.java.srcDirs
+    javadocTask.source  project.sourceSets.api24.java
+    sourcesJarTask.from project.sourceSets.api24.java.srcDirs
 
     artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
diff --git a/v7/mediarouter/res/values-b+sr+Latn/strings.xml b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..c10c42d
--- /dev/null
+++ b/v7/mediarouter/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
+    <string name="mr_button_content_description" msgid="3698378085901466129">"Dugme Prebaci"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Prebacujte na"</string>
+    <string name="mr_chooser_searching" msgid="6349900579507521956">"Pronalaženje uređaja"</string>
+    <string name="mr_controller_disconnect" msgid="1227264889412989580">"Prekini vezu"</string>
+    <string name="mr_controller_stop" msgid="4570331844078181931">"Zaustavi prebacivanje"</string>
+    <string name="mr_controller_close_description" msgid="7333862312480583260">"Zatvori"</string>
+    <string name="mr_controller_play" msgid="683634565969987458">"Pusti"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"Pauziraj"</string>
+    <string name="mr_controller_expand_group" msgid="8062427022744266907">"Proširi"</string>
+    <string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skupi"</string>
+    <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Nema izabranih medija"</string>
+    <string name="mr_controller_no_info_available" msgid="5585418471741142924">"Nisu dostupne nikakve informacije"</string>
+    <string name="mr_controller_casting_screen" msgid="4868457957151124867">"Prebacuje se ekran"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-fa/strings.xml b/v7/mediarouter/res/values-fa/strings.xml
index 87a81ba..2c812ae 100644
--- a/v7/mediarouter/res/values-fa/strings.xml
+++ b/v7/mediarouter/res/values-fa/strings.xml
@@ -25,7 +25,7 @@
     <string name="mr_controller_stop" msgid="4570331844078181931">"توقف ارسال محتوا"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"بستن"</string>
     <string name="mr_controller_play" msgid="683634565969987458">"پخش"</string>
-    <string name="mr_controller_pause" msgid="5451884435510905406">"توقف موقت"</string>
+    <string name="mr_controller_pause" msgid="5451884435510905406">"مکث"</string>
     <string name="mr_controller_expand_group" msgid="8062427022744266907">"بزرگ کردن"</string>
     <string name="mr_controller_collapse_group" msgid="7924809056904240926">"کوچک کردن"</string>
     <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"رسانه انتخاب نشده است"</string>
diff --git a/v7/mediarouter/res/values-fr/strings.xml b/v7/mediarouter/res/values-fr/strings.xml
index de94f18..6e14ccc 100644
--- a/v7/mediarouter/res/values-fr/strings.xml
+++ b/v7/mediarouter/res/values-fr/strings.xml
@@ -19,7 +19,7 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Icône Cast"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Diffuser sur"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Caster sur"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Recherche d\'appareils en cours…"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Déconnecter"</string>
     <string name="mr_controller_stop" msgid="4570331844078181931">"Arrêter de diffuser"</string>
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy-rAM/strings.xml
index 85bd488..b05c17c 100644
--- a/v7/mediarouter/res/values-hy-rAM/strings.xml
+++ b/v7/mediarouter/res/values-hy-rAM/strings.xml
@@ -19,7 +19,7 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Համակարգ"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Սարքեր"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Հեռարձակման կոճակ"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Հեռարձակել դեպի"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Ընտրեք սարքը"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Սարքերի որոնում"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Անջատել"</string>
     <string name="mr_controller_stop" msgid="4570331844078181931">"Դադարեցնել հեռարձակումը"</string>
diff --git a/v7/mediarouter/res/values-in/strings.xml b/v7/mediarouter/res/values-in/strings.xml
index 3d01880..4d84852 100644
--- a/v7/mediarouter/res/values-in/strings.xml
+++ b/v7/mediarouter/res/values-in/strings.xml
@@ -19,7 +19,7 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"Perangkat"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"Tombol transmisi"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"Transmisi ke"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"Transmisikan ke"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari perangkat"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
     <string name="mr_controller_stop" msgid="4570331844078181931">"Hentikan transmisi"</string>
diff --git a/v7/mediarouter/res/values-ja/strings.xml b/v7/mediarouter/res/values-ja/strings.xml
index ea8d838..ffe9054 100644
--- a/v7/mediarouter/res/values-ja/strings.xml
+++ b/v7/mediarouter/res/values-ja/strings.xml
@@ -19,7 +19,7 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"システム"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"端末"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"キャストアイコン"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"キャスト先"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"キャストするデバイス"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"端末を検索しています"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"接続を解除"</string>
     <string name="mr_controller_stop" msgid="4570331844078181931">"キャストを停止"</string>
diff --git a/v7/mediarouter/res/values-ko/strings.xml b/v7/mediarouter/res/values-ko/strings.xml
index 2b3455f..fc1dbac 100644
--- a/v7/mediarouter/res/values-ko/strings.xml
+++ b/v7/mediarouter/res/values-ko/strings.xml
@@ -19,7 +19,7 @@
     <string name="mr_system_route_name" msgid="5441529851481176817">"시스템"</string>
     <string name="mr_user_route_category_name" msgid="7498112907524977311">"기기"</string>
     <string name="mr_button_content_description" msgid="3698378085901466129">"전송 버튼"</string>
-    <string name="mr_chooser_title" msgid="414301941546135990">"전송 대상"</string>
+    <string name="mr_chooser_title" msgid="414301941546135990">"전송할 기기"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"기기를 찾는 중"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"연결 해제"</string>
     <string name="mr_controller_stop" msgid="4570331844078181931">"전송 중지"</string>
diff --git a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
index 6a09650..21ff5b4 100644
--- a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
@@ -49,6 +49,9 @@
     }
 
     public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return new Api24Impl(context, syncCallback);
+        }
         if (Build.VERSION.SDK_INT >= 18) {
             return new JellybeanMr2Impl(context, syncCallback);
         }
@@ -836,4 +839,21 @@
             return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj);
         }
     }
+
+    /**
+     * Api24 implementation.
+     */
+    private static class Api24Impl extends JellybeanMr2Impl {
+        public Api24Impl(Context context, SyncCallback syncCallback) {
+            super(context, syncCallback);
+        }
+
+        @Override
+        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
+                                                    MediaRouteDescriptor.Builder builder) {
+            super.onBuildSystemRouteDescriptor(record, builder);
+
+            builder.setDeviceType(MediaRouterApi24.RouteInfo.getDeviceType(record.mRouteObj));
+        }
+    }
 }
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index e2db0f1..4fb0f03 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -14,14 +14,14 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# Build the resources using the current SDK version.
+# Build the resources and resource constants using the current SDK version.
 # We do this here because the final static library must be compiled with an older
 # SDK version than the resources.  The resources library and the R class that it
 # contains will not be linked into the final static library.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v7-preference-res
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_SRC_FILES := $(call all-java-files-under, constants)
 LOCAL_RESOURCE_DIR := \
         frameworks/support/v7/appcompat/res \
         frameworks/support/v7/recyclerview/res \
diff --git a/v7/preference/api/current.txt b/v7/preference/api/current.txt
index 9034fd4..35447cd 100644
--- a/v7/preference/api/current.txt
+++ b/v7/preference/api/current.txt
@@ -35,6 +35,14 @@
     method public abstract android.support.v7.preference.Preference findPreference(java.lang.CharSequence);
   }
 
+  public class DropDownPreference extends android.support.v7.preference.ListPreference {
+    ctor public DropDownPreference(android.content.Context);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public DropDownPreference(android.content.Context, android.util.AttributeSet, int, int);
+    method protected android.widget.ArrayAdapter createAdapter();
+  }
+
   public class EditTextPreference extends android.support.v7.preference.DialogPreference {
     ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
@@ -117,6 +125,7 @@
     method public void onBindViewHolder(android.support.v7.preference.PreferenceViewHolder);
     method protected void onClick();
     method public void onDependencyChanged(android.support.v7.preference.Preference, boolean);
+    method public void onDetached();
     method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
     method public void onParentChanged(android.support.v7.preference.Preference, boolean);
     method protected void onPrepareForRemoval();
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index 45b4f96..1693b3a 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -30,6 +30,7 @@
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
+        main.java.srcDir 'constants'
         main.res.srcDir 'res'
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
diff --git a/v7/preference/constants/android/support/v7/preference/AndroidResources.java b/v7/preference/constants/android/support/v7/preference/AndroidResources.java
new file mode 100644
index 0000000..d529ad2
--- /dev/null
+++ b/v7/preference/constants/android/support/v7/preference/AndroidResources.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.support.v7.preference;
+
+public class AndroidResources {
+
+    public static final int ANDROID_R_ICON_FRAME = android.R.id.icon_frame;
+    public static final int ANDROID_R_LIST_CONTAINER = android.R.id.list_container;
+    public static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;
+    public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE
+            = android.R.attr.preferenceFragmentStyle;
+    public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
+            = android.R.attr.editTextPreferenceStyle;
+
+}
diff --git a/v7/preference/res/layout/preference_dropdown.xml b/v7/preference/res/layout/preference_dropdown.xml
new file mode 100644
index 0000000..09ea8fb
--- /dev/null
+++ b/v7/preference/res/layout/preference_dropdown.xml
@@ -0,0 +1,30 @@
+<?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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Spinner
+        android:id="@+id/spinner"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:visibility="invisible" />
+
+    <include layout="@layout/preference" />
+
+</FrameLayout>
diff --git a/v7/preference/res/layout/preference_list_fragment.xml b/v7/preference/res/layout/preference_list_fragment.xml
index 44c5438..fe11fb2 100644
--- a/v7/preference/res/layout/preference_list_fragment.xml
+++ b/v7/preference/res/layout/preference_list_fragment.xml
@@ -21,7 +21,7 @@
     android:layout_width="match_parent" >
 
     <FrameLayout
-        android:id="@+id/list_container"
+        android:id="@android:id/list_container"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1" />
diff --git a/v7/preference/res/layout/preference_widget_checkbox.xml b/v7/preference/res/layout/preference_widget_checkbox.xml
index e53b7df..cc99443 100644
--- a/v7/preference/res/layout/preference_widget_checkbox.xml
+++ b/v7/preference/res/layout/preference_widget_checkbox.xml
@@ -18,7 +18,7 @@
 <!-- Layout used by CheckBoxPreference for the checkbox style. This is inflated
      inside android.R.layout.preference. -->
 <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/checkbox"
+    android:id="@android:id/checkbox"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:focusable="false"
diff --git a/v7/preference/res/values/attrs.xml b/v7/preference/res/values/attrs.xml
index 5d39b63..c0f255a 100644
--- a/v7/preference/res/values/attrs.xml
+++ b/v7/preference/res/values/attrs.xml
@@ -50,6 +50,8 @@
         <attr name="editTextPreferenceStyle" format="reference" />
         <!-- Default style for RingtonePreference. -->
         <attr name="ringtonePreferenceStyle" format="reference" />
+        <!-- Default style for DropDownPreference. -->
+        <attr name="dropdownPreferenceStyle" format="reference" />
         <!-- The preference layout that has the child/tabbed effect. -->
         <attr name="preferenceLayoutChild" format="reference" />
         <!-- Preference panel style -->
@@ -245,4 +247,11 @@
         <attr name="android:maxHeight" />
     </declare-styleable>
 
+    <!-- Used to access some android attrs -->
+    <declare-styleable name="BackgroundStyle">
+        <attr name="android:selectableItemBackground" />
+        <!-- Need a non-android: attr here so that gradle doesn't remove it -->
+        <attr name="selectableItemBackground" />
+    </declare-styleable>
+
 </resources>
diff --git a/v7/preference/res/values/styles.xml b/v7/preference/res/values/styles.xml
index 774c0c9..06b24fa 100644
--- a/v7/preference/res/values/styles.xml
+++ b/v7/preference/res/values/styles.xml
@@ -65,4 +65,8 @@
         <item name="android:paddingLeft">16dp</item>
         <item name="android:paddingRight">16dp</item>
     </style>
+
+    <style name="Preference.DropDown">
+        <item name="android:layout">@layout/preference_dropdown</item>
+    </style>
 </resources>
diff --git a/v7/preference/res/values/themes.xml b/v7/preference/res/values/themes.xml
index 8a7eaa3..bb7f496 100644
--- a/v7/preference/res/values/themes.xml
+++ b/v7/preference/res/values/themes.xml
@@ -27,5 +27,6 @@
         <item name="dialogPreferenceStyle">@style/Preference.DialogPreference</item>
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference</item>
         <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList</item>
+        <item name="dropdownPreferenceStyle">@style/Preference.DropDown</item>
     </style>
 </resources>
diff --git a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
index 61eb13c..94f0751 100644
--- a/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
+++ b/v7/preference/src/android/support/v7/preference/CheckBoxPreference.java
@@ -61,7 +61,8 @@
     }
 
     public CheckBoxPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.checkBoxPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.checkBoxPreferenceStyle,
+                android.R.attr.checkBoxPreferenceStyle));
     }
 
     public CheckBoxPreference(Context context) {
@@ -72,7 +73,7 @@
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
 
-        View checkboxView = holder.findViewById(R.id.checkbox);
+        View checkboxView = holder.findViewById(android.R.id.checkbox);
         if (checkboxView != null && checkboxView instanceof Checkable) {
             ((Checkable) checkboxView).setChecked(mChecked);
         }
diff --git a/v7/preference/src/android/support/v7/preference/DialogPreference.java b/v7/preference/src/android/support/v7/preference/DialogPreference.java
index bf0f4ef..158accb 100644
--- a/v7/preference/src/android/support/v7/preference/DialogPreference.java
+++ b/v7/preference/src/android/support/v7/preference/DialogPreference.java
@@ -90,7 +90,8 @@
     }
 
     public DialogPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.dialogPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+                android.R.attr.dialogPreferenceStyle));
     }
 
     public DialogPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/DropDownPreference.java b/v7/preference/src/android/support/v7/preference/DropDownPreference.java
new file mode 100644
index 0000000..e7a0514
--- /dev/null
+++ b/v7/preference/src/android/support/v7/preference/DropDownPreference.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.preference;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+public class DropDownPreference extends ListPreference {
+
+    private final Context mContext;
+    private final ArrayAdapter<String> mAdapter;
+
+    private Spinner mSpinner;
+
+    public DropDownPreference(Context context) {
+        this(context, null);
+    }
+
+    public DropDownPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.dropdownPreferenceStyle);
+    }
+
+    public DropDownPreference(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+
+    public DropDownPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mContext = context;
+        mAdapter = createAdapter();
+
+        updateEntries();
+    }
+
+    @Override
+    protected void onClick() {
+        mSpinner.performClick();
+    }
+
+    @Override
+    public void setEntries(@NonNull CharSequence[] entries) {
+        super.setEntries(entries);
+        updateEntries();
+    }
+
+
+    /**
+     * By default, this class uses a simple {@link android.widget.ArrayAdapter}. But if you need
+     * a more complicated {@link android.widget.ArrayAdapter}, this method can be overriden to
+     * create a custom one.
+     * <p> Note: This method is called from the constructor. So, overriden methods will get called
+     * before any subclass initialization.
+     *
+     * @return The custom {@link android.widget.ArrayAdapter} that needs to be used with this class.
+     */
+    protected ArrayAdapter createAdapter() {
+        return new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_dropdown_item);
+    }
+
+    private void updateEntries() {
+        mAdapter.clear();
+        if (getEntries() != null) {
+            for (CharSequence c : getEntries()) {
+                mAdapter.add(c.toString());
+            }
+        }
+    }
+
+    public void setValueIndex(int index) {
+        setValue(getEntryValues()[index].toString());
+    }
+
+    /**
+     * @hide
+     */
+    public int findSpinnerIndexOfValue(String value) {
+        CharSequence[] entryValues = getEntryValues();
+        if (value != null && entryValues != null) {
+            for (int i = entryValues.length - 1; i >= 0; i--) {
+                if (entryValues[i].equals(value)) {
+                    return i;
+                }
+            }
+        }
+        return Spinner.INVALID_POSITION;
+    }
+
+    @Override
+    protected void notifyChanged() {
+        super.notifyChanged();
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner);
+        mSpinner.setAdapter(mAdapter);
+        mSpinner.setOnItemSelectedListener(mItemSelectedListener);
+        mSpinner.setSelection(findSpinnerIndexOfValue(getValue()));
+        super.onBindViewHolder(view);
+    }
+
+    private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
+            if (position >= 0) {
+                String value = getEntryValues()[position].toString();
+                if (!value.equals(getValue()) && callChangeListener(value)) {
+                    setValue(value);
+                }
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // noop
+        }
+    };
+}
+
diff --git a/v7/preference/src/android/support/v7/preference/EditTextPreference.java b/v7/preference/src/android/support/v7/preference/EditTextPreference.java
index c4941e6..423a0c9 100644
--- a/v7/preference/src/android/support/v7/preference/EditTextPreference.java
+++ b/v7/preference/src/android/support/v7/preference/EditTextPreference.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.v4.content.res.TypedArrayUtils;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.widget.EditText;
@@ -46,7 +47,8 @@
     }
 
     public EditTextPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.editTextPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.editTextPreferenceStyle,
+                AndroidResources.ANDROID_R_EDITTEXT_PREFERENCE_STYLE));
     }
 
     public EditTextPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/ListPreference.java b/v7/preference/src/android/support/v7/preference/ListPreference.java
index 848cfad..4507b26 100644
--- a/v7/preference/src/android/support/v7/preference/ListPreference.java
+++ b/v7/preference/src/android/support/v7/preference/ListPreference.java
@@ -74,7 +74,8 @@
     }
 
     public ListPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.dialogPreferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
+                android.R.attr.dialogPreferenceStyle));
     }
 
     public ListPreference(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/Preference.java b/v7/preference/src/android/support/v7/preference/Preference.java
index f386592..2f320de 100644
--- a/v7/preference/src/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/android/support/v7/preference/Preference.java
@@ -272,7 +272,7 @@
 
         mShouldDisableView =
                 TypedArrayUtils.getBoolean(a, R.styleable.Preference_shouldDisableView,
-                        R.styleable.Preference_shouldDisableView, true);
+                        R.styleable.Preference_android_shouldDisableView, true);
 
         a.recycle();
     }
@@ -316,7 +316,8 @@
      * @see #Preference(Context, AttributeSet, int)
      */
     public Preference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.preferenceStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+                android.R.attr.preferenceStyle));
     }
 
     /**
@@ -506,7 +507,10 @@
             imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
         }
 
-        final View imageFrame = holder.findViewById(R.id.icon_frame);
+        View imageFrame = holder.findViewById(R.id.icon_frame);
+        if (imageFrame == null) {
+            imageFrame = holder.findViewById(AndroidResources.ANDROID_R_ICON_FRAME);
+        }
         if (imageFrame != null) {
             imageFrame.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
         }
@@ -1083,6 +1087,16 @@
         registerDependency();
     }
 
+    /**
+     * Called when the Preference hierarchy has been detached from the
+     * list of preferences. This can also be called when this
+     * Preference has been removed from a group that was attached
+     * to the list of preferences.
+     */
+    public void onDetached() {
+        unregisterDependency();
+    }
+
     private void registerDependency() {
 
         if (TextUtils.isEmpty(mDependencyKey)) return;
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceCategory.java b/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
index bed7f7e..10a0753 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceCategory.java
@@ -17,6 +17,7 @@
 package android.support.v7.preference;
 
 import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
 import android.util.AttributeSet;
 
 /**
@@ -43,7 +44,8 @@
     }
 
     public PreferenceCategory(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.preferenceCategoryStyle);
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceCategoryStyle,
+                android.R.attr.preferenceCategoryStyle));
     }
 
     public PreferenceCategory(Context context) {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
index 221d3ea..802fdb6 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceFragmentCompat.java
@@ -257,10 +257,10 @@
 
         final View view = themedInflater.inflate(mLayoutResId, container, false);
 
-        final View rawListContainer = view.findViewById(R.id.list_container);
+        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
         if (!(rawListContainer instanceof ViewGroup)) {
-            throw new RuntimeException("Content has view with id attribute 'R.id.list_container' "
-                    + "that is not a ViewGroup class");
+            throw new RuntimeException("Content has view with id attribute "
+                    + "'android.R.id.list_container' that is not a ViewGroup class");
         }
 
         final ViewGroup listContainer = (ViewGroup) rawListContainer;
@@ -309,14 +309,19 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
 
         if (mHavePrefs) {
             bindPreferences();
         }
 
         mInitDone = true;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
 
         if (savedInstanceState != null) {
             Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
@@ -345,9 +350,12 @@
 
     @Override
     public void onDestroyView() {
-        mList = null;
         mHandler.removeCallbacks(mRequestFocus);
         mHandler.removeMessages(MSG_BIND_PREFERENCES);
+        if (mHavePrefs) {
+            unbindPreferences();
+        }
+        mList = null;
         super.onDestroyView();
     }
 
@@ -514,6 +522,15 @@
         onBindPreferences();
     }
 
+    private void unbindPreferences() {
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        if (preferenceScreen != null) {
+            preferenceScreen.onDetached();
+            getListView().setAdapter(null);
+        }
+        onUnbindPreferences();
+    }
+
     /** @hide */
     protected void onBindPreferences() {
     }
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroup.java b/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
index 968b777..16d3d3a 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroup.java
@@ -192,7 +192,11 @@
     private boolean removePreferenceInt(Preference preference) {
         synchronized(this) {
             preference.onPrepareForRemoval();
-            return mPreferenceList.remove(preference);
+            boolean success = mPreferenceList.remove(preference);
+            if (success && mAttachedToHierarchy) {
+                preference.onDetached();
+            }
+            return success;
         }
     }
 
@@ -285,11 +289,17 @@
     }
 
     @Override
-    protected void onPrepareForRemoval() {
-        super.onPrepareForRemoval();
+    public void onDetached() {
+        super.onDetached();
 
         // We won't be attached to the activity anymore
         mAttachedToHierarchy = false;
+
+        // Dispatch to all contained preferences
+        final int preferenceCount = getPreferenceCount();
+        for (int i = 0; i < preferenceCount; i++) {
+            getPreference(i).onDetached();
+        }
     }
 
     @Override
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
index c4ccc66..fefab02 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceGroupAdapter.java
@@ -16,6 +16,8 @@
 
 package android.support.v7.preference;
 
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
@@ -271,8 +273,18 @@
     public PreferenceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         final PreferenceLayout pl = mPreferenceLayouts.get(viewType);
         final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        TypedArray a
+                = parent.getContext().obtainStyledAttributes(null, R.styleable.BackgroundStyle);
+        Drawable background
+                = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground);
+        if (background == null) {
+            background = parent.getContext().getResources()
+                    .getDrawable(android.R.drawable.list_selector_background);
+        }
+        a.recycle();
 
         final View view = inflater.inflate(pl.resId, parent, false);
+        view.setBackgroundDrawable(background);
 
         final ViewGroup widgetFrame = (ViewGroup) view.findViewById(android.R.id.widget_frame);
         if (widgetFrame != null) {
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceManager.java b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
index 0c3e65f..1c460a1 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceManager.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceManager.java
@@ -238,6 +238,9 @@
      */
     public boolean setPreferences(PreferenceScreen preferenceScreen) {
         if (preferenceScreen != mPreferenceScreen) {
+            if (mPreferenceScreen != null) {
+                mPreferenceScreen.onDetached();
+            }
             mPreferenceScreen = preferenceScreen;
             return true;
         }
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceScreen.java b/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
index e64ebcc..ee8b056 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceScreen.java
@@ -17,6 +17,7 @@
 package android.support.v7.preference;
 
 import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
 import android.util.AttributeSet;
 
 /**
@@ -79,7 +80,8 @@
      * @hide-
      */
     public PreferenceScreen(Context context, AttributeSet attrs) {
-        super(context, attrs, R.attr.preferenceScreenStyle);
+        super(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceScreenStyle,
+                android.R.attr.preferenceScreenStyle));
     }
 
     @Override
diff --git a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
index 2b604bd..97a907a 100644
--- a/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
+++ b/v7/preference/src/android/support/v7/preference/PreferenceViewHolder.java
@@ -39,6 +39,8 @@
         mCachedViews.put(android.R.id.summary, itemView.findViewById(android.R.id.summary));
         mCachedViews.put(android.R.id.icon, itemView.findViewById(android.R.id.icon));
         mCachedViews.put(R.id.icon_frame, itemView.findViewById(R.id.icon_frame));
+        mCachedViews.put(AndroidResources.ANDROID_R_ICON_FRAME,
+                itemView.findViewById(AndroidResources.ANDROID_R_ICON_FRAME));
     }
 
     /**
diff --git a/v8/renderscript/Android.mk b/v8/renderscript/Android.mk
index 520c8a1..d695f72 100644
--- a/v8/renderscript/Android.mk
+++ b/v8/renderscript/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_CFLAGS += -std=c++11
 
 LOCAL_MODULE := android-support-v8-renderscript
-LOCAL_SDK_VERSION := 22
+LOCAL_SDK_VERSION := 23
 LOCAL_SRC_FILES := $(call all-java-files-under, java/src)
 LOCAL_JAVA_LIBRARIES := android-support-annotations
 
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java b/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
index 03244ff..c8a8854 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Allocation.java
@@ -19,6 +19,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.nio.ByteBuffer;
+
 import android.content.res.Resources;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -61,8 +63,10 @@
     Type mType;
     Bitmap mBitmap;
     int mUsage;
-    Allocation mAdaptedAllocation;
     int mSize;
+    Allocation mAdaptedAllocation;
+    ByteBuffer mByteBuffer = null;
+    long mByteBufferStride = 0;
 
     boolean mConstrainedLOD;
     boolean mConstrainedFace;
@@ -468,6 +472,56 @@
     }
 
     /**
+     * @hide
+     * Get the ByteBuffer pointing to the raw data associated with Allocation.
+     * Note: The ByteBuffer will be Read-Only for devices before Lollopop (API 21).
+     */
+    public ByteBuffer getByteBuffer() {
+        int xBytesSize = mType.getX() * mType.getElement().getBytesSize();
+        // When running on devices before L, we need to construct the ByteBuffer
+        // and explicitly copy the data from the allocation to it.
+        if (mRS.getDispatchAPILevel() < 21) {
+            byte[] data = null;
+            if (mType.getZ() > 0) {
+                // TODO: add support for 3D allocations.
+                return null;
+            } else if (mType.getY() > 0) {
+                // 2D Allocation
+                data = new byte[xBytesSize * mType.getY()];
+                copy2DRangeToUnchecked(0, 0, mType.getX(), mType.getY(), data,
+                                       Element.DataType.SIGNED_8, xBytesSize * mType.getY());
+            } else {
+                // 1D Allocation
+                data = new byte[xBytesSize];
+                copy1DRangeToUnchecked(0, mType.getX(), data);
+            }
+            ByteBuffer bBuffer = ByteBuffer.wrap(data).asReadOnlyBuffer();
+            mByteBufferStride = xBytesSize;
+            return bBuffer;
+        }
+        // Create a new ByteBuffer if it is not initialized or using IO_INPUT.
+        if (mByteBuffer == null || (mUsage & USAGE_IO_INPUT) != 0) {
+            mByteBuffer = mRS.nAllocationGetByteBuffer(getID(mRS), xBytesSize, mType.getY(), mType.getZ());
+        }
+        return mByteBuffer;
+    }
+
+    /**
+     * @hide
+     * Get the Stride of raw data associated with this 2D Allocation.
+     */
+    public long getStride() {
+        if (mByteBufferStride ==0) {
+            if (mRS.getDispatchAPILevel() > 21) {
+                mByteBufferStride = mRS.nAllocationGetStride(getID(mRS));
+            } else {
+                mByteBufferStride = mType.getX() * mType.getElement().getBytesSize();
+            }
+        }
+        return mByteBufferStride;
+    }
+
+    /**
      * Receive the latest input into the Allocation. This operation
      * is only valid if {@link #USAGE_IO_INPUT} is set on the Allocation.
      *
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index 5dcb50b..d7b7789 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -20,6 +20,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.ArrayList;
+import java.nio.ByteBuffer;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -30,7 +32,6 @@
 import android.os.Process;
 import android.util.Log;
 import android.view.Surface;
-import java.util.ArrayList;
 
 /**
  * This class provides access to a RenderScript context, which controls RenderScript
@@ -49,13 +50,18 @@
     @SuppressWarnings({"UnusedDeclaration", "deprecation"})
     static final boolean LOG_ENABLED = false;
     static final int SUPPORT_LIB_API = 23;
+    static final int SUPPORT_LIB_VERSION = 2301;
 
     static private ArrayList<RenderScript> mProcessContextList = new ArrayList<RenderScript>();
     private boolean mIsProcessContext = false;
+    private boolean mEnableMultiInput = false;
+    // TODO: Update to set to true at the correct API level when reduce is added.
+    private boolean mEnableReduce = false;
+    private int mDispatchAPILevel = 0;
+
     private int mContextFlags = 0;
     private int mContextSdkVersion = 0;
 
-
     private Context mApplicationContext;
     private String mNativeLibDir;
 
@@ -97,7 +103,7 @@
     static Object lock = new Object();
 
     // Non-threadsafe functions.
-    native boolean nLoadSO(boolean useNative, int deviceApi);
+    native boolean nLoadSO(boolean useNative, int deviceApi, String libPath);
     native boolean nLoadIOSO();
     native long nDeviceCreate();
     native void nDeviceDestroy(long dev);
@@ -119,6 +125,9 @@
      */
     public static final int CREATE_FLAG_NONE = 0x0000;
 
+    int getDispatchAPILevel() {
+        return mDispatchAPILevel;
+    }
 
     boolean isUseNative() {
         return useNative;
@@ -165,49 +174,42 @@
 
             if (sNative == 1) {
                 // Workarounds that may disable thunking go here
-                ApplicationInfo info = null;
+                ApplicationInfo info;
                 try {
                     info = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(),
                                                                       PackageManager.GET_META_DATA);
                 } catch (PackageManager.NameNotFoundException e) {
                     // assume no workarounds needed
+                    return true;
+                }
+                long minorVersion = 0;
+
+                // load minorID from reflection
+                try {
+                    Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
+                    Method getMinorID = javaRS.getDeclaredMethod("getMinorID");
+                    minorVersion = ((java.lang.Long)getMinorID.invoke(null)).longValue();
+                } catch (Exception e) {
+                    // minor version remains 0 on devices with no possible WARs
                 }
 
-                if (info != null) {
-                    long minorVersion = 0;
-                    // load minorID from reflection
-                    try {
-                        Class<?> javaRS = Class.forName("android.renderscript.RenderScript");
-                        if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
-                            Method getMinorID = javaRS.getDeclaredMethod("getMinorID");
-                            minorVersion = ((java.lang.Long)getMinorID.invoke(null)).longValue();
-                        } else {
-                            //For M+
-                            Method getMinorVersion = javaRS.getDeclaredMethod("getMinorVersion");
-                            minorVersion = ((java.lang.Long)getMinorVersion.invoke(null)).longValue();
-                        }
-                    } catch (Exception e) {
-                        // minor version remains 0 on devices with no possible WARs
-                    }
-
-                    if (info.metaData != null) {
-                        // asynchronous teardown: minor version 1+
-                        if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
-                            if (minorVersion == 0) {
-                                sNative = 0;
-                            }
-                        }
-
-                        // blur issues on some drivers with 4.4
-                        if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
-                            if (android.os.Build.VERSION.SDK_INT <= 19) {
-                                //android.util.Log.e("rs", "war on");
-                                sNative = 0;
-                            }
+                if (info.metaData != null) {
+                    // asynchronous teardown: minor version 1+
+                    if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableAsyncTeardown") == true) {
+                        if (minorVersion == 0) {
+                            sNative = 0;
                         }
                     }
-                    // end of workarounds
+
+                    // blur issues on some drivers with 4.4
+                    if (info.metaData.getBoolean("com.android.support.v8.renderscript.EnableBlurWorkaround") == true) {
+                        if (android.os.Build.VERSION.SDK_INT <= 19) {
+                            //android.util.Log.e("rs", "war on");
+                            sNative = 0;
+                        }
+                    }
                 }
+                // end of workarounds
             }
         }
 
@@ -435,7 +437,16 @@
         validate();
         rsnAllocationIoReceive(mContext, alloc);
     }
-
+    native ByteBuffer rsnAllocationGetByteBuffer(long con, long alloc, int xBytesSize, int dimY, int dimZ);
+    synchronized ByteBuffer nAllocationGetByteBuffer(long alloc, int xBytesSize, int dimY, int dimZ) {
+        validate();
+        return rsnAllocationGetByteBuffer(mContext, alloc, xBytesSize, dimY, dimZ);
+    }
+    native long rsnAllocationGetStride(long con, long alloc);
+    synchronized long nAllocationGetStride(long alloc) {
+        validate();
+        return rsnAllocationGetStride(mContext, alloc);
+    }
 
     native void rsnAllocationGenerateMipmaps(long con, long alloc);
     synchronized void nAllocationGenerateMipmaps(long alloc) {
@@ -549,7 +560,7 @@
         validate();
         rsnAllocationRead1D(mContext, id, off, mip, count, d, sizeBytes, dt.mID, mSize, usePadding);
     }
-    
+
     /*
     native void rsnAllocationElementRead(long con,long id, int xoff, int yoff, int zoff,
                                          int mip, int compIdx, byte[] d, int sizeBytes);
@@ -651,6 +662,31 @@
         }
     }
 
+    native void rsnScriptForEach(long con, long id, int slot, long[] ains,
+                                 long aout, byte[] params, int[] limits);
+
+    synchronized void nScriptForEach(long id, int slot, long[] ains, long aout,
+                                     byte[] params, int[] limits) {
+        if (!mEnableMultiInput) {
+            Log.e(LOG_TAG, "Multi-input kernels are not supported, please change targetSdkVersion to >= 23");
+            throw new RSRuntimeException("Multi-input kernels are not supported before API 23)");
+        }
+        validate();
+        rsnScriptForEach(mContext, id, slot, ains, aout, params, limits);
+    }
+
+    native void rsnScriptReduce(long con, long id, int slot, long ain, long aout,
+                                int[] limits);
+    synchronized void nScriptReduce(long id, int slot, long ain, long aout, int[] limits) {
+        if (!mEnableReduce) {
+            // TODO: Update to include the API level when reduce is added.
+            Log.e(LOG_TAG, "Reduce kernels are not supported");
+            throw new RSRuntimeException("Reduce kernels are not supported");
+        }
+        validate();
+        rsnScriptReduce(mContext, id, slot, ain, aout, limits);
+    }
+
     native void rsnScriptInvokeV(long con, long id, int slot, byte[] params, boolean mUseInc);
     synchronized void nScriptInvokeV(long id, int slot, byte[] params, boolean mUseInc) {
         validate();
@@ -749,7 +785,7 @@
                     Log.e(LOG_TAG, "Error loading RS Compat library for Incremental Intrinsic Support: " + e);
                     throw new RSRuntimeException("Error loading RS Compat library for Incremental Intrinsic Support: " + e);
                 }
-                if (!nIncLoadSO(SUPPORT_LIB_API)) {
+                if (!nIncLoadSO(SUPPORT_LIB_API, mNativeLibDir + "/libRSSupport.so")) {
                     throw new RSRuntimeException("Error loading libRSSupport library for Incremental Intrinsic Support");
                 }
                 mIncLoaded = true;
@@ -945,7 +981,7 @@
 
 // Additional Entry points For inc libRSSupport
 
-    native boolean nIncLoadSO(int deviceApi);
+    native boolean nIncLoadSO(int deviceApi, String libPath);
     native long nIncDeviceCreate();
     native void nIncDeviceDestroy(long dev);
     // Methods below are wrapped to protect the non-threadsafe
@@ -996,10 +1032,10 @@
         validate();
         return rsnIncTypeCreate(mIncCon, eid, x, y, z, mips, faces, yuv);
     }
-    native long  rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type);
-    synchronized long nIncAllocationCreateTyped(long alloc, long type) {
+    native long  rsnIncAllocationCreateTyped(long con, long incCon, long alloc, long type, int xBytesSize);
+    synchronized long nIncAllocationCreateTyped(long alloc, long type, int xBytesSize) {
         validate();
-        return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type);
+        return rsnIncAllocationCreateTyped(mContext, mIncCon, alloc, type, xBytesSize);
     }
 
     long     mDev;
@@ -1185,6 +1221,14 @@
         }
     }
 
+    void validateObject(BaseObj o) {
+        if (o != null) {
+            if (o.mRS != this) {
+                throw new RSIllegalArgumentException("Attempting to use an object across contexts.");
+            }
+        }
+    }
+
     void validate() {
         if (mContext == 0) {
             throw new RSInvalidStateException("Calling RS with no Context active.");
@@ -1293,7 +1337,10 @@
         mContextType = ContextType.NORMAL;
         if (ctx != null) {
             mApplicationContext = ctx.getApplicationContext();
-            mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
+            // Only set mNativeLibDir for API 9+.
+            if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.FROYO) {
+                mNativeLibDir = mApplicationContext.getApplicationInfo().nativeLibraryDir;
+            }
         }
         mIncDev = 0;
         mIncCon = 0;
@@ -1339,12 +1386,19 @@
                     sUseGCHooks = false;
                 }
                 try {
-                    System.loadLibrary("rsjni");
+                    // For API 9 - 22, always use the absolute path of librsjni.so
+                    // http://b/25226912
+                    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
+                        rs.mNativeLibDir != null) {
+                        System.load(rs.mNativeLibDir + "/librsjni.so");
+                    } else {
+                        System.loadLibrary("rsjni");
+                    }
                     sInitialized = true;
                     sPointerSize = rsnSystemGetPointerSize();
                 } catch (UnsatisfiedLinkError e) {
                     Log.e(LOG_TAG, "Error loading RS jni library: " + e);
-                    throw new RSRuntimeException("Error loading RS jni library: " + e);
+                    throw new RSRuntimeException("Error loading RS jni library: " + e + " Support lib API: " + SUPPORT_LIB_VERSION);
                 }
             }
         }
@@ -1359,24 +1413,39 @@
             useIOlib = true;
         }
 
+        // The target API level used to init dispatchTable.
         int dispatchAPI = sdkVersion;
         if (sdkVersion < android.os.Build.VERSION.SDK_INT) {
             // If the device API is higher than target API level, init dispatch table based on device API.
             dispatchAPI = android.os.Build.VERSION.SDK_INT;
         }
-        if (!rs.nLoadSO(useNative, dispatchAPI)) {
+
+        String rssupportPath = null;
+        // For API 9 - 22, always use the absolute path of libRSSupport.so
+        // http://b/25226912
+        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
+            rs.mNativeLibDir != null) {
+            rssupportPath = rs.mNativeLibDir + "/libRSSupport.so";
+        }
+        if (!rs.nLoadSO(useNative, dispatchAPI, rssupportPath)) {
             if (useNative) {
                 android.util.Log.v(LOG_TAG, "Unable to load libRS.so, falling back to compat mode");
                 useNative = false;
             }
             try {
-                System.loadLibrary("RSSupport");
+                if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M &&
+                    rs.mNativeLibDir != null) {
+                    System.load(rssupportPath);
+                } else {
+                    System.loadLibrary("RSSupport");
+                }
             } catch (UnsatisfiedLinkError e) {
-                Log.e(LOG_TAG, "Error loading RS Compat library: " + e);
-                throw new RSRuntimeException("Error loading RS Compat library: " + e);
+                Log.e(LOG_TAG, "Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
+                throw new RSRuntimeException("Error loading RS Compat library: " + e + " Support lib version: " + SUPPORT_LIB_VERSION);
             }
-            if (!rs.nLoadSO(false, dispatchAPI)) {
-                throw new RSRuntimeException("Error loading libRSSupport library");
+            if (!rs.nLoadSO(false, dispatchAPI, rssupportPath)) {
+                Log.e(LOG_TAG, "Error loading RS Compat library: nLoadSO() failed; Support lib version: " + SUPPORT_LIB_VERSION);
+                throw new RSRuntimeException("Error loading libRSSupport library, Support lib version: " + SUPPORT_LIB_VERSION);
             }
         }
 
@@ -1392,11 +1461,24 @@
             }
         }
 
+        // For old APIs with dlopen bug, need to load blas lib in Java first.
+        // Only try load to blasV8 when the desired API level includes IntrinsicBLAS.
+        if (dispatchAPI >= 23) {
+            // Enable multi-input kernels only when diapatchAPI is M+.
+            rs.mEnableMultiInput = true;
+            try {
+                System.loadLibrary("blasV8");
+            } catch (UnsatisfiedLinkError e) {
+                Log.v(LOG_TAG, "Unable to load BLAS lib, ONLY BNNM will be supported: " + e);
+            }
+        }
+
         rs.mDev = rs.nDeviceCreate();
         rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion, ct.mID, rs.mNativeLibDir);
         rs.mContextType = ct;
         rs.mContextFlags = flags;
         rs.mContextSdkVersion = sdkVersion;
+        rs.mDispatchAPILevel = dispatchAPI;
         if (rs.mContext == 0) {
             throw new RSDriverException("Failed to create RS context.");
         }
@@ -1595,9 +1677,21 @@
         }
         nContextDeinitToClient(mContext);
         mMessageThread.mRun = false;
-        try {
-            mMessageThread.join();
-        } catch(InterruptedException e) {
+
+        // Wait for mMessageThread to join.  Try in a loop, in case this thread gets interrupted
+        // during the wait.  If interrupted, set the "interrupted" status of the current thread.
+        boolean hasJoined = false, interrupted = false;
+        while (!hasJoined) {
+            try {
+                mMessageThread.join();
+                hasJoined = true;
+            } catch (InterruptedException e) {
+                interrupted = true;
+            }
+        }
+        if (interrupted) {
+            Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join");
+            Thread.currentThread().interrupt();
         }
 
         nContextDestroy();
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
index cacd146..7cb52ae 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/Script.java
@@ -44,9 +44,11 @@
         long dInType = 0;
         long dummyAlloc = 0;
         if (ain != null) {
-            dInElement = ain.getType().getElement().getDummyElement(mRS);
-            dInType = ain.getType().getDummyType(mRS, dInElement);
-            dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType);
+            Type inType = ain.getType();
+            dInElement = inType.getElement().getDummyElement(mRS);
+            dInType = inType.getDummyType(mRS, dInElement);
+            int xBytesSize = inType.getX() * inType.getElement().getBytesSize();
+            dummyAlloc = mRS.nIncAllocationCreateTyped(ain.getID(mRS), dInType, xBytesSize);
             ain.setIncAllocID(dummyAlloc);
         }
 
@@ -317,6 +319,101 @@
     /**
      * Only intended for use by generated reflected code.
      *
+     * @hide
+     */
+    protected void forEach(int slot, Allocation[] ains, Allocation aout,
+                           FieldPacker v) {
+        forEach(slot, ains, aout, v, null);
+    }
+
+    /**
+     * Only intended for use by generated reflected code.
+     *
+     * @hide
+     */
+    protected void forEach(int slot, Allocation[] ains, Allocation aout,
+                           FieldPacker v, LaunchOptions sc) {
+        // TODO: Is this necessary if nScriptForEach calls validate as well?
+        mRS.validate();
+        if (ains != null) {
+            for (Allocation ain : ains) {
+                mRS.validateObject(ain);
+            }
+        }
+        mRS.validateObject(aout);
+
+        if (ains == null && aout == null) {
+            throw new RSIllegalArgumentException(
+                "At least one of ain or aout is required to be non-null.");
+        }
+
+        long[] in_ids;
+        if (ains != null) {
+            in_ids = new long[ains.length];
+            for (int index = 0; index < ains.length; ++index) {
+                in_ids[index] = ains[index].getID(mRS);
+            }
+        } else {
+            in_ids = null;
+        }
+
+        long out_id = 0;
+        if (aout != null) {
+            out_id = aout.getID(mRS);
+        }
+
+        byte[] params = null;
+        if (v != null) {
+            params = v.getData();
+        }
+
+        int[] limits = null;
+        if (sc != null) {
+            limits = new int[6];
+
+            limits[0] = sc.xstart;
+            limits[1] = sc.xend;
+            limits[2] = sc.ystart;
+            limits[3] = sc.yend;
+            limits[4] = sc.zstart;
+            limits[5] = sc.zend;
+        }
+
+        mRS.nScriptForEach(getID(mRS), slot, in_ids, out_id, params, limits);
+    }
+
+    /**
+     * Only intended for use by generated reflected code.
+     *
+     * @hide
+     */
+    protected void reduce(int slot, Allocation ain, Allocation aout, LaunchOptions sc) {
+        mRS.validate();
+        mRS.validateObject(ain);
+        mRS.validateObject(aout);
+
+        if (ain == null || aout == null) {
+            throw new RSIllegalArgumentException(
+                "Both ain and aout are required to be non-null.");
+        }
+
+        long in_id = ain.getID(mRS);
+        long out_id = aout.getID(mRS);
+
+        int[] limits = null;
+        if (sc != null) {
+            limits = new int[2];
+
+            limits[0] = sc.xstart;
+            limits[1] = sc.xend;
+        }
+
+        mRS.nScriptReduce(getID(mRS), slot, in_id, out_id, limits);
+    }
+
+    /**
+     * Only intended for use by generated reflected code.
+     *
      * @param index
      * @param v
      */
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
index 15e8b55..065712a 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptGroup.java
@@ -307,10 +307,10 @@
                     value = ((Long)obj).longValue();
                     size = 8;
                 } else if (obj instanceof Float) {
-                    value = ((Float)obj).longValue();
+                    value = Float.floatToRawIntBits(((Float)obj).floatValue());
                     size = 4;
                 } else if (obj instanceof Double) {
-                    value = ((Double)obj).longValue();
+                    value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
                     size = 8;
                 }
             }
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
index a5b046b..2d2923e 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicBlur.java
@@ -97,7 +97,7 @@
      *             type.
      */
     public void forEach(Allocation aout) {
-        forEach(0, null, aout, null);
+        forEach(0, (Allocation) null, aout, null);
     }
 
     /**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
index ea45461..0d398b7 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -115,7 +115,7 @@
      *             type.
      */
     public void forEach(Allocation aout) {
-        forEach(0, null, aout, null);
+        forEach(0, (Allocation) null, aout, null);
     }
 
     /**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
index bcd37f1..9dd9cba 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -117,7 +117,7 @@
      *             type.
      */
     public void forEach(Allocation aout) {
-        forEach(0, null, aout, null);
+        forEach(0, (Allocation) null, aout, null);
     }
 
     /**
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
index f3d0df7..6c84020 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -74,7 +74,7 @@
      *             type.
      */
     public void forEach(Allocation aout) {
-        forEach(0, null, aout, null);
+        forEach(0, (Allocation) null, aout, null);
     }
 
     /**
diff --git a/v8/renderscript/jni/Android.mk b/v8/renderscript/jni/Android.mk
index d2af4a9..72f8720 100644
--- a/v8/renderscript/jni/Android.mk
+++ b/v8/renderscript/jni/Android.mk
@@ -50,6 +50,6 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_REQUIRED_MODULES := libRSSupport
 
-LOCAL_LDFLAGS += -ldl
+LOCAL_LDFLAGS += -ldl -llog
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index 9d69a2d..eea462c 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -30,6 +30,8 @@
 
 //#define LOG_API ALOG
 #define LOG_API(...)
+#define LOG_ERR(...) __android_log_print(ANDROID_LOG_ERROR, "RenderScript JNI", __VA_ARGS__);
+#define RS_JNI_VERSION 2301
 
 #define NELEM(m) (sizeof(m) / sizeof((m)[0]))
 
@@ -269,23 +271,31 @@
 // Incremental Support lib
 static dispatchTable dispatchTabInc;
 
-static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative, jint targetApi) {
+static jboolean nLoadSO(JNIEnv *_env, jobject _this, jboolean useNative, jint targetApi, jstring libPath) {
     void* handle = NULL;
     if (useNative) {
         handle = dlopen("libRS.so", RTLD_LAZY | RTLD_LOCAL);
     } else {
-        handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+        // For API 9+, dlopen the full path of libRSSupport.
+        if (libPath != NULL) {
+            const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
+            handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
+            _env->ReleaseStringUTFChars(libPath, libPathJni);
+        } else {
+            handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+        }
     }
     if (handle == NULL) {
-        LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+        LOG_ERR("couldn't dlopen %s; librsjni version: %d", dlerror(), RS_JNI_VERSION);
         return false;
     }
 
     if (loadSymbols(handle, dispatchTab, targetApi) == false) {
-        LOG_API("%s init failed!", filename);
+        LOG_ERR("Dispatch table init failed! librsjni version: %d", RS_JNI_VERSION);
+        dlclose(handle);
         return false;
     }
-    LOG_API("Successfully loaded %s", filename);
+    LOG_API("Successfully loaded runtime");
     return true;
 }
 
@@ -294,11 +304,11 @@
     void* handleIO = NULL;
     handleIO = dlopen("libRSSupportIO.so", RTLD_LAZY | RTLD_LOCAL);
     if (handleIO == NULL) {
-        LOG_API("Couldn't load libRSSupportIO.so");
+        LOG_ERR("Couldn't load libRSSupportIO.so, librsjni version: %d", RS_JNI_VERSION);
         return false;
     }
     if (loadIOSuppSyms(handleIO, ioDispatch) == false) {
-        LOG_API("libRSSupportIO init failed!");
+        LOG_ERR("libRSSupportIO init failed! librsjni version: %d", RS_JNI_VERSION);
         return false;
     }
     return true;
@@ -362,31 +372,30 @@
 
   size_t numValues, numDependencies;
   RsScriptFieldID* fieldIDs;
-  uintptr_t* values;
   RsClosure* depClosures;
   RsScriptFieldID* depFieldIDs;
 
   if (fieldIDs_length != values_length || values_length != sizes_length) {
-      LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+      LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
       goto exit;
   }
 
   numValues = (size_t)fieldIDs_length;
 
   if (depClosures_length != depFieldIDs_length) {
-      LOG_API("Unmatched closures and field IDs for dependencies in closure creation.");
+      LOG_ERR("Unmatched closures and field IDs for dependencies in closure creation.");
       goto exit;
   }
 
   numDependencies = (size_t)depClosures_length;
 
   if (numDependencies > numValues) {
-      LOG_API("Unexpected number of dependencies in closure creation");
+      LOG_ERR("Unexpected number of dependencies in closure creation");
       goto exit;
   }
 
   if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
-      LOG_API("Too many arguments or globals in closure creation");
+      LOG_ERR("Too many arguments or globals in closure creation");
       goto exit;
   }
 
@@ -399,15 +408,6 @@
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
-  if (values == nullptr) {
-      goto exit;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
   depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
   if (depClosures == nullptr) {
       goto exit;
@@ -428,7 +428,7 @@
 
   ret = (jlong)(uintptr_t)dispatchTab.ClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
-      fieldIDs, numValues, values, numValues,
+      fieldIDs, numValues, jValues, numValues,
       (int*)jSizes, numValues,
       depClosures, numDependencies,
       depFieldIDs, numDependencies);
@@ -461,17 +461,16 @@
 
   size_t numValues;
   RsScriptFieldID* fieldIDs;
-  uintptr_t* values;
 
   if (fieldIDs_length != values_length || values_length != sizes_length) {
-      LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+      LOG_ERR("Unmatched field IDs, values, and sizes in closure creation.");
       goto exit;
   }
 
   numValues = (size_t) fieldIDs_length;
 
   if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
-      LOG_API("Too many arguments or globals in closure creation");
+      LOG_ERR("Too many arguments or globals in closure creation");
       goto exit;
   }
 
@@ -484,18 +483,9 @@
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
-  if (values == nullptr) {
-      goto exit;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
   ret = (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
-      fieldIDs, numValues, values, numValues,
+      fieldIDs, numValues, jValues, numValues,
       (int*)jSizes, numValues);
 
 exit:
@@ -511,15 +501,17 @@
 static void
 nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                jint index, jlong value, jint size) {
+  // Size is signed with -1 indicating the values is an Allocation
   dispatchTab.ClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
-                  (uintptr_t)value, (size_t)size);
+                  (uintptr_t)value, size);
 }
 
 static void
 nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                   jlong fieldID, jlong value, jint size) {
+  // Size is signed with -1 indicating the values is an Allocation
   dispatchTab.ClosureSetGlobal((RsContext)con, (RsClosure)closureID,
-                     (RsScriptFieldID)fieldID, (uintptr_t)value, (size_t)size);
+                     (RsScriptFieldID)fieldID, (int64_t)value, size);
 }
 
 static long
@@ -536,7 +528,7 @@
   RsClosure* closures;
 
   if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
-    LOG_API("Too many closures in script group");
+    LOG_ERR("Too many closures in script group");
     goto exit;
   }
 
@@ -912,7 +904,7 @@
     jint len = 0;
     if (data) {
         len = _env->GetArrayLength(data);
-        jint *ptr = _env->GetIntArrayElements(data, NULL);
+        ptr = _env->GetIntArrayElements(data, NULL);
     }
     LOG_API("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
     dispatchTab.ContextSendMessage((RsContext)con, id, (const uint8_t *)ptr, len * sizeof(int));
@@ -1546,6 +1538,7 @@
                                   NULL, 0, NULL, 0);
     }
 }
+
 static void
 nScriptForEachV(JNIEnv *_env, jobject _this, jlong con, jlong incCon,
                 jlong script, jint slot, jlong ain, jlong aout, jbyteArray params, jboolean mUseInc)
@@ -1627,6 +1620,165 @@
     _env->ReleaseByteArrayElements(params, ptr, JNI_ABORT);
 }
 
+static void
+nScriptForEachMulti(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
+                    jlongArray ains, jlong aout, jbyteArray params,
+                    jintArray limits)
+{
+    LOG_API("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
+
+    jint   in_len = 0;
+    jlong *in_ptr = nullptr;
+
+    RsAllocation *in_allocs = nullptr;
+
+    if (ains != nullptr) {
+        in_len = _env->GetArrayLength(ains);
+        if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) {
+            LOG_ERR("Too many arguments in kernel launch.");
+            // TODO (b/20758983): Report back to Java and throw an exception
+            return;
+        }
+
+        // TODO (b/20760800): Check in_ptr is not null
+        in_ptr = _env->GetLongArrayElements(ains, nullptr);
+        if (sizeof(RsAllocation) == sizeof(jlong)) {
+            in_allocs = (RsAllocation*)in_ptr;
+
+        } else {
+            // Convert from 64-bit jlong types to the native pointer type.
+
+            in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
+            if (in_allocs == nullptr) {
+                LOG_ERR("Failed launching kernel for lack of memory.");
+                _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+                return;
+            }
+
+            for (int index = in_len; --index >= 0;) {
+                in_allocs[index] = (RsAllocation)in_ptr[index];
+            }
+        }
+    }
+
+    jint   param_len = 0;
+    jbyte *param_ptr = nullptr;
+
+    if (params != nullptr) {
+        param_len = _env->GetArrayLength(params);
+        param_ptr = _env->GetByteArrayElements(params, nullptr);
+    }
+
+    RsScriptCall sc, *sca = nullptr;
+    uint32_t sc_size = 0;
+
+    jint  limit_len = 0;
+    jint *limit_ptr = nullptr;
+
+    if (limits != nullptr) {
+        limit_len = _env->GetArrayLength(limits);
+        limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+
+        if (limit_len != 6) {
+            LOG_ERR("LaunchOptions cannot be recognized.");
+            goto exit;
+        }
+
+        sc.xStart     = limit_ptr[0];
+        sc.xEnd       = limit_ptr[1];
+        sc.yStart     = limit_ptr[2];
+        sc.yEnd       = limit_ptr[3];
+        sc.zStart     = limit_ptr[4];
+        sc.zEnd       = limit_ptr[5];
+        sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
+        sc.arrayStart = 0;
+        sc.arrayEnd = 0;
+        sc.array2Start = 0;
+        sc.array2End = 0;
+        sc.array3Start = 0;
+        sc.array3End = 0;
+        sc.array4Start = 0;
+        sc.array4End = 0;
+
+        sca = &sc;
+    }
+
+    dispatchTabInc.ScriptForEachMulti((RsContext)con, (RsScript)script, slot,
+                                      in_allocs, in_len, (RsAllocation)aout,
+                                      param_ptr, param_len, sca, sc_size);
+
+exit:
+
+    if (ains != nullptr) {
+        _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+    }
+
+    if (params != nullptr) {
+        _env->ReleaseByteArrayElements(params, param_ptr, JNI_ABORT);
+    }
+
+    if (limits != nullptr) {
+        _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
+    }
+}
+
+static void
+nScriptReduce(JNIEnv *_env, jobject _this, jlong con, jlong script, jint slot,
+              jlong ain, jlong aout, jintArray limits)
+{
+    LOG_API("nScriptReduce, con(%p), s(%p), slot(%i) ain(%" PRId64 ") aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ain, aout);
+
+    RsScriptCall sc, *sca = nullptr;
+    uint32_t sc_size = 0;
+
+    jint  limit_len = 0;
+    jint *limit_ptr = nullptr;
+
+    bool limitLengthValid = true;
+
+    // If the caller passed limits, reflect them in the RsScriptCall.
+    if (limits != nullptr) {
+        limit_len = _env->GetArrayLength(limits);
+        limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+
+        // We expect to be passed an array [x1, x2] which specifies
+        // the sub-range for a 1-dimensional reduction.
+        if (limit_len == 2) {
+            sc.xStart     = limit_ptr[0];
+            sc.xEnd       = limit_ptr[1];
+            sc.yStart     = 0;
+            sc.yEnd       = 0;
+            sc.zStart     = 0;
+            sc.zEnd       = 0;
+            sc.strategy   = RS_FOR_EACH_STRATEGY_DONT_CARE;
+            sc.arrayStart = 0;
+            sc.arrayEnd = 0;
+            sc.array2Start = 0;
+            sc.array2End = 0;
+            sc.array3Start = 0;
+            sc.array3End = 0;
+            sc.array4Start = 0;
+            sc.array4End = 0;
+
+            sca = &sc;
+            sc_size = sizeof(sc);
+        } else {
+            LOG_ERR("LaunchOptions cannot be recognized.");
+            limitLengthValid = false;
+        }
+    }
+
+    if (limitLengthValid) {
+        dispatchTab.ScriptReduce((RsContext)con, (RsScript)script, slot,
+                                 (RsAllocation)ain, (RsAllocation)aout,
+                                 sca, sc_size);
+    }
+
+    if (limits != nullptr) {
+        _env->ReleaseIntArrayElements(limits, limit_ptr, JNI_ABORT);
+    }
+}
+
 // -----------------------------------
 
 static jlong
@@ -1831,19 +1983,34 @@
 
 // ---------------------------------------------------------------------------
 // For Incremental Intrinsic Support
-static jboolean nIncLoadSO(JNIEnv *_env, jobject _this, jint deviceApi) {
+static jboolean nIncLoadSO(JNIEnv *_env, jobject _this, jint deviceApi, jstring libPath) {
     void* handle = NULL;
-    handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+    // For API 9+, dlopen the full path of libRSSupport.
+    if (libPath != NULL) {
+        const char * libPathJni = _env->GetStringUTFChars(libPath, JNI_FALSE);
+        handle = dlopen(libPathJni, RTLD_LAZY | RTLD_LOCAL);
+        _env->ReleaseStringUTFChars(libPath, libPathJni);
+    } else {
+        handle = dlopen("libRSSupport.so", RTLD_LAZY | RTLD_LOCAL);
+    }
+
     if (handle == NULL) {
-        LOG_API("couldn't dlopen %s, %s", filename, dlerror());
+        LOG_ERR("couldn't dlopen %s;  librsjni version: %d", dlerror(), RS_JNI_VERSION);
         return false;
     }
 
     if (loadSymbols(handle, dispatchTabInc, deviceApi) == false) {
-        LOG_API("%s init failed!", filename);
+        LOG_ERR("Dispatch Table init failed! librsjni version: %d", RS_JNI_VERSION);
+        dlclose(handle);
         return false;
     }
-    LOG_API("Successfully loaded %s", filename);
+    dispatchTabInc.AllocationCreateStrided = (AllocationCreateStridedFnPtr)dlsym(handle, "rsAllocationCreateStrided");
+    if (dispatchTabInc.AllocationCreateStrided == NULL) {
+        LOG_ERR("Couldn't initialize dispatchTabInc.AllocationCreateStrided");
+        dlclose(handle);
+        return false;
+    }
+    LOG_API("Successfully loaded compat runtime");
     return true;
 }
 
@@ -1921,7 +2088,7 @@
 // -----------------------------------
 // Create Allocation from pointer
 static jlong
-nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type)
+nIncAllocationCreateTyped(JNIEnv *_env, jobject _this, jlong con, jlong incCon, jlong alloc, jlong type, jint xBytesSize)
 {
     LOG_API("nAllocationCreateTyped, con(%p), type(%p), mip(%i), usage(%i), ptr(%p)",
             incCon, (RsElement)type, mips, usage, (void *)pointer);
@@ -1932,21 +2099,77 @@
         pIn = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
                                                RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
                                                &strideIn, sizeof(size_t));
-        ainI = dispatchTabInc.AllocationCreateTyped((RsContext)incCon, (RsType)type,
-                                                    RS_ALLOCATION_MIPMAP_NONE,
-                                                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
-                                                    (uintptr_t)pIn);
+        /*
+         * By definition stride is a roundup of xBytesSize with requiredAlignment, so requiredAlignment must
+         * be strictly larger than the difference of (stride - xBytesSize).
+         *
+         * We can prove that as long as requiredAlignment satisfies the following two conditions, the
+         * memory layout will be identical :
+         * 1. Smaller or equal than stride;
+         * 2. Larger than minRequiredAlignment.
+         *
+         * In this case we can simply choose the first power of 2 that satisfies both conditions.
+         */
+        size_t requiredAlignment = 16;
+        size_t minRequiredAlignment = strideIn - xBytesSize;
+        while (requiredAlignment <= minRequiredAlignment) {
+            requiredAlignment <<= 1;
+        }
+        ainI = dispatchTabInc.AllocationCreateStrided((RsContext)incCon, (RsType)type,
+                                                      RS_ALLOCATION_MIPMAP_NONE,
+                                                      RS_ALLOCATION_USAGE_INCREMENTAL_SUPPORT | RS_ALLOCATION_USAGE_SHARED,
+                                                      (uintptr_t)pIn, requiredAlignment);
     }
     return (jlong)(uintptr_t) ainI;
 }
 
+static jobject
+nAllocationGetByteBuffer(JNIEnv *_env, jobject _this, jlong con, jlong alloc, jint xBytesSize, jint dimY, jint dimZ)
+{
+    LOG_API("nAllocationGetByteBuffer, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
+    size_t strideIn = xBytesSize;
+    void* ptr = NULL;
+    if (alloc != 0 && dispatchTab.AllocationGetPointer != nullptr) {
+        ptr = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
+                                               RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, dimZ, 0,
+                                               &strideIn, sizeof(size_t));
+    }
+    if (ptr != NULL) {
+        size_t bufferSize = strideIn;
+        if (dimY > 0) {
+            bufferSize *= dimY;
+        }
+        if (dimZ > 0) {
+            bufferSize *= dimZ;
+        }
+        jobject byteBuffer = _env->NewDirectByteBuffer(ptr, (jlong) bufferSize);
+        return byteBuffer;
+    } else {
+        return NULL;
+    }
+}
+
+static jlong
+nAllocationGetStride(JNIEnv *_env, jobject _this, jlong con, jlong alloc)
+{
+    LOG_API("nAllocationGetStride, con(%p), alloc(%p)", (RsContext)con, (RsAllocation)alloc);
+    size_t strideIn;
+    void* ptr = NULL;
+    if (alloc != 0 && dispatchTab.AllocationGetPointer != nullptr) {
+        ptr = dispatchTab.AllocationGetPointer((RsContext)con, (RsAllocation)alloc, 0,
+                                               RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, 0, 0,
+                                               &strideIn, sizeof(size_t));
+    }
+    return (jlong)strideIn;
+}
+
 // ---------------------------------------------------------------------------
 
 
 static const char *classPathName = "android/support/v8/renderscript/RenderScript";
 
 static JNINativeMethod methods[] = {
-{"nLoadSO",                        "(ZI)Z",                                   (bool*)nLoadSO },
+{"nLoadSO",                        "(ZILjava/lang/String;)Z",                 (bool*)nLoadSO },
 {"nLoadIOSO",                      "()Z",                                     (bool*)nLoadIOSO },
 {"nDeviceCreate",                  "()J",                                     (void*)nDeviceCreate },
 {"nDeviceDestroy",                 "(J)V",                                    (void*)nDeviceDestroy },
@@ -2010,8 +2233,10 @@
 {"rsnScriptInvokeV",                 "(JJI[BZ)V",                             (void*)nScriptInvokeV },
 {"rsnScriptForEach",                 "(JJJIJJZ)V",                            (void*)nScriptForEach },
 {"rsnScriptForEach",                 "(JJJIJJ[BZ)V",                          (void*)nScriptForEachV },
+{"rsnScriptForEach",                 "(JJI[JJ[B[I)V",                         (void*)nScriptForEachMulti },
 {"rsnScriptForEachClipped",          "(JJJIJJIIIIIIZ)V",                      (void*)nScriptForEachClipped },
 {"rsnScriptForEachClipped",          "(JJJIJJ[BIIIIIIZ)V",                    (void*)nScriptForEachClippedV },
+{"rsnScriptReduce",                  "(JJIJJ[I)V",                            (void*)nScriptReduce },
 {"rsnScriptSetVarI",                 "(JJIIZ)V",                              (void*)nScriptSetVarI },
 {"rsnScriptSetVarJ",                 "(JJIJZ)V",                              (void*)nScriptSetVarJ },
 {"rsnScriptSetVarF",                 "(JJIFZ)V",                              (void*)nScriptSetVarF },
@@ -2044,7 +2269,7 @@
 {"rsnSystemGetPointerSize",          "()I",                                   (void*)nSystemGetPointerSize },
 
 // Entry points for Inc libRSSupport
-{"nIncLoadSO",                       "(I)Z",                                  (bool*)nIncLoadSO },
+{"nIncLoadSO",                       "(ILjava/lang/String;)Z",                (bool*)nIncLoadSO },
 {"nIncDeviceCreate",                 "()J",                                   (void*)nIncDeviceCreate },
 {"nIncDeviceDestroy",                "(J)V",                                  (void*)nIncDeviceDestroy },
 {"rsnIncContextCreate",              "(JIII)J",                               (void*)nIncContextCreate },
@@ -2053,7 +2278,9 @@
 {"rsnIncObjDestroy",                 "(JJ)V",                                 (void*)nIncObjDestroy },
 {"rsnIncElementCreate",              "(JJIZI)J",                              (void*)nIncElementCreate },
 {"rsnIncTypeCreate",                 "(JJIIIZZI)J",                           (void*)nIncTypeCreate },
-{"rsnIncAllocationCreateTyped",      "(JJJJ)J",                               (void*)nIncAllocationCreateTyped },
+{"rsnIncAllocationCreateTyped",      "(JJJJI)J",                              (void*)nIncAllocationCreateTyped },
+{"rsnAllocationGetByteBuffer",       "(JJIII)Ljava/nio/ByteBuffer;",          (void*)nAllocationGetByteBuffer },
+{"rsnAllocationGetStride",           "(JJ)J",                                 (void*)nAllocationGetStride },
 };
 
 // ---------------------------------------------------------------------------
diff --git a/v8/renderscript/rs_support/Android.mk b/v8/renderscript/rs_support/Android.mk
index c3f2401..7781fdf 100644
--- a/v8/renderscript/rs_support/Android.mk
+++ b/v8/renderscript/rs_support/Android.mk
@@ -29,7 +29,7 @@
     rsg_generator.c
 
 LOCAL_CXX_STL := none
-LOCAL_ADDRESS_SANITIZER := false
+LOCAL_SANITIZE := never
 
 include $(BUILD_HOST_EXECUTABLE)
 
@@ -53,8 +53,8 @@
         )
 
 $(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR_SUPPORT) $< $@ <$(PRIVATE_PATH)/rs.spec
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec
+$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
+$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
 $(GEN): $(generated_sources_dir)/%.h : $(LOCAL_PATH)/%.h.rsg
 	$(transform-generated-source)
 
@@ -70,8 +70,8 @@
         )
 
 $(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR_SUPPORT) $< $@ <$(PRIVATE_PATH)/rs.spec
-$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec
+$(GEN) : PRIVATE_CUSTOM_TOOL = cat $(PRIVATE_PATH)/rs.spec $(PRIVATE_PATH)/rs_compat.spec | $(RSG_GENERATOR_SUPPORT) $< $@
+$(GEN) : $(RSG_GENERATOR_SUPPORT) $(LOCAL_PATH)/rs.spec $(LOCAL_PATH)/rs_compat.spec
 $(GEN): $(generated_sources_dir)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
 	$(transform-generated-source)
 
@@ -81,7 +81,6 @@
 LOCAL_GENERATED_SOURCES += $(GEN)
 
 LOCAL_SRC_FILES:= \
-	rsAdapter.cpp \
 	rsAllocation.cpp \
 	rsClosure.cpp \
 	rsCompatibilityLib.cpp \
@@ -139,7 +138,7 @@
 ifeq ($(ARCH_ARM_HAVE_ARMV7A),true)
 LOCAL_CFLAGS_arm := -DARCH_ARM_HAVE_VFP -DARCH_ARM_USE_INTRINSICS
 LOCAL_ASFLAGS_arm := -mfpu=neon
-# frameworks/rs/cpu_ref/rsCpuIntrinsics_neon_3DLUT.S does not compile.
+# Clang does not support nested .irp in *_Blur.S
 LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
 LOCAL_SRC_FILES_arm := \
         cpu_ref/rsCpuIntrinsics_neon_3DLUT.S \
@@ -152,13 +151,15 @@
 endif
 
 LOCAL_REQUIRED_MODULES := libblasV8
+LOCAL_STATIC_LIBRARIES := libbnnmlowpV8
 LOCAL_LDFLAGS += -llog -ldl
 LOCAL_NDK_STL_VARIANT := stlport_static
 
 LOCAL_C_INCLUDES += frameworks/compile/libbcc/include
 LOCAL_C_INCLUDES += external/cblas/include
+LOCAL_C_INCLUDES += external/gemmlowp/eight_bit_int_gemm
 
-LOCAL_CFLAGS += $(rs_base_CFLAGS)
+LOCAL_CFLAGS += $(rs_base_CFLAGS) -DGEMMLOWP_USE_STLPORT
 
 LOCAL_MODULE:= libRSSupport
 LOCAL_MODULE_TAGS := optional