Merge "Changes of root storage space unmounting time on Zygote Process" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index c82c04d..a5d8a93 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -944,6 +944,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2754,14 +2756,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -47196,6 +47194,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -50689,6 +50689,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -50770,6 +50771,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
@@ -57347,6 +57349,7 @@
     method public E get(int);
     method public boolean removeIf(java.util.function.Predicate<? super E>);
     method public int size();
+    method public void sort(java.util.Comparator<? super E>);
     method public java.util.Spliterator<E> spliterator();
     method public void trimToSize();
   }
@@ -57478,6 +57481,14 @@
     method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
     method public static java.util.Spliterator.OfDouble spliterator(double[]);
     method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
+    method public static java.util.stream.Stream<T> stream(T[]);
+    method public static java.util.stream.Stream<T> stream(T[], int, int);
+    method public static java.util.stream.IntStream stream(int[]);
+    method public static java.util.stream.IntStream stream(int[], int, int);
+    method public static java.util.stream.LongStream stream(long[]);
+    method public static java.util.stream.LongStream stream(long[], int, int);
+    method public static java.util.stream.DoubleStream stream(double[]);
+    method public static java.util.stream.DoubleStream stream(double[], int, int);
     method public static java.lang.String toString(long[]);
     method public static java.lang.String toString(int[]);
     method public static java.lang.String toString(short[]);
@@ -57637,11 +57648,13 @@
     method public abstract int hashCode();
     method public abstract boolean isEmpty();
     method public abstract java.util.Iterator<E> iterator();
+    method public default java.util.stream.Stream<E> parallelStream();
     method public abstract boolean remove(java.lang.Object);
     method public abstract boolean removeAll(java.util.Collection<?>);
     method public default boolean removeIf(java.util.function.Predicate<? super E>);
     method public abstract boolean retainAll(java.util.Collection<?>);
     method public abstract int size();
+    method public default java.util.stream.Stream<E> stream();
     method public abstract java.lang.Object[] toArray();
     method public abstract T[] toArray(T[]);
   }
@@ -58841,6 +58854,34 @@
     method public java.util.Spliterator<T> trySplit();
   }
 
+  public final class SplittableRandom {
+    ctor public SplittableRandom(long);
+    ctor public SplittableRandom();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
+    method public boolean nextBoolean();
+    method public double nextDouble();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt();
+    method public int nextInt(int);
+    method public int nextInt(int, int);
+    method public long nextLong();
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+    method public java.util.SplittableRandom split();
+  }
+
   public class Stack extends java.util.Vector {
     ctor public Stack();
     method public boolean empty();
@@ -60024,6 +60065,18 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -61425,6 +61478,292 @@
 
 }
 
+package java.util.stream {
+
+  public abstract interface BaseStream implements java.lang.AutoCloseable {
+    method public abstract void close();
+    method public abstract boolean isParallel();
+    method public abstract java.util.Iterator<T> iterator();
+    method public abstract S onClose(java.lang.Runnable);
+    method public abstract S parallel();
+    method public abstract S sequential();
+    method public abstract java.util.Spliterator<T> spliterator();
+    method public abstract S unordered();
+  }
+
+  public abstract interface Collector {
+    method public abstract java.util.function.BiConsumer<A, T> accumulator();
+    method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics();
+    method public abstract java.util.function.BinaryOperator<A> combiner();
+    method public abstract java.util.function.Function<A, R> finisher();
+    method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...);
+    method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...);
+    method public abstract java.util.function.Supplier<A> supplier();
+  }
+
+  public static final class Collector.Characteristics extends java.lang.Enum {
+    method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String);
+    method public static final java.util.stream.Collector.Characteristics[] values();
+    enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT;
+    enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH;
+    enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED;
+  }
+
+  public final class Collectors {
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> counting();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining();
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+    method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet();
+  }
+
+  public abstract interface DoubleStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.DoublePredicate);
+    method public abstract boolean anyMatch(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Double> boxed();
+    method public static java.util.stream.DoubleStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream);
+    method public abstract long count();
+    method public abstract java.util.stream.DoubleStream distinct();
+    method public static java.util.stream.DoubleStream empty();
+    method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble findAny();
+    method public abstract java.util.OptionalDouble findFirst();
+    method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>);
+    method public abstract void forEach(java.util.function.DoubleConsumer);
+    method public abstract void forEachOrdered(java.util.function.DoubleConsumer);
+    method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier);
+    method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfDouble iterator();
+    method public abstract java.util.stream.DoubleStream limit(long);
+    method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>);
+    method public abstract java.util.OptionalDouble max();
+    method public abstract java.util.OptionalDouble min();
+    method public abstract boolean noneMatch(java.util.function.DoublePredicate);
+    method public static java.util.stream.DoubleStream of(double);
+    method public static java.util.stream.DoubleStream of(double...);
+    method public abstract java.util.stream.DoubleStream parallel();
+    method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer);
+    method public abstract double reduce(double, java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.stream.DoubleStream sequential();
+    method public abstract java.util.stream.DoubleStream skip(long);
+    method public abstract java.util.stream.DoubleStream sorted();
+    method public abstract java.util.Spliterator.OfDouble spliterator();
+    method public abstract double sum();
+    method public abstract java.util.DoubleSummaryStatistics summaryStatistics();
+    method public abstract double[] toArray();
+  }
+
+  public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer {
+    method public abstract void accept(double);
+    method public default java.util.stream.DoubleStream.Builder add(double);
+    method public abstract java.util.stream.DoubleStream build();
+  }
+
+  public abstract interface IntStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.IntPredicate);
+    method public abstract boolean anyMatch(java.util.function.IntPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.stream.LongStream asLongStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Integer> boxed();
+    method public static java.util.stream.IntStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream);
+    method public abstract long count();
+    method public abstract java.util.stream.IntStream distinct();
+    method public static java.util.stream.IntStream empty();
+    method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate);
+    method public abstract java.util.OptionalInt findAny();
+    method public abstract java.util.OptionalInt findFirst();
+    method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>);
+    method public abstract void forEach(java.util.function.IntConsumer);
+    method public abstract void forEachOrdered(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream generate(java.util.function.IntSupplier);
+    method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfInt iterator();
+    method public abstract java.util.stream.IntStream limit(long);
+    method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>);
+    method public abstract java.util.OptionalInt max();
+    method public abstract java.util.OptionalInt min();
+    method public abstract boolean noneMatch(java.util.function.IntPredicate);
+    method public static java.util.stream.IntStream of(int);
+    method public static java.util.stream.IntStream of(int...);
+    method public abstract java.util.stream.IntStream parallel();
+    method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream range(int, int);
+    method public static java.util.stream.IntStream rangeClosed(int, int);
+    method public abstract int reduce(int, java.util.function.IntBinaryOperator);
+    method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator);
+    method public abstract java.util.stream.IntStream sequential();
+    method public abstract java.util.stream.IntStream skip(long);
+    method public abstract java.util.stream.IntStream sorted();
+    method public abstract java.util.Spliterator.OfInt spliterator();
+    method public abstract int sum();
+    method public abstract java.util.IntSummaryStatistics summaryStatistics();
+    method public abstract int[] toArray();
+  }
+
+  public static abstract interface IntStream.Builder implements java.util.function.IntConsumer {
+    method public abstract void accept(int);
+    method public default java.util.stream.IntStream.Builder add(int);
+    method public abstract java.util.stream.IntStream build();
+  }
+
+  public abstract interface LongStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.LongPredicate);
+    method public abstract boolean anyMatch(java.util.function.LongPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Long> boxed();
+    method public static java.util.stream.LongStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream);
+    method public abstract long count();
+    method public abstract java.util.stream.LongStream distinct();
+    method public static java.util.stream.LongStream empty();
+    method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate);
+    method public abstract java.util.OptionalLong findAny();
+    method public abstract java.util.OptionalLong findFirst();
+    method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.LongConsumer);
+    method public abstract void forEachOrdered(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream generate(java.util.function.LongSupplier);
+    method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfLong iterator();
+    method public abstract java.util.stream.LongStream limit(long);
+    method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>);
+    method public abstract java.util.OptionalLong max();
+    method public abstract java.util.OptionalLong min();
+    method public abstract boolean noneMatch(java.util.function.LongPredicate);
+    method public static java.util.stream.LongStream of(long);
+    method public static java.util.stream.LongStream of(long...);
+    method public abstract java.util.stream.LongStream parallel();
+    method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream range(long, long);
+    method public static java.util.stream.LongStream rangeClosed(long, long);
+    method public abstract long reduce(long, java.util.function.LongBinaryOperator);
+    method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator);
+    method public abstract java.util.stream.LongStream sequential();
+    method public abstract java.util.stream.LongStream skip(long);
+    method public abstract java.util.stream.LongStream sorted();
+    method public abstract java.util.Spliterator.OfLong spliterator();
+    method public abstract long sum();
+    method public abstract java.util.LongSummaryStatistics summaryStatistics();
+    method public abstract long[] toArray();
+  }
+
+  public static abstract interface LongStream.Builder implements java.util.function.LongConsumer {
+    method public abstract void accept(long);
+    method public default java.util.stream.LongStream.Builder add(long);
+    method public abstract java.util.stream.LongStream build();
+  }
+
+  public abstract interface Stream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.Predicate<? super T>);
+    method public abstract boolean anyMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream.Builder<T> builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>);
+    method public abstract R collect(java.util.stream.Collector<? super T, A, R>);
+    method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>);
+    method public abstract long count();
+    method public abstract java.util.stream.Stream<T> distinct();
+    method public static java.util.stream.Stream<T> empty();
+    method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>);
+    method public abstract java.util.Optional<T> findAny();
+    method public abstract java.util.Optional<T> findFirst();
+    method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>);
+    method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>);
+    method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>);
+    method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.Consumer<? super T>);
+    method public abstract void forEachOrdered(java.util.function.Consumer<? super T>);
+    method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>);
+    method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>);
+    method public abstract java.util.stream.Stream<T> limit(long);
+    method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>);
+    method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>);
+    method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>);
+    method public abstract boolean noneMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream<T> of(T);
+    method public static java.util.stream.Stream<T> of(T...);
+    method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>);
+    method public abstract T reduce(T, java.util.function.BinaryOperator<T>);
+    method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>);
+    method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>);
+    method public abstract java.util.stream.Stream<T> skip(long);
+    method public abstract java.util.stream.Stream<T> sorted();
+    method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>);
+    method public abstract java.lang.Object[] toArray();
+    method public abstract A[] toArray(java.util.function.IntFunction<A[]>);
+  }
+
+  public static abstract interface Stream.Builder implements java.util.function.Consumer {
+    method public abstract void accept(T);
+    method public default java.util.stream.Stream.Builder<T> add(T);
+    method public abstract java.util.stream.Stream<T> build();
+  }
+
+  public final class StreamSupport {
+    method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean);
+    method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean);
+  }
+
+}
+
 package java.util.zip {
 
   public class Adler32 implements java.util.zip.Checksum {
diff --git a/api/system-current.txt b/api/system-current.txt
index 24edfe0..97b7710 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1039,6 +1039,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2856,14 +2858,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -31386,6 +31384,7 @@
     field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
     field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
     field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+    field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
     field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
     field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
     field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2
@@ -48671,9 +48670,8 @@
     field public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; // 0x6
     field public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; // 0x5
     field public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; // 0x3
-    field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9; // 0x9
+    field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8
     field public static final int LIBLOAD_SUCCESS = 0; // 0x0
-    field public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8; // 0x8
     field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1
   }
 
@@ -50260,6 +50258,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -53753,6 +53753,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -53834,6 +53835,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
@@ -60411,6 +60413,7 @@
     method public E get(int);
     method public boolean removeIf(java.util.function.Predicate<? super E>);
     method public int size();
+    method public void sort(java.util.Comparator<? super E>);
     method public java.util.Spliterator<E> spliterator();
     method public void trimToSize();
   }
@@ -60542,6 +60545,14 @@
     method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
     method public static java.util.Spliterator.OfDouble spliterator(double[]);
     method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
+    method public static java.util.stream.Stream<T> stream(T[]);
+    method public static java.util.stream.Stream<T> stream(T[], int, int);
+    method public static java.util.stream.IntStream stream(int[]);
+    method public static java.util.stream.IntStream stream(int[], int, int);
+    method public static java.util.stream.LongStream stream(long[]);
+    method public static java.util.stream.LongStream stream(long[], int, int);
+    method public static java.util.stream.DoubleStream stream(double[]);
+    method public static java.util.stream.DoubleStream stream(double[], int, int);
     method public static java.lang.String toString(long[]);
     method public static java.lang.String toString(int[]);
     method public static java.lang.String toString(short[]);
@@ -60701,11 +60712,13 @@
     method public abstract int hashCode();
     method public abstract boolean isEmpty();
     method public abstract java.util.Iterator<E> iterator();
+    method public default java.util.stream.Stream<E> parallelStream();
     method public abstract boolean remove(java.lang.Object);
     method public abstract boolean removeAll(java.util.Collection<?>);
     method public default boolean removeIf(java.util.function.Predicate<? super E>);
     method public abstract boolean retainAll(java.util.Collection<?>);
     method public abstract int size();
+    method public default java.util.stream.Stream<E> stream();
     method public abstract java.lang.Object[] toArray();
     method public abstract T[] toArray(T[]);
   }
@@ -61905,6 +61918,34 @@
     method public java.util.Spliterator<T> trySplit();
   }
 
+  public final class SplittableRandom {
+    ctor public SplittableRandom(long);
+    ctor public SplittableRandom();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
+    method public boolean nextBoolean();
+    method public double nextDouble();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt();
+    method public int nextInt(int);
+    method public int nextInt(int, int);
+    method public long nextLong();
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+    method public java.util.SplittableRandom split();
+  }
+
   public class Stack extends java.util.Vector {
     ctor public Stack();
     method public boolean empty();
@@ -63088,6 +63129,18 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -64489,6 +64542,292 @@
 
 }
 
+package java.util.stream {
+
+  public abstract interface BaseStream implements java.lang.AutoCloseable {
+    method public abstract void close();
+    method public abstract boolean isParallel();
+    method public abstract java.util.Iterator<T> iterator();
+    method public abstract S onClose(java.lang.Runnable);
+    method public abstract S parallel();
+    method public abstract S sequential();
+    method public abstract java.util.Spliterator<T> spliterator();
+    method public abstract S unordered();
+  }
+
+  public abstract interface Collector {
+    method public abstract java.util.function.BiConsumer<A, T> accumulator();
+    method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics();
+    method public abstract java.util.function.BinaryOperator<A> combiner();
+    method public abstract java.util.function.Function<A, R> finisher();
+    method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...);
+    method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...);
+    method public abstract java.util.function.Supplier<A> supplier();
+  }
+
+  public static final class Collector.Characteristics extends java.lang.Enum {
+    method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String);
+    method public static final java.util.stream.Collector.Characteristics[] values();
+    enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT;
+    enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH;
+    enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED;
+  }
+
+  public final class Collectors {
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> counting();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining();
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+    method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet();
+  }
+
+  public abstract interface DoubleStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.DoublePredicate);
+    method public abstract boolean anyMatch(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Double> boxed();
+    method public static java.util.stream.DoubleStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream);
+    method public abstract long count();
+    method public abstract java.util.stream.DoubleStream distinct();
+    method public static java.util.stream.DoubleStream empty();
+    method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble findAny();
+    method public abstract java.util.OptionalDouble findFirst();
+    method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>);
+    method public abstract void forEach(java.util.function.DoubleConsumer);
+    method public abstract void forEachOrdered(java.util.function.DoubleConsumer);
+    method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier);
+    method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfDouble iterator();
+    method public abstract java.util.stream.DoubleStream limit(long);
+    method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>);
+    method public abstract java.util.OptionalDouble max();
+    method public abstract java.util.OptionalDouble min();
+    method public abstract boolean noneMatch(java.util.function.DoublePredicate);
+    method public static java.util.stream.DoubleStream of(double);
+    method public static java.util.stream.DoubleStream of(double...);
+    method public abstract java.util.stream.DoubleStream parallel();
+    method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer);
+    method public abstract double reduce(double, java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.stream.DoubleStream sequential();
+    method public abstract java.util.stream.DoubleStream skip(long);
+    method public abstract java.util.stream.DoubleStream sorted();
+    method public abstract java.util.Spliterator.OfDouble spliterator();
+    method public abstract double sum();
+    method public abstract java.util.DoubleSummaryStatistics summaryStatistics();
+    method public abstract double[] toArray();
+  }
+
+  public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer {
+    method public abstract void accept(double);
+    method public default java.util.stream.DoubleStream.Builder add(double);
+    method public abstract java.util.stream.DoubleStream build();
+  }
+
+  public abstract interface IntStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.IntPredicate);
+    method public abstract boolean anyMatch(java.util.function.IntPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.stream.LongStream asLongStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Integer> boxed();
+    method public static java.util.stream.IntStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream);
+    method public abstract long count();
+    method public abstract java.util.stream.IntStream distinct();
+    method public static java.util.stream.IntStream empty();
+    method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate);
+    method public abstract java.util.OptionalInt findAny();
+    method public abstract java.util.OptionalInt findFirst();
+    method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>);
+    method public abstract void forEach(java.util.function.IntConsumer);
+    method public abstract void forEachOrdered(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream generate(java.util.function.IntSupplier);
+    method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfInt iterator();
+    method public abstract java.util.stream.IntStream limit(long);
+    method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>);
+    method public abstract java.util.OptionalInt max();
+    method public abstract java.util.OptionalInt min();
+    method public abstract boolean noneMatch(java.util.function.IntPredicate);
+    method public static java.util.stream.IntStream of(int);
+    method public static java.util.stream.IntStream of(int...);
+    method public abstract java.util.stream.IntStream parallel();
+    method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream range(int, int);
+    method public static java.util.stream.IntStream rangeClosed(int, int);
+    method public abstract int reduce(int, java.util.function.IntBinaryOperator);
+    method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator);
+    method public abstract java.util.stream.IntStream sequential();
+    method public abstract java.util.stream.IntStream skip(long);
+    method public abstract java.util.stream.IntStream sorted();
+    method public abstract java.util.Spliterator.OfInt spliterator();
+    method public abstract int sum();
+    method public abstract java.util.IntSummaryStatistics summaryStatistics();
+    method public abstract int[] toArray();
+  }
+
+  public static abstract interface IntStream.Builder implements java.util.function.IntConsumer {
+    method public abstract void accept(int);
+    method public default java.util.stream.IntStream.Builder add(int);
+    method public abstract java.util.stream.IntStream build();
+  }
+
+  public abstract interface LongStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.LongPredicate);
+    method public abstract boolean anyMatch(java.util.function.LongPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Long> boxed();
+    method public static java.util.stream.LongStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream);
+    method public abstract long count();
+    method public abstract java.util.stream.LongStream distinct();
+    method public static java.util.stream.LongStream empty();
+    method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate);
+    method public abstract java.util.OptionalLong findAny();
+    method public abstract java.util.OptionalLong findFirst();
+    method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.LongConsumer);
+    method public abstract void forEachOrdered(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream generate(java.util.function.LongSupplier);
+    method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfLong iterator();
+    method public abstract java.util.stream.LongStream limit(long);
+    method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>);
+    method public abstract java.util.OptionalLong max();
+    method public abstract java.util.OptionalLong min();
+    method public abstract boolean noneMatch(java.util.function.LongPredicate);
+    method public static java.util.stream.LongStream of(long);
+    method public static java.util.stream.LongStream of(long...);
+    method public abstract java.util.stream.LongStream parallel();
+    method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream range(long, long);
+    method public static java.util.stream.LongStream rangeClosed(long, long);
+    method public abstract long reduce(long, java.util.function.LongBinaryOperator);
+    method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator);
+    method public abstract java.util.stream.LongStream sequential();
+    method public abstract java.util.stream.LongStream skip(long);
+    method public abstract java.util.stream.LongStream sorted();
+    method public abstract java.util.Spliterator.OfLong spliterator();
+    method public abstract long sum();
+    method public abstract java.util.LongSummaryStatistics summaryStatistics();
+    method public abstract long[] toArray();
+  }
+
+  public static abstract interface LongStream.Builder implements java.util.function.LongConsumer {
+    method public abstract void accept(long);
+    method public default java.util.stream.LongStream.Builder add(long);
+    method public abstract java.util.stream.LongStream build();
+  }
+
+  public abstract interface Stream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.Predicate<? super T>);
+    method public abstract boolean anyMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream.Builder<T> builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>);
+    method public abstract R collect(java.util.stream.Collector<? super T, A, R>);
+    method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>);
+    method public abstract long count();
+    method public abstract java.util.stream.Stream<T> distinct();
+    method public static java.util.stream.Stream<T> empty();
+    method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>);
+    method public abstract java.util.Optional<T> findAny();
+    method public abstract java.util.Optional<T> findFirst();
+    method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>);
+    method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>);
+    method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>);
+    method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.Consumer<? super T>);
+    method public abstract void forEachOrdered(java.util.function.Consumer<? super T>);
+    method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>);
+    method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>);
+    method public abstract java.util.stream.Stream<T> limit(long);
+    method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>);
+    method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>);
+    method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>);
+    method public abstract boolean noneMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream<T> of(T);
+    method public static java.util.stream.Stream<T> of(T...);
+    method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>);
+    method public abstract T reduce(T, java.util.function.BinaryOperator<T>);
+    method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>);
+    method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>);
+    method public abstract java.util.stream.Stream<T> skip(long);
+    method public abstract java.util.stream.Stream<T> sorted();
+    method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>);
+    method public abstract java.lang.Object[] toArray();
+    method public abstract A[] toArray(java.util.function.IntFunction<A[]>);
+  }
+
+  public static abstract interface Stream.Builder implements java.util.function.Consumer {
+    method public abstract void accept(T);
+    method public default java.util.stream.Stream.Builder<T> add(T);
+    method public abstract java.util.stream.Stream<T> build();
+  }
+
+  public final class StreamSupport {
+    method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean);
+    method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean);
+  }
+
+}
+
 package java.util.zip {
 
   public class Adler32 implements java.util.zip.Checksum {
diff --git a/api/test-current.txt b/api/test-current.txt
index 97e3b95..fa2669c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -944,6 +944,8 @@
     field public static final int popupBackground = 16843126; // 0x1010176
     field public static final int popupCharacters = 16843332; // 0x1010244
     field public static final int popupElevation = 16843916; // 0x101048c
+    field public static final int popupEnterTransition = 16844065; // 0x1010521
+    field public static final int popupExitTransition = 16844066; // 0x1010522
     field public static final int popupKeyboard = 16843331; // 0x1010243
     field public static final int popupLayout = 16843323; // 0x101023b
     field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -2754,14 +2756,10 @@
   }
 
   public final class GestureDescription {
-    method public static android.accessibilityservice.GestureDescription createClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
-    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
-    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public static long getMaxGestureDuration();
+    method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
     method public int getStrokeCount();
-    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
-    field public static final int MAX_STROKE_COUNT = 10; // 0xa
   }
 
   public static class GestureDescription.Builder {
@@ -47269,6 +47267,8 @@
     method public android.graphics.drawable.Drawable getBackground();
     method public android.view.View getContentView();
     method public float getElevation();
+    method public android.transition.Transition getEnterTransition();
+    method public android.transition.Transition getExitTransition();
     method public int getHeight();
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
@@ -50762,6 +50762,7 @@
     method public float floatValue();
     method public static int hashCode(double);
     method public int intValue();
+    method public static boolean isFinite(double);
     method public static boolean isInfinite(double);
     method public boolean isInfinite();
     method public static boolean isNaN(double);
@@ -50843,6 +50844,7 @@
     method public float floatValue();
     method public static float intBitsToFloat(int);
     method public int intValue();
+    method public static boolean isFinite(float);
     method public static boolean isInfinite(float);
     method public boolean isInfinite();
     method public static boolean isNaN(float);
@@ -57420,6 +57422,7 @@
     method public E get(int);
     method public boolean removeIf(java.util.function.Predicate<? super E>);
     method public int size();
+    method public void sort(java.util.Comparator<? super E>);
     method public java.util.Spliterator<E> spliterator();
     method public void trimToSize();
   }
@@ -57551,6 +57554,14 @@
     method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
     method public static java.util.Spliterator.OfDouble spliterator(double[]);
     method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
+    method public static java.util.stream.Stream<T> stream(T[]);
+    method public static java.util.stream.Stream<T> stream(T[], int, int);
+    method public static java.util.stream.IntStream stream(int[]);
+    method public static java.util.stream.IntStream stream(int[], int, int);
+    method public static java.util.stream.LongStream stream(long[]);
+    method public static java.util.stream.LongStream stream(long[], int, int);
+    method public static java.util.stream.DoubleStream stream(double[]);
+    method public static java.util.stream.DoubleStream stream(double[], int, int);
     method public static java.lang.String toString(long[]);
     method public static java.lang.String toString(int[]);
     method public static java.lang.String toString(short[]);
@@ -57710,11 +57721,13 @@
     method public abstract int hashCode();
     method public abstract boolean isEmpty();
     method public abstract java.util.Iterator<E> iterator();
+    method public default java.util.stream.Stream<E> parallelStream();
     method public abstract boolean remove(java.lang.Object);
     method public abstract boolean removeAll(java.util.Collection<?>);
     method public default boolean removeIf(java.util.function.Predicate<? super E>);
     method public abstract boolean retainAll(java.util.Collection<?>);
     method public abstract int size();
+    method public default java.util.stream.Stream<E> stream();
     method public abstract java.lang.Object[] toArray();
     method public abstract T[] toArray(T[]);
   }
@@ -58914,6 +58927,34 @@
     method public java.util.Spliterator<T> trySplit();
   }
 
+  public final class SplittableRandom {
+    ctor public SplittableRandom(long);
+    ctor public SplittableRandom();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
+    method public boolean nextBoolean();
+    method public double nextDouble();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt();
+    method public int nextInt(int);
+    method public int nextInt(int, int);
+    method public long nextLong();
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+    method public java.util.SplittableRandom split();
+  }
+
   public class Stack extends java.util.Vector {
     ctor public Stack();
     method public boolean empty();
@@ -60097,6 +60138,18 @@
 
   public class ThreadLocalRandom extends java.util.Random {
     method public static java.util.concurrent.ThreadLocalRandom current();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
     method public double nextDouble(double);
     method public double nextDouble(double, double);
     method public int nextInt(int, int);
@@ -61498,6 +61551,292 @@
 
 }
 
+package java.util.stream {
+
+  public abstract interface BaseStream implements java.lang.AutoCloseable {
+    method public abstract void close();
+    method public abstract boolean isParallel();
+    method public abstract java.util.Iterator<T> iterator();
+    method public abstract S onClose(java.lang.Runnable);
+    method public abstract S parallel();
+    method public abstract S sequential();
+    method public abstract java.util.Spliterator<T> spliterator();
+    method public abstract S unordered();
+  }
+
+  public abstract interface Collector {
+    method public abstract java.util.function.BiConsumer<A, T> accumulator();
+    method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics();
+    method public abstract java.util.function.BinaryOperator<A> combiner();
+    method public abstract java.util.function.Function<A, R> finisher();
+    method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...);
+    method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...);
+    method public abstract java.util.function.Supplier<A> supplier();
+  }
+
+  public static final class Collector.Characteristics extends java.lang.Enum {
+    method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String);
+    method public static final java.util.stream.Collector.Characteristics[] values();
+    enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT;
+    enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH;
+    enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED;
+  }
+
+  public final class Collectors {
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> counting();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining();
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence);
+    method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+    method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>);
+    method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>);
+    method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>);
+    method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList();
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>);
+    method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet();
+  }
+
+  public abstract interface DoubleStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.DoublePredicate);
+    method public abstract boolean anyMatch(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Double> boxed();
+    method public static java.util.stream.DoubleStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream);
+    method public abstract long count();
+    method public abstract java.util.stream.DoubleStream distinct();
+    method public static java.util.stream.DoubleStream empty();
+    method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate);
+    method public abstract java.util.OptionalDouble findAny();
+    method public abstract java.util.OptionalDouble findFirst();
+    method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>);
+    method public abstract void forEach(java.util.function.DoubleConsumer);
+    method public abstract void forEachOrdered(java.util.function.DoubleConsumer);
+    method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier);
+    method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfDouble iterator();
+    method public abstract java.util.stream.DoubleStream limit(long);
+    method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>);
+    method public abstract java.util.OptionalDouble max();
+    method public abstract java.util.OptionalDouble min();
+    method public abstract boolean noneMatch(java.util.function.DoublePredicate);
+    method public static java.util.stream.DoubleStream of(double);
+    method public static java.util.stream.DoubleStream of(double...);
+    method public abstract java.util.stream.DoubleStream parallel();
+    method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer);
+    method public abstract double reduce(double, java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator);
+    method public abstract java.util.stream.DoubleStream sequential();
+    method public abstract java.util.stream.DoubleStream skip(long);
+    method public abstract java.util.stream.DoubleStream sorted();
+    method public abstract java.util.Spliterator.OfDouble spliterator();
+    method public abstract double sum();
+    method public abstract java.util.DoubleSummaryStatistics summaryStatistics();
+    method public abstract double[] toArray();
+  }
+
+  public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer {
+    method public abstract void accept(double);
+    method public default java.util.stream.DoubleStream.Builder add(double);
+    method public abstract java.util.stream.DoubleStream build();
+  }
+
+  public abstract interface IntStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.IntPredicate);
+    method public abstract boolean anyMatch(java.util.function.IntPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.stream.LongStream asLongStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Integer> boxed();
+    method public static java.util.stream.IntStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream);
+    method public abstract long count();
+    method public abstract java.util.stream.IntStream distinct();
+    method public static java.util.stream.IntStream empty();
+    method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate);
+    method public abstract java.util.OptionalInt findAny();
+    method public abstract java.util.OptionalInt findFirst();
+    method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>);
+    method public abstract void forEach(java.util.function.IntConsumer);
+    method public abstract void forEachOrdered(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream generate(java.util.function.IntSupplier);
+    method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfInt iterator();
+    method public abstract java.util.stream.IntStream limit(long);
+    method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>);
+    method public abstract java.util.OptionalInt max();
+    method public abstract java.util.OptionalInt min();
+    method public abstract boolean noneMatch(java.util.function.IntPredicate);
+    method public static java.util.stream.IntStream of(int);
+    method public static java.util.stream.IntStream of(int...);
+    method public abstract java.util.stream.IntStream parallel();
+    method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream range(int, int);
+    method public static java.util.stream.IntStream rangeClosed(int, int);
+    method public abstract int reduce(int, java.util.function.IntBinaryOperator);
+    method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator);
+    method public abstract java.util.stream.IntStream sequential();
+    method public abstract java.util.stream.IntStream skip(long);
+    method public abstract java.util.stream.IntStream sorted();
+    method public abstract java.util.Spliterator.OfInt spliterator();
+    method public abstract int sum();
+    method public abstract java.util.IntSummaryStatistics summaryStatistics();
+    method public abstract int[] toArray();
+  }
+
+  public static abstract interface IntStream.Builder implements java.util.function.IntConsumer {
+    method public abstract void accept(int);
+    method public default java.util.stream.IntStream.Builder add(int);
+    method public abstract java.util.stream.IntStream build();
+  }
+
+  public abstract interface LongStream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.LongPredicate);
+    method public abstract boolean anyMatch(java.util.function.LongPredicate);
+    method public abstract java.util.stream.DoubleStream asDoubleStream();
+    method public abstract java.util.OptionalDouble average();
+    method public abstract java.util.stream.Stream<java.lang.Long> boxed();
+    method public static java.util.stream.LongStream.Builder builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>);
+    method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream);
+    method public abstract long count();
+    method public abstract java.util.stream.LongStream distinct();
+    method public static java.util.stream.LongStream empty();
+    method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate);
+    method public abstract java.util.OptionalLong findAny();
+    method public abstract java.util.OptionalLong findFirst();
+    method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.LongConsumer);
+    method public abstract void forEachOrdered(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream generate(java.util.function.LongSupplier);
+    method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator);
+    method public abstract java.util.PrimitiveIterator.OfLong iterator();
+    method public abstract java.util.stream.LongStream limit(long);
+    method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction);
+    method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>);
+    method public abstract java.util.OptionalLong max();
+    method public abstract java.util.OptionalLong min();
+    method public abstract boolean noneMatch(java.util.function.LongPredicate);
+    method public static java.util.stream.LongStream of(long);
+    method public static java.util.stream.LongStream of(long...);
+    method public abstract java.util.stream.LongStream parallel();
+    method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream range(long, long);
+    method public static java.util.stream.LongStream rangeClosed(long, long);
+    method public abstract long reduce(long, java.util.function.LongBinaryOperator);
+    method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator);
+    method public abstract java.util.stream.LongStream sequential();
+    method public abstract java.util.stream.LongStream skip(long);
+    method public abstract java.util.stream.LongStream sorted();
+    method public abstract java.util.Spliterator.OfLong spliterator();
+    method public abstract long sum();
+    method public abstract java.util.LongSummaryStatistics summaryStatistics();
+    method public abstract long[] toArray();
+  }
+
+  public static abstract interface LongStream.Builder implements java.util.function.LongConsumer {
+    method public abstract void accept(long);
+    method public default java.util.stream.LongStream.Builder add(long);
+    method public abstract java.util.stream.LongStream build();
+  }
+
+  public abstract interface Stream implements java.util.stream.BaseStream {
+    method public abstract boolean allMatch(java.util.function.Predicate<? super T>);
+    method public abstract boolean anyMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream.Builder<T> builder();
+    method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>);
+    method public abstract R collect(java.util.stream.Collector<? super T, A, R>);
+    method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>);
+    method public abstract long count();
+    method public abstract java.util.stream.Stream<T> distinct();
+    method public static java.util.stream.Stream<T> empty();
+    method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>);
+    method public abstract java.util.Optional<T> findAny();
+    method public abstract java.util.Optional<T> findFirst();
+    method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>);
+    method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>);
+    method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>);
+    method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>);
+    method public abstract void forEach(java.util.function.Consumer<? super T>);
+    method public abstract void forEachOrdered(java.util.function.Consumer<? super T>);
+    method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>);
+    method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>);
+    method public abstract java.util.stream.Stream<T> limit(long);
+    method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>);
+    method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>);
+    method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>);
+    method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>);
+    method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>);
+    method public abstract boolean noneMatch(java.util.function.Predicate<? super T>);
+    method public static java.util.stream.Stream<T> of(T);
+    method public static java.util.stream.Stream<T> of(T...);
+    method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>);
+    method public abstract T reduce(T, java.util.function.BinaryOperator<T>);
+    method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>);
+    method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>);
+    method public abstract java.util.stream.Stream<T> skip(long);
+    method public abstract java.util.stream.Stream<T> sorted();
+    method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>);
+    method public abstract java.lang.Object[] toArray();
+    method public abstract A[] toArray(java.util.function.IntFunction<A[]>);
+  }
+
+  public static abstract interface Stream.Builder implements java.util.function.Consumer {
+    method public abstract void accept(T);
+    method public default java.util.stream.Stream.Builder<T> add(T);
+    method public abstract java.util.stream.Stream<T> build();
+  }
+
+  public final class StreamSupport {
+    method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean);
+    method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean);
+    method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean);
+  }
+
+}
+
 package java.util.zip {
 
   public class Adler32 implements java.util.zip.Checksum {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ac3b8e3..ddc5b0c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -851,6 +851,7 @@
                     return connection.getMagnificationScale();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain scale", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 1.0f;
@@ -879,6 +880,7 @@
                     return connection.getMagnificationCenterX();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center X", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 0.0f;
@@ -907,6 +909,7 @@
                     return connection.getMagnificationCenterY();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain center Y", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return 0.0f;
@@ -933,6 +936,7 @@
                     return connection.getMagnifiedRegion();
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to obtain magnified region", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return Region.obtain();
@@ -961,6 +965,7 @@
                     return connection.resetMagnification(animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to reset", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -989,6 +994,7 @@
                             scale, Float.NaN, Float.NaN, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set scale", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -1020,6 +1026,7 @@
                             Float.NaN, centerX, centerY, animate);
                 } catch (RemoteException re) {
                     Log.w(LOG_TAG, "Failed to set center", re);
+                    re.rethrowFromSystemServer();
                 }
             }
             return false;
@@ -1254,10 +1261,7 @@
                    Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
                    re.rethrowFromSystemServer();
                }
-           } else {
-               throw new RuntimeException("AccessibilityServiceConnection is null");
            }
-
            return false;
         }
 
@@ -1301,6 +1305,7 @@
                 return connection.performGlobalAction(action);
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
+                re.rethrowFromSystemServer();
             }
         }
         return false;
@@ -1349,6 +1354,7 @@
                 return connection.getServiceInfo();
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re);
+                re.rethrowFromSystemServer();
             }
         }
         return null;
@@ -1382,6 +1388,7 @@
                 AccessibilityInteractionClient.getInstance().clearCache();
             } catch (RemoteException re) {
                 Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
+                re.rethrowFromSystemServer();
             }
         }
     }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index 14aabcf..7a0c89b 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -43,135 +43,32 @@
  */
 public final class GestureDescription {
     /** Gestures may contain no more than this many strokes */
-    public static final int MAX_STROKE_COUNT = 10;
+    private static final int MAX_STROKE_COUNT = 10;
 
     /**
      * Upper bound on total gesture duration. Nearly all gestures will be much shorter.
      */
-    public static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
+    private static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
 
     private final List<StrokeDescription> mStrokes = new ArrayList<>();
     private final float[] mTempPos = new float[2];
 
     /**
-     * Create a description of a click gesture
+     * Get the upper limit for the number of strokes a gesture may contain.
      *
-     * @param x The x coordinate to click. Must not be negative.
-     * @param y The y coordinate to click. Must not be negative.
-     *
-     * @return A description of a click at (x, y)
+     * @return The maximum number of strokes.
      */
-    public static GestureDescription createClick(@IntRange(from = 0) int x,
-            @IntRange(from = 0) int y) {
-        Path clickPath = new Path();
-        clickPath.moveTo(x, y);
-        clickPath.lineTo(x + 1, y);
-        return new GestureDescription(
-                new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout()));
+    public static int getMaxStrokeCount() {
+        return MAX_STROKE_COUNT;
     }
 
     /**
-     * Create a description of a long click gesture
+     * Get the upper limit on a gesture's duration.
      *
-     * @param x The x coordinate to click. Must not be negative.
-     * @param y The y coordinate to click. Must not be negative.
-     *
-     * @return A description of a click at (x, y)
+     * @return The maximum duration in milliseconds.
      */
-    public static GestureDescription createLongClick(@IntRange(from = 0) int x,
-            @IntRange(from = 0) int y) {
-        Path clickPath = new Path();
-        clickPath.moveTo(x, y);
-        clickPath.lineTo(x + 1, y);
-        int longPressTime = ViewConfiguration.getLongPressTimeout();
-        return new GestureDescription(
-                new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2)));
-    }
-
-    /**
-     * Create a description of a swipe gesture
-     *
-     * @param startX The x coordinate of the starting point. Must not be negative.
-     * @param startY The y coordinate of the starting point. Must not be negative.
-     * @param endX The x coordinate of the ending point. Must not be negative.
-     * @param endY The y coordinate of the ending point. Must not be negative.
-     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
-     *
-     * @return A description of a swipe from ({@code startX}, {@code startY}) to
-     * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null}
-     * if the path specified for the swipe is invalid.
-     */
-    public static GestureDescription createSwipe(@IntRange(from = 0) int startX,
-            @IntRange(from = 0) int startY,
-            @IntRange(from = 0) int endX,
-            @IntRange(from = 0) int endY,
-            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
-        Path swipePath = new Path();
-        swipePath.moveTo(startX, startY);
-        swipePath.lineTo(endX, endY);
-        return new GestureDescription(new StrokeDescription(swipePath, 0, duration));
-    }
-
-    /**
-     * Create a description for a pinch (or zoom) gesture.
-     *
-     * @param centerX The x coordinate of the center of the pinch. Must not be negative.
-     * @param centerY The y coordinate of the center of the pinch. Must not be negative.
-     * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not
-     * be negative.
-     * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be
-     * negative.
-     * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch
-     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
-     *
-     * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts
-     * with the touch points spaced by {@code startSpacing} and ends with them spaced by
-     * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path
-     * specified for the pinch is invalid.
-     */
-    public static GestureDescription createPinch(@IntRange(from = 0) int centerX,
-            @IntRange(from = 0) int centerY,
-            @IntRange(from = 0) int startSpacing,
-            @IntRange(from = 0) int endSpacing,
-            float orientation,
-            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
-        if ((startSpacing < 0) || (endSpacing < 0)) {
-            throw new IllegalArgumentException("Pinch spacing cannot be negative");
-        }
-        float[] startPoint1 = new float[2];
-        float[] endPoint1 = new float[2];
-        float[] startPoint2 = new float[2];
-        float[] endPoint2 = new float[2];
-
-        /* Build points for a horizontal gesture centered at the origin */
-        startPoint1[0] = startSpacing / 2;
-        startPoint1[1] = 0;
-        endPoint1[0] = endSpacing / 2;
-        endPoint1[1] = 0;
-        startPoint2[0] = -startSpacing / 2;
-        startPoint2[1] = 0;
-        endPoint2[0] = -endSpacing / 2;
-        endPoint2[1] = 0;
-
-        /* Rotate and translate the points */
-        Matrix matrix = new Matrix();
-        matrix.setRotate(orientation);
-        matrix.postTranslate(centerX, centerY);
-        matrix.mapPoints(startPoint1);
-        matrix.mapPoints(endPoint1);
-        matrix.mapPoints(startPoint2);
-        matrix.mapPoints(endPoint2);
-
-        Path path1 = new Path();
-        path1.moveTo(startPoint1[0], startPoint1[1]);
-        path1.lineTo(endPoint1[0], endPoint1[1]);
-        Path path2 = new Path();
-        path2.moveTo(startPoint2[0], startPoint2[1]);
-        path2.lineTo(endPoint2[0], endPoint2[1]);
-
-        return new GestureDescription(Arrays.asList(
-                new StrokeDescription(path1, 0, duration),
-                new StrokeDescription(path2, 0, duration)));
+    public static long getMaxGestureDuration() {
+        return MAX_GESTURE_DURATION_MS;
     }
 
     private GestureDescription() {}
@@ -180,10 +77,6 @@
         mStrokes.addAll(strokes);
     }
 
-    private GestureDescription(StrokeDescription stroke) {
-        mStrokes.add(stroke);
-    }
-
     /**
      * Get the number of stroke in the gesture.
      *
@@ -278,21 +171,23 @@
          */
         public Builder addStroke(@NonNull StrokeDescription strokeDescription) {
             if (mStrokes.size() >= MAX_STROKE_COUNT) {
-                throw new RuntimeException("Attempting to add too many strokes to a gesture");
+                throw new IllegalStateException(
+                        "Attempting to add too many strokes to a gesture");
             }
 
             mStrokes.add(strokeDescription);
 
             if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) {
                 mStrokes.remove(strokeDescription);
-                throw new RuntimeException("Gesture would exceed maximum duration with new stroke");
+                throw new IllegalStateException(
+                        "Gesture would exceed maximum duration with new stroke");
             }
             return this;
         }
 
         public GestureDescription build() {
             if (mStrokes.size() == 0) {
-                throw new RuntimeException("Gestures must have at least one stroke");
+                throw new IllegalStateException("Gestures must have at least one stroke");
             }
             return new GestureDescription(mStrokes);
         }
@@ -317,8 +212,8 @@
          * Must not be negative.
          */
         public StrokeDescription(@NonNull Path path,
-                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime,
-                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+                @IntRange(from = 0) long startTime,
+                @IntRange(from = 0) long duration) {
             if (duration <= 0) {
                 throw new IllegalArgumentException("Duration must be positive");
             }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 33fd1db..13e8e75 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -49,6 +50,8 @@
 import com.android.internal.content.ReferrerIntent;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -78,7 +81,15 @@
     public static final String REPORT_KEY_STREAMRESULT = "stream";
 
     private static final String TAG = "Instrumentation";
-    
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES})
+    public @interface UiAutomationFlags {};
+
+
     private final Object mSync = new Object();
     private ActivityThread mThread = null;
     private MessageQueue mMessageQueue = null;
@@ -1876,7 +1887,7 @@
      *
      * @see UiAutomation
      */
-    public UiAutomation getUiAutomation(int flags) {
+    public UiAutomation getUiAutomation(@UiAutomationFlags int flags) {
         if (mUiAutomationConnection != null) {
             if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) {
                 mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(),
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8724d5e..d865f345 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -160,7 +160,8 @@
          * as defined in {@link #hasShortcutHostPermission()}, will receive it.
          *
          * @param packageName The name of the package that has the shortcuts.
-         * @param shortcuts all shortcuts from the package (dynamic and/or pinned).
+         * @param shortcuts all shortcuts from the package (dynamic and/or pinned).  Only "key"
+         *    information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
          * @param user The UserHandle of the profile that generated the change.
          */
         public void onShortcutsChanged(@NonNull String packageName,
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index b5c1f30..ae75e3f 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -46,8 +46,6 @@
  *   if necessary, and persisted.
  *
  *   We will disallow byte[] icons, because they can easily go over binder size limit.
- *
- * TODO Move save/load to this class
  */
 public class ShortcutInfo implements Parcelable {
     /* @hide */
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 04e64af..ffd9d89 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1099,6 +1099,11 @@
                 if (oldHandler == null) {
                     updateCallbackLocked(callback, handler);
                 }
+
+                // If not connected to camera service, schedule a reconnect to camera service.
+                if (mCameraService == null) {
+                    scheduleCameraServiceReconnectionLocked();
+                }
             }
         }
 
@@ -1123,6 +1128,11 @@
                 if (oldHandler == null) {
                     updateTorchCallbackLocked(callback, handler);
                 }
+
+                // If not connected to camera service, schedule a reconnect to camera service.
+                if (mCameraService == null) {
+                    scheduleCameraServiceReconnectionLocked();
+                }
             }
         }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e1c7ad77..6df2a9d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -477,10 +477,10 @@
          * also be bumped.
          */
         static final String[] USER_ACTIVITY_TYPES = {
-            "other", "button", "touch"
+            "other", "button", "touch", "accessibility"
         };
         
-        public static final int NUM_USER_ACTIVITY_TYPES = 3;
+        public static final int NUM_USER_ACTIVITY_TYPES = 4;
 
         public abstract void noteUserActivityLocked(int type);
         public abstract boolean hasUserActivity();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2a4507c..cb4caea 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -318,6 +318,13 @@
     public static final int USER_ACTIVITY_EVENT_TOUCH = 2;
 
     /**
+     * User activity event type: Accessibility taking action on behalf of user.
+     * @hide
+     */
+    @SystemApi
+    public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;
+
+    /**
      * User activity flag: If already dimmed, extend the dim timeout
      * but do not brighten.  This flag is useful for keeping the screen on
      * a little longer without causing a visible change such as when
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index def303a..b06d503 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -44,7 +44,8 @@
  * Only the system, the default SMS application, and the default phone app
  * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps
  * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber
- * provider.
+ * provider. However, {@link #canCurrentUserBlockNumbers(Context)} can be accessed by any
+ * application.
  * </p>
  *
  * <h3> Data </h3>
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index aa11e3f..816d9c4 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4994,6 +4994,8 @@
         if (child.hasFocus()) {
             requestChildFocus(child, child.findFocus());
         }
+        dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
+                && isShown());
     }
 
     /**
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index 5697dfc..9434f0c 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -54,6 +54,11 @@
     WebViewProviderInfo[] getValidWebViewPackages();
 
     /**
+     * Fetch all packages that could potentially implement WebView.
+     */
+    WebViewProviderInfo[] getAllWebViewPackages();
+
+    /**
      * Used by DevelopmentSetting to get the name of the WebView provider currently in use.
      */
     String getCurrentWebViewPackageName();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 0751ab0..f1bf890 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -103,8 +103,7 @@
     public static final int LIBLOAD_FAILED_JNI_CALL = 7;
 
     // more error codes for waiting for WebView preparation
-    public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8;
-    public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9;
+    public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8;
 
     // error for namespace lookup
     public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
@@ -115,8 +114,6 @@
                 return "Time out waiting for Relro files being created";
             case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
                 return "No WebView installed";
-            case LIBLOAD_WEBVIEW_BEING_REPLACED:
-                return "Time out waiting for WebView to be replaced";
             case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN:
                 return "Crashed for unknown reason";
         }
@@ -131,98 +128,9 @@
         public MissingWebViewPackageException(Exception e) { super(e); }
     }
 
-    private static String TAG_START = "webviewproviders";
-    private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
-    private static String TAG_PACKAGE_NAME = "packageName";
-    private static String TAG_DESCRIPTION = "description";
-    // Whether or not the provider must be explicitly chosen by the user to be used.
-    private static String TAG_AVAILABILITY = "availableByDefault";
-    private static String TAG_SIGNATURE = "signature";
-    private static String TAG_FALLBACK = "isFallback";
-
-    /**
-     * Reads all signatures at the current depth (within the current provider) from the XML parser.
-     */
-    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
-            XmlPullParserException {
-        List<String> signatures = new ArrayList<String>();
-        int outerDepth = parser.getDepth();
-        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
-            if (parser.getName().equals(TAG_SIGNATURE)) {
-                // Parse the value within the signature tag
-                String signature = parser.nextText();
-                signatures.add(signature);
-            } else {
-                Log.e(LOGTAG, "Found an element in a webview provider that is not a signature");
-            }
-        }
-        return signatures.toArray(new String[signatures.size()]);
-    }
-
-    /**
-     * Returns all packages declared in the framework resources as potential WebView providers.
-     * @hide
-     * */
-    public static WebViewProviderInfo[] getWebViewPackages() {
-        int numFallbackPackages = 0;
-        XmlResourceParser parser = null;
-        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
-        try {
-            parser = AppGlobals.getInitialApplication().getResources().getXml(
-                    com.android.internal.R.xml.config_webview_packages);
-            XmlUtils.beginDocument(parser, TAG_START);
-            while(true) {
-                XmlUtils.nextElement(parser);
-                String element = parser.getName();
-                if (element == null) {
-                    break;
-                }
-                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
-                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
-                    if (packageName == null) {
-                        throw new MissingWebViewPackageException(
-                                "WebView provider in framework resources missing package name");
-                    }
-                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
-                    if (description == null) {
-                        throw new MissingWebViewPackageException(
-                                "WebView provider in framework resources missing description");
-                    }
-                    boolean availableByDefault = "true".equals(
-                            parser.getAttributeValue(null, TAG_AVAILABILITY));
-                    boolean isFallback = "true".equals(
-                            parser.getAttributeValue(null, TAG_FALLBACK));
-                    WebViewProviderInfo currentProvider =
-                            new WebViewProviderInfo(packageName, description, availableByDefault,
-                                isFallback, readSignatures(parser));
-                    if (currentProvider.isFallbackPackage()) {
-                        numFallbackPackages++;
-                        if (numFallbackPackages > 1) {
-                            throw new AndroidRuntimeException(
-                                    "There can be at most one webview fallback package.");
-                        }
-                    }
-                    webViewProviders.add(currentProvider);
-                }
-                else {
-                    Log.e(LOGTAG, "Found an element that is not a webview provider");
-                }
-            }
-        } catch(XmlPullParserException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
-        } catch(IOException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
-    }
-
-
     // TODO (gsennton) remove when committing webview xts test change
     public static String getWebViewPackageName() {
-        WebViewProviderInfo[] providers = getWebViewPackages();
-        return providers[0].packageName;
+        return null;
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 64c2caa..75ccf35 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -150,6 +150,8 @@
     private WebViewProviderInfo(Parcel in) {
         packageName = in.readString();
         description = in.readString();
+        availableByDefault = (in.readInt() > 0);
+        isFallback = (in.readInt() > 0);
         signatures = in.createStringArray();
         packageInfo = null;
     }
@@ -163,6 +165,8 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(packageName);
         out.writeString(description);
+        out.writeInt(availableByDefault ? 1 : 0);
+        out.writeInt(isFallback ? 1 : 0);
         out.writeStringArray(signatures);
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4bcb406..942cbcb 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1827,7 +1827,7 @@
             return;
         }
 
-        Layout layout = getActiveLayout();
+        Layout layout = mTextView.getLayout();
         final int offset = mTextView.getSelectionStart();
         final int line = layout.getLineForOffset(offset);
         final int top = layout.getLineTop(line);
@@ -2192,27 +2192,35 @@
             mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable(
                     mTextView.mCursorDrawableRes);
         final Drawable drawable = mCursorDrawable[cursorIndex];
-        final int left = clampCursorHorizontalPosition(drawable, horizontal);
+        final int left = clampHorizontalPosition(drawable, horizontal);
         final int width = drawable.getIntrinsicWidth();
         drawable.setBounds(left, top - mTempRect.top, left + width,
                 bottom + mTempRect.bottom);
     }
 
     /**
-     * Return clamped position for the cursor. If the cursor is within the boundaries of the view,
-     * then it is offset with the left padding of the cursor drawable. If the cursor is at
+     * Return clamped position for the drawable. If the drawable is within the boundaries of the
+     * view, then it is offset with the left padding of the cursor drawable. If the drawable is at
      * the beginning or the end of the text then its drawable edge is aligned with left or right of
-     * the view boundary.
+     * the view boundary. If the drawable is null, horizontal parameter is aligned to left or right
+     * of the view.
      *
-     * @param drawable   Cursor drawable.
-     * @param horizontal Horizontal position for the cursor.
-     * @return The clamped horizontal position for the cursor.
+     * @param drawable Drawable. Can be null.
+     * @param horizontal Horizontal position for the drawable.
+     * @return The clamped horizontal position for the drawable.
      */
-    private final int clampCursorHorizontalPosition(final Drawable drawable, float
-            horizontal) {
+    private int clampHorizontalPosition(@Nullable final Drawable drawable, float horizontal) {
         horizontal = Math.max(0.5f, horizontal - 0.5f);
         if (mTempRect == null) mTempRect = new Rect();
-        drawable.getPadding(mTempRect);
+
+        int drawableWidth = 0;
+        if (drawable != null) {
+            drawable.getPadding(mTempRect);
+            drawableWidth = drawable.getIntrinsicWidth();
+        } else {
+            mTempRect.setEmpty();
+        }
+
         int scrollX = mTextView.getScrollX();
         float horizontalDiff = horizontal - scrollX;
         int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft()
@@ -2221,9 +2229,11 @@
         final int left;
         if (horizontalDiff >= (viewClippedWidth - 1f)) {
             // at the rightmost position
-            final int cursorWidth = drawable.getIntrinsicWidth();
-            left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right);
-        } else if (Math.abs(horizontalDiff) <= 1f) {
+            left = viewClippedWidth + scrollX - (drawableWidth - mTempRect.right);
+        } else if (Math.abs(horizontalDiff) <= 1f ||
+                (TextUtils.isEmpty(mTextView.getText())
+                        && (TextView.VERY_WIDE - scrollX) <= (viewClippedWidth + 1f)
+                        && horizontal <= 1f)) {
             // at the leftmost position
             left = scrollX - mTempRect.left;
         } else {
@@ -3772,10 +3782,10 @@
                                 + mHandleHeight);
             } else {
                 // We have a single cursor.
-                Layout layout = getActiveLayout();
+                Layout layout = mTextView.getLayout();
                 int line = layout.getLineForOffset(mTextView.getSelectionStart());
-                float primaryHorizontal =
-                        layout.getPrimaryHorizontal(mTextView.getSelectionStart());
+                float primaryHorizontal = clampHorizontalPosition(null,
+                        layout.getPrimaryHorizontal(mTextView.getSelectionStart()));
                 mSelectionBounds.set(
                         primaryHorizontal,
                         layout.getLineTop(line),
@@ -4152,7 +4162,7 @@
                 prepareCursorControllers();
                 return;
             }
-            layout = getActiveLayout();
+            layout = mTextView.getLayout();
 
             boolean offsetChanged = offset != mPreviousOffset;
             if (offsetChanged || parentScrolled) {
@@ -4322,19 +4332,6 @@
         public void onDetached() {}
     }
 
-    /**
-     * Returns the active layout (hint or text layout). Note that the text layout can be null.
-     */
-    private Layout getActiveLayout() {
-        Layout layout = mTextView.getLayout();
-        Layout hintLayout = mTextView.getHintLayout();
-        if (TextUtils.isEmpty(layout.getText()) && hintLayout != null &&
-                !TextUtils.isEmpty(hintLayout.getText())) {
-            layout = hintLayout;
-        }
-        return layout;
-    }
-
     private class InsertionHandleView extends HandleView {
         private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
         private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
@@ -4431,7 +4428,7 @@
             final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null;
             if (drawable != null) {
                 final float horizontal = layout.getPrimaryHorizontal(offset);
-                return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left;
+                return clampHorizontalPosition(drawable, horizontal) + mTempRect.left;
             }
             return super.getCursorHorizontalPosition(layout, offset);
         }
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 04d344f..222a040 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -913,7 +913,9 @@
         if (mDrawable != null) {
             mDrawable.setCallback(null);
             unscheduleDrawable(mDrawable);
-            mDrawable.setVisible(false, false);
+            if (isAttachedToWindow()) {
+                mDrawable.setVisible(false, false);
+            }
         }
 
         mDrawable = d;
@@ -924,8 +926,9 @@
             if (d.isStateful()) {
                 d.setState(getDrawableState());
             }
-            d.setVisible(isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown(),
-                    true);
+            if (isAttachedToWindow()) {
+                d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true);
+            }
             d.setLevel(mLevel);
             mDrawableWidth = d.getIntrinsicWidth();
             mDrawableHeight = d.getIntrinsicHeight();
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index e0ef86c..9e8f778 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -869,10 +869,10 @@
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
-        final int delta = heightSize - mTotalLength
+        int remainingExcess = heightSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
-            final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+            float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             mTotalLength = 0;
 
@@ -883,9 +883,12 @@
                 }
 
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    final int share = (int) (childExtra * delta / weightSum);
+                final float childWeight = lp.weight;
+                if (childWeight > 0) {
+                    final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
+                    remainingExcess -= share;
+                    remainingWeightSum -= childWeight;
+
                     final int childHeight;
                     if (lp.height == 0 && (!mAllowInconsistentMeasurement
                             || heightMode == MeasureSpec.EXACTLY)) {
@@ -1244,10 +1247,10 @@
         // Either expand children with weight to take up available space or
         // shrink them if they extend beyond our current bounds. If we skipped
         // measurement on any children, we need to measure them now.
-        final int delta = widthSize - mTotalLength
+        int remainingExcess = widthSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
-        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
-            final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
+        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+            float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
             maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
@@ -1262,9 +1265,12 @@
                 }
 
                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    final int share = (int) (childExtra * delta / weightSum);
+                final float childWeight = lp.weight;
+                if (childWeight > 0) {
+                    final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
+                    remainingExcess -= share;
+                    remainingWeightSum -= childWeight;
+
                     final int childWidth;
                     if (lp.width == 0 && (!mAllowInconsistentMeasurement
                             || widthMode == MeasureSpec.EXACTLY)) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index dcadb6a..36e0c77 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -879,12 +879,13 @@
     /**
      * Filter key down events. By forwarding key down events to this function,
      * views using non-modal ListPopupWindow can have it handle key selection of items.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyDown
      * @param event event param passed to the host view's onKeyDown
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
+     * @see #onKeyUp(int, KeyEvent)
      */
     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
         // when the drop down is shown, we drive it directly
@@ -972,14 +973,15 @@
     }
 
     /**
-     * Filter key down events. By forwarding key up events to this function,
+     * Filter key up events. By forwarding key up events to this function,
      * views using non-modal ListPopupWindow can have it handle key selection of items.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyUp
      * @param event event param passed to the host view's onKeyUp
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
+     * @see #onKeyDown(int, KeyEvent)
      */
     public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
         if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
@@ -998,11 +1000,11 @@
      * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
      * events to this function, views using ListPopupWindow can have it dismiss the popup
      * when the back key is pressed.
-     *  
+     *
      * @param keyCode keyCode param passed to the host view's onKeyPreIme
      * @param event event param passed to the host view's onKeyPreIme
      * @return true if the event was handled, false if it was ignored.
-     * 
+     *
      * @see #setModal(boolean)
      */
     public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index dd6a41f..a1417f0 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,12 +16,10 @@
 
 package android.widget;
 
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
-
 import com.android.internal.R;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
@@ -54,10 +52,46 @@
 
 import java.lang.ref.WeakReference;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
 /**
- * <p>A popup window that can be used to display an arbitrary view. The popup
- * window is a floating container that appears on top of the current
- * activity.</p>
+ * <p>
+ * This class represents a popup window that can be used to display an
+ * arbitrary view. The popup window is a floating container that appears on top
+ * of the current activity.
+ * </p>
+ * <a name="Animation"></a>
+ * <h3>Animation</h3>
+ * <p>
+ * On all versions of Android, popup window enter and exit animations may be
+ * specified by calling {@link #setAnimationStyle(int)} and passing the
+ * resource ID for an animation style that defines {@code windowEnterAnimation}
+ * and {@code windowExitAnimation}. For example, passing
+ * {@link android.R.style#Animation_Dialog} will give a scale and alpha
+ * animation.
+ * </br>
+ * A window animation style may also be specified in the popup window's style
+ * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle}
+ * attribute.
+ * </p>
+ * <p>
+ * Starting with API 23, more complex popup window enter and exit transitions
+ * may be specified by calling either {@link #setEnterTransition(Transition)}
+ * or {@link #setExitTransition(Transition)} and passing a  {@link Transition}.
+ * </br>
+ * Popup enter and exit transitions may also be specified in the popup window's
+ * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition}
+ * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition}
+ * attributes, respectively.
+ * </p>
+ *
+ * @attr ref android.R.styleable#PopupWindow_overlapAnchor
+ * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle
+ * @attr ref android.R.styleable#PopupWindow_popupBackground
+ * @attr ref android.R.styleable#PopupWindow_popupElevation
+ * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+ * @attr ref android.R.styleable#PopupWindow_popupExitTransition
  *
  * @see android.widget.AutoCompleteTextView
  * @see android.widget.Spinner
@@ -351,15 +385,54 @@
         setFocusable(focusable);
     }
 
-    public void setEnterTransition(Transition enterTransition) {
+    /**
+     * Sets the enter transition to be used when the popup window is shown.
+     *
+     * @param enterTransition the enter transition, or {@code null} to clear
+     * @see #getEnterTransition()
+     * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+     */
+    public void setEnterTransition(@Nullable Transition enterTransition) {
         mEnterTransition = enterTransition;
     }
 
-    public void setExitTransition(Transition exitTransition) {
+    /**
+     * Returns the enter transition to be used when the popup window is shown.
+     *
+     * @return the enter transition, or {@code null} if not set
+     * @see #setEnterTransition(Transition)
+     * @attr ref android.R.styleable#PopupWindow_popupEnterTransition
+     */
+    @Nullable
+    public Transition getEnterTransition() {
+        return mEnterTransition;
+    }
+
+    /**
+     * Sets the exit transition to be used when the popup window is dismissed.
+     *
+     * @param exitTransition the exit transition, or {@code null} to clear
+     * @see #getExitTransition()
+     * @attr ref android.R.styleable#PopupWindow_popupExitTransition
+     */
+    public void setExitTransition(@Nullable Transition exitTransition) {
         mExitTransition = exitTransition;
     }
 
     /**
+     * Returns the exit transition to be used when the popup window is
+     * dismissed.
+     *
+     * @return the exit transition, or {@code null} if not set
+     * @see #setExitTransition(Transition)
+     * @attr ref android.R.styleable#PopupWindow_popupExitTransition
+     */
+    @Nullable
+    public Transition getExitTransition() {
+        return mExitTransition;
+    }
+
+    /**
      * Sets the bounds used as the epicenter of the enter and exit transitions.
      * <p>
      * Transitions use a point or Rect, referred to as the epicenter, to orient
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5060350..3195097 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -285,8 +285,8 @@
 
     private static final RectF TEMP_RECTF = new RectF();
 
-    // XXX should be much larger
-    private static final int VERY_WIDE = 1024*1024;
+    /** @hide */
+    static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger
     private static final int ANIMATED_SCROLL_GAP = 250;
 
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9897b12..80e1db0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -128,8 +128,8 @@
                 com.android.internal.R.string.whichSendApplication,
                 com.android.internal.R.string.whichSendApplicationNamed),
         SENDTO(Intent.ACTION_SENDTO,
-                com.android.internal.R.string.whichSendApplication,
-                com.android.internal.R.string.whichSendApplicationNamed),
+                com.android.internal.R.string.whichSendToApplication,
+                com.android.internal.R.string.whichSendToApplicationNamed),
         SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
                 com.android.internal.R.string.whichSendApplication,
                 com.android.internal.R.string.whichSendApplicationNamed),
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8de9c09..d11787d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -107,7 +107,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 141 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 142 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index ce03bb8..8682030 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -308,9 +308,6 @@
         private static final int MIN_OVERFLOW_SIZE = 2;
         private static final int MAX_OVERFLOW_SIZE = 4;
 
-        /* The duration of the overflow button vector animation duration. */
-        private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400;
-
         private final Context mContext;
         private final View mParent;  // Parent for the popup window.
         private final PopupWindow mPopupWindow;
@@ -377,18 +374,6 @@
             }
         };
 
-        /* Runnable to reset the overflow button's drawable after an overflow transition. */
-        private final Runnable mResetOverflowButtonDrawable = new Runnable() {
-            @Override
-            public void run() {
-                if (mIsOverflowOpen) {
-                    mOverflowButton.setImageDrawable(mArrow);
-                } else {
-                    mOverflowButton.setImageDrawable(mOverflow);
-                }
-            }
-        };
-
         private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
         private boolean mHidden; // tracks whether this popup is hidden or hiding.
 
@@ -902,7 +887,9 @@
                 final Size containerSize = mOverflowPanelSize;
                 setSize(mContentContainer, containerSize);
                 mMainPanel.setAlpha(0);
+                mMainPanel.setVisibility(View.INVISIBLE);
                 mOverflowPanel.setAlpha(1);
+                mOverflowPanel.setVisibility(View.VISIBLE);
                 mOverflowButton.setImageDrawable(mArrow);
 
                 // Update x-coordinates depending on RTL state.
@@ -941,7 +928,9 @@
                 final Size containerSize = mMainPanelSize;
                 setSize(mContentContainer, containerSize);
                 mMainPanel.setAlpha(1);
+                mMainPanel.setVisibility(View.VISIBLE);
                 mOverflowPanel.setAlpha(0);
+                mOverflowPanel.setVisibility(View.INVISIBLE);
                 mOverflowButton.setImageDrawable(mOverflow);
 
                 if (hasOverflow()) {
@@ -1327,8 +1316,6 @@
                         mToArrow.start();
                         openOverflow();
                     }
-                    overflowButton.postDelayed(
-                            mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY);
                 }
             });
             return overflowButton;
@@ -1389,6 +1376,10 @@
                     // Disable the overflow button while it's animating.
                     // It will be re-enabled when the animation stops.
                     mOverflowButton.setEnabled(false);
+                    // Ensure both panels have visibility turned on when the overflow animation
+                    // starts.
+                    mMainPanel.setVisibility(View.VISIBLE);
+                    mOverflowPanel.setVisibility(View.VISIBLE);
                 }
 
                 @Override
diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml
index 5d3ba22..6e12105 100644
--- a/core/res/res/anim/progress_indeterminate_rotation_material.xml
+++ b/core/res/res/anim/progress_indeterminate_rotation_material.xml
@@ -16,7 +16,7 @@
 -->
 
 <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="6665"
+    android:duration="4444"
     android:interpolator="@android:anim/linear_interpolator"
     android:propertyName="rotation"
     android:repeatCount="-1"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
index cd678f1..6448f3b 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml
@@ -16,22 +16,22 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="76dp"
         android:width="76dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48"
+        android:viewportHeight="76"
+        android:viewportWidth="76"
         android:tint="?attr/colorControlActivated">
 
     <group
         android:name="root"
-        android:translateX="24.0"
-        android:translateY="24.0" >
+        android:translateX="38.0"
+        android:translateY="38.0" >
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -29 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
-            android:strokeWidth="4"
+            android:strokeWidth="6"
             android:trimPathEnd="0"
             android:trimPathOffset="0"
             android:trimPathStart="0" />
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
index 7f038f4..80e3355 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml
@@ -27,7 +27,7 @@
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -18 a 18,18 0 1,1 0,36 a 18,18 0 1,1 0,-36"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
index 5625788..ebb632a 100644
--- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml
+++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml
@@ -16,22 +16,22 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="16dp"
         android:width="16dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48"
+        android:viewportHeight="16"
+        android:viewportWidth="16"
         android:tint="?attr/colorControlActivated">
 
     <group
         android:name="root"
-        android:translateX="24.0"
-        android:translateY="24.0" >
+        android:translateX="8.0"
+        android:translateY="8.0" >
         <path
             android:name="progressBar"
             android:fillColor="#00000000"
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -5.9375 a 5.9375,5.9375 0 1,1 0,11.875 a 5.9375,5.9375 0 1,1 0,-11.875"
             android:strokeColor="@color/white"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
-            android:strokeWidth="4"
+            android:strokeWidth="2.125"
             android:trimPathEnd="0"
             android:trimPathOffset="0"
             android:trimPathStart="0" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3c5b4c0..7b85c7e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2707,6 +2707,8 @@
     <public type="attr" name="tunerCount" />
     <public type="attr" name="nfcAntennaPositionDrawable" />
     <public type="attr" name="fillType" />
+    <public type="attr" name="popupEnterTransition" />
+    <public type="attr" name="popupExitTransition" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5d083d7..ba9bcaa 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2567,6 +2567,13 @@
     <!-- Title of intent resolver dialog when selecting a sharing application to run
          and a previously used application is known. -->
     <string name="whichSendApplicationNamed">Share with %1$s</string>
+    <!-- Title of intent resolver dialog when selecting an application to run to
+         send content to a specific recipient. Often used for email. -->
+    <string name="whichSendToApplication">Send using</string>
+    <!-- Title of intent resolver dialog when selecting an application to run to
+         send content to a specific recipient and a previously used application is known.
+         Often used for email. -->
+    <string name="whichSendToApplicationNamed">Send using %1$s</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run. -->
     <string name="whichHomeApplication">Select a Home app</string>
     <!-- Title of intent resolver dialog when selecting a HOME application to run
@@ -2936,8 +2943,8 @@
 
     <!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. -->
     <string name="select_input_method">Change keyboard</string>
-    <!-- Title of a button to open the settings to enable or disable keyboards, also known as input methods [CHAR LIMIT=30] -->
-    <string name="configure_input_methods">Choose keyboards</string>
+    <!-- Title of a button to open the settings to enable or disable other soft keyboards (also known as input methods) [CHAR LIMIT=30] -->
+    <string name="configure_input_methods">Other keyboards</string>
     <!-- Summary text of a toggle switch to enable/disable use of the IME while a physical
          keyboard is connected -->
     <string name="show_ime">Keep it on screen while physical keyboard is active</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f01cce3..8b78183 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2213,6 +2213,8 @@
   <java-symbol type="string" name="whichEditApplicationNamed" />
   <java-symbol type="string" name="whichSendApplication" />
   <java-symbol type="string" name="whichSendApplicationNamed" />
+  <java-symbol type="string" name="whichSendToApplication" />
+  <java-symbol type="string" name="whichSendToApplicationNamed" />
   <java-symbol type="attr" name="lightY" />
   <java-symbol type="attr" name="lightZ" />
   <java-symbol type="attr" name="lightRadius" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bfa2b10..b780778 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -111,7 +111,7 @@
     <!-- accessibility test permissions -->
     <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
 
-    <application android:theme="@style/Theme">
+    <application android:theme="@style/Theme" android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
         <meta-data
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index 3be9cfc..e1b305f 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -69,6 +69,10 @@
     public void testCombiningEnclosingKeycaps() {
         EditorState state = new EditorState();
 
+        state.setByString("'1' U+E0101 U+20E3 |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
         // multiple COMBINING ENCLOSING KEYCAP
         state.setByString("'1' U+20E3 U+20E3 |");
         backspace(state, 0);
@@ -157,6 +161,19 @@
     public void testEmojiZWJSequence() {
         EditorState state = new EditorState();
 
+        // U+200D is ZERO WIDTH JOINER.
+        state.setByString("U+1F441 U+200D U+1F5E8 |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
+        state.setByString("U+1F441 U+200D U+1F5E8 U+FE0E |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
+        state.setByString("U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468 |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
         // End with ZERO WIDTH JOINER
         state.setByString("U+1F441 U+200D |");
         backspace(state, 0);
@@ -224,6 +241,11 @@
     public void testEmojiModifier() {
         EditorState state = new EditorState();
 
+        // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
+        state.setByString("U+1F466 U+1F3FB |");
+        backspace(state, 0);
+        state.assertEquals("|");
+
         // Isolated emoji modifier
         state.setByString("U+1F3FB |");
         backspace(state, 0);
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index f7dab2d..0fed77c 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -137,6 +137,15 @@
     public void testEmojiZeroWidthJoinerSequence() {
         EditorState state = new EditorState();
 
+        // U+200D is ZERO WIDTH JOINER.
+        state.setByString("| U+1F441 U+200D U+1F5E8");
+        forwardDelete(state, 0);
+        state.assertEquals("|");
+
+        state.setByString("| U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468");
+        forwardDelete(state, 0);
+        state.assertEquals("|");
+
         // End with ZERO WIDTH JOINER
         state.setByString("| U+1F441 U+200D");
         forwardDelete(state, 0);
@@ -188,6 +197,11 @@
     public void testEmojiModifier() {
         EditorState state = new EditorState();
 
+        // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
+        state.setByString("| U+1F466 U+1F3FB");
+        forwardDelete(state, 0);
+        state.assertEquals("|");
+
         // Isolated emoji modifier
         state.setByString("| U+1F3FB");
         forwardDelete(state, 0);
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index 04c8b8c..6d650ff 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -16,16 +16,34 @@
 
 package android.widget;
 
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Choreographer;
 import android.view.ViewGroup;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.isEmptyString;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+
 public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
 
+
+    private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa";
+    private final static String LTR_HINT = "hint";
+    private final static String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";
+    private final static String RTL_HINT = "الروبوت";
+    private final static int CURSOR_BLINK_MS = 500;
+
     private EditText mEditText;
-    private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";
 
     public EditorCursorTest() {
         super(TextViewActivity.class);
@@ -55,110 +73,160 @@
             @Override
             public void run() {
                 getActivity().setContentView(layout);
-                mEditText.requestFocus();
             }
         });
         getInstrumentation().waitForIdleSync();
+        onView(sameInstance(mEditText)).perform(click());
     }
 
     @SmallTest
-    public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception {
+    public void testCursorIsInViewBoundariesWhenOnRightForLtr() {
         // Asserts that when an EditText has LTR text, and cursor is at the end (right),
         // cursor is drawn to the right edge of the view
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
-                int length = mEditText.getText().length();
-                mEditText.setSelection(length, length);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
+        setEditTextText(LTR_STRING, LTR_STRING.length());
 
-        Editor editor = mEditText.getEditorForTesting();
-        Drawable drawable = editor.getCursorDrawable()[0];
-        Rect drawableBounds = drawable.getBounds();
-        Rect drawablePadding = new Rect();
-        drawable.getPadding(drawablePadding);
-
-        // right edge of the view including the scroll
-        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
-                - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX();
-        int diff = drawableBounds.right - drawablePadding.right - maxRight;
-        assertTrue(diff >= 0 && diff <= 1);
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
     }
 
     @SmallTest
-    public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception {
+    public void testCursorIsInViewBoundariesWhenOnLeftForLtr() {
         // Asserts that when an EditText has LTR text, and cursor is at the beginning,
         // cursor is drawn to the left edge of the view
+        setEditTextText(LTR_STRING, 0);
 
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
-                mEditText.setSelection(0, 0);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-
-        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
-        Rect drawableBounds = drawable.getBounds();
-        Rect drawablePadding = new Rect();
-        drawable.getPadding(drawablePadding);
-
-        int diff = drawableBounds.left + drawablePadding.left;
-        assertTrue(diff >= 0 && diff <= 1);
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
     }
 
     @SmallTest
-    public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception {
+    public void testCursorIsInViewBoundariesWhenOnRightForRtl() {
         // Asserts that when an EditText has RTL text, and cursor is at the end,
         // cursor is drawn to the left edge of the view
+        setEditTextText(RTL_STRING, 0);
 
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mEditText.setText(RTL_STRING);
-                mEditText.setSelection(0, 0);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-
-        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
-        Rect drawableBounds = drawable.getBounds();
-        Rect drawablePadding = new Rect();
-        drawable.getPadding(drawablePadding);
-
-        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
-                - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX();
-
-        int diff = drawableBounds.right - drawablePadding.right - maxRight;
-        assertTrue(diff >= 0 && diff <= 1);
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
     }
 
     @SmallTest
-    public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception {
+    public void testCursorIsInViewBoundariesWhenOnLeftForRtl() {
         // Asserts that when an EditText has RTL text, and cursor is at the beginning,
         // cursor is drawn to the right edge of the view
+        setEditTextText(RTL_STRING, RTL_STRING.length());
 
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+    }
+
+    /* Tests for cursor positioning with hint */
+    @SmallTest
+    public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() {
+        setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+        assertThat(mEditText.getHint(), nullValue());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+
+        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+
+        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+    }
+
+    @SmallTest
+    public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() {
+        setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+        assertThat(mEditText.getHint(), nullValue());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+
+        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+
+        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+    }
+
+    @SmallTest
+    public void testCursorIsOnLeft_withLtrAlgorithm() {
+        setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+        assertThat(mEditText.getHint(), nullValue());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+
+        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+
+        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft());
+    }
+
+    @SmallTest
+    public void testCursorIsOnRight_withRtlAlgorithm() {
+        setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+        assertThat(mEditText.getHint(), nullValue());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+
+        setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+
+        setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0);
+        assertThat(mEditText.getText().toString(), isEmptyString());
+
+        onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight());
+    }
+
+    private void setEditTextProperties(final String text, final String hint,
+            final Integer textDirection, final Integer selection) {
         getActivity().runOnUiThread(new Runnable() {
             @Override
             public void run() {
-                mEditText.setText(RTL_STRING);
-                int length = mEditText.getText().length();
-                mEditText.setSelection(length, length);
+                if (textDirection != null) mEditText.setTextDirection(textDirection);
+                if (text != null) mEditText.setText(text);
+                if (hint != null) mEditText.setHint(hint);
+                if (selection != null) mEditText.setSelection(selection);
             }
         });
         getInstrumentation().waitForIdleSync();
 
-        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
-        Rect drawableBounds = drawable.getBounds();
-        Rect drawablePadding = new Rect();
-        drawable.getPadding(drawablePadding);
-
-        int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left;
-        assertTrue(diff >= 0 && diff <= 1);
+        // wait for cursor to be drawn. updateCursorPositions function is called during draw() and
+        // only when cursor is visible during blink.
+        final CountDownLatch latch = new CountDownLatch(1);
+        mEditText.postOnAnimationDelayed(new Runnable() {
+            @Override
+            public void run() {
+                latch.countDown();
+            }
+        }, CURSOR_BLINK_MS);
+        try {
+            assertThat("Problem while waiting for the cursor to blink",
+                    latch.await(10, TimeUnit.SECONDS), equalTo(true));
+        } catch (Exception e) {
+            fail("Problem while waiting for the cursor to blink");
+        }
     }
 
+    private void setEditTextHint(final String hint, final int textDirection, final int selection) {
+        setEditTextProperties(null, hint, textDirection, selection);
+    }
+
+    private void setEditTextText(final String text, final Integer selection) {
+        setEditTextProperties(text, null, null, selection);
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4a4727f..91d57e7 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,8 +16,10 @@
 
 package android.widget;
 
+import static android.support.test.espresso.action.ViewActions.longClick;
 import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
 import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -49,6 +51,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.Selection;
 import android.text.Spannable;
+import android.text.InputType;
 import android.view.KeyEvent;
 
 import static org.hamcrest.Matchers.anyOf;
@@ -234,6 +237,33 @@
     }
 
     @SmallTest
+    public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint()
+            throws Exception {
+        // after the hint layout change, the floating toolbar was not visible in the case below
+        // this test tests that the floating toolbar is displayed on the screen and is visible to
+        // user.
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        textView.post(new Runnable() {
+            @Override
+            public void run() {
+                textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR);
+                textView.setInputType(InputType.TYPE_CLASS_TEXT);
+                textView.setSingleLine(true);
+                textView.setHint("الروبوت");
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test"));
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
+        onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click());
+        onView(withId(R.id.textview)).perform(longClick());
+        sleepForFloatingToolbarPopup();
+
+        assertFloatingToolbarIsDisplayed();
+    }
+
+    @SmallTest
     public void testToolbarAndInsertionHandle() throws Exception {
         final String text = "text";
         onView(withId(R.id.textview)).perform(click());
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index f02fe00..0f7f359 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -54,6 +54,16 @@
     }
 
     /**
+     * Creates a {@link ViewInteraction} for the floating bar menu item with the given matcher.
+     *
+     * @param matcher The matcher for the menu item.
+     */
+    public static ViewInteraction onFloatingToolBarItem(Matcher<View> matcher) {
+        return onView(matcher)
+                .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
+    }
+
+    /**
      * Asserts that the floating toolbar is displayed on screen.
      *
      * @throws AssertionError if the assertion fails
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
index 37c7425..6e44cd8 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -19,15 +19,23 @@
 import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.number.IsCloseTo.closeTo;
 
+import android.annotation.IntDef;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
 import android.view.View;
+import android.widget.EditText;
 import android.widget.TextView;
 
 import junit.framework.AssertionFailedError;
 import org.hamcrest.Matcher;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A collection of assertions on a {@link android.widget.TextView}.
  */
@@ -113,6 +121,22 @@
     }
 
     /**
+     * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on
+     * the left edge.
+     */
+    public static ViewAssertion hasInsertionPointerOnLeft() {
+        return new CursorPositionAssertion(CursorPositionAssertion.LEFT);
+    }
+
+    /**
+     * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on
+     * the right edge.
+     */
+    public static ViewAssertion hasInsertionPointerOnRight() {
+        return new CursorPositionAssertion(CursorPositionAssertion.RIGHT);
+    }
+
+    /**
      * A {@link ViewAssertion} to check the selected text in a {@link TextView}.
      */
     private static final class TextSelectionAssertion implements ViewAssertion {
@@ -142,4 +166,54 @@
             }
         }
     }
+
+    /**
+     * {@link ViewAssertion} to check that EditText cursor is on a given position.
+     */
+    static class CursorPositionAssertion implements ViewAssertion {
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({LEFT, RIGHT})
+        public @interface CursorEdgePositionType {}
+        public static final int LEFT = 0;
+        public static final int RIGHT = 1;
+
+        private final int mPosition;
+
+        private CursorPositionAssertion(@CursorEdgePositionType int position) {
+            this.mPosition = position;
+        }
+
+        @Override
+        public void check(View view, NoMatchingViewException exception) {
+            if (!(view instanceof EditText)) {
+                throw new AssertionFailedError("View should be an instance of EditText");
+            }
+            EditText editText = (EditText) view;
+            Drawable drawable = editText.getEditorForTesting().getCursorDrawable()[0];
+            Rect drawableBounds = drawable.getBounds();
+            Rect drawablePadding = new Rect();
+            drawable.getPadding(drawablePadding);
+
+            final int diff;
+            final String positionStr;
+            switch (mPosition) {
+                case LEFT:
+                    positionStr = "left";
+                    diff = drawableBounds.left - editText.getScrollX() + drawablePadding.left;
+                    break;
+                case RIGHT:
+                    positionStr = "right";
+                    int maxRight = editText.getWidth() - editText.getCompoundPaddingRight()
+                            - editText.getCompoundPaddingLeft() + editText.getScrollX();
+                    diff = drawableBounds.right - drawablePadding.right - maxRight;
+                    break;
+                default:
+                    throw new AssertionFailedError("Unknown position for cursor assertion");
+            }
+
+            assertThat("Cursor should be on the " + positionStr, Double.valueOf(diff),
+                    closeTo(0f, 1f));
+        }
+    }
 }
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index e3f1d9e..ca7255b 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -284,7 +284,7 @@
     </tr>
     <tr>
         <td class="label_neg">drawPicture()</td>
-        <td class="value_neg">23</td>
+        <td class="value_pos">23</td>
     </tr>
     <tr>
         <td class="label_pos">drawPosText()</td>
@@ -421,14 +421,6 @@
         <td colspan="5" class="s5">Xfermode</td>
     </tr>
     <tr>
-        <td class="label_neg">AvoidXfermode</td>
-        <td class="value_neg">&#10007;</td>
-    </tr>
-    <tr>
-        <td class="label_neg">PixelXorXfermode</td>
-        <td class="value_neg">&#10007;</td>
-    </tr>
-    <tr>
         <td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td>
         <td class="value_neg">&#10007;</td>
     </tr>
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 1fc236a..ed358d3 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -605,8 +605,9 @@
     // not only getting information from EXIF but also from some JPEG special segments such as
     // MARKER_COM for user comment and MARKER_SOFx for image width and height.
 
-    // Identifier for APP1 segment in JPEG
-    private static final byte[] IDENTIFIER_APP1 = "Exif\0\0".getBytes(Charset.forName("US-ASCII"));
+    // Identifier for EXIF APP1 segment in JPEG
+    private static final byte[] IDENTIFIER_EXIF_APP1 =
+            "Exif\0\0".getBytes(Charset.forName("US-ASCII"));
     // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
     // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
     // of frame(baseline DCT) and the image size info exists in its beginning part.
@@ -1125,7 +1126,9 @@
         String time = getAttribute(TAG_GPS_TIMESTAMP);
         if (date == null || time == null
                 || (!sNonZeroTimePattern.matcher(date).matches()
-                && !sNonZeroTimePattern.matcher(time).matches())) return -1;
+                && !sNonZeroTimePattern.matcher(time).matches())) {
+            return -1;
+        }
 
         String dateTimeString = date + ' ' + time;
 
@@ -1176,7 +1179,6 @@
         DataInputStream dataInputStream = new DataInputStream(inputStream);
         byte marker;
         int bytesRead = 0;
-        ++bytesRead;
         if ((marker = dataInputStream.readByte()) != MARKER) {
             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
         }
@@ -1184,8 +1186,8 @@
         if (dataInputStream.readByte() != MARKER_SOI) {
             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
         }
+        ++bytesRead;
         while (true) {
-            ++bytesRead;
             marker = dataInputStream.readByte();
             if (marker != MARKER) {
                 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
@@ -1195,36 +1197,40 @@
             if (DEBUG) {
                 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
             }
+            ++bytesRead;
 
             // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
             // the image data will terminate right after.
             if (marker == MARKER_EOI || marker == MARKER_SOS) {
                 break;
             }
-            bytesRead += 2;
             int length = dataInputStream.readUnsignedShort() - 2;
+            bytesRead += 2;
+            if (DEBUG) {
+                Log.d(TAG, "JPEG segment: " + marker + " (length: " + (length + 2) + ")");
+            }
             if (length < 0) {
                 throw new IOException("Invalid length");
             }
-            bytesRead += length;
             switch (marker) {
                 case MARKER_APP1: {
                     if (DEBUG) {
                         Log.d(TAG, "MARKER_APP1");
                     }
-                    bytesRead -= length;
                     if (length < 6) {
-                        throw new IOException("Invalid exif");
+                        // Skip if it's not an EXIF APP1 segment.
+                        break;
                     }
                     byte[] identifier = new byte[6];
                     if (inputStream.read(identifier) != 6) {
                         throw new IOException("Invalid exif");
                     }
-                    if (!Arrays.equals(identifier, IDENTIFIER_APP1)) {
-                        throw new IOException("Invalid app1 identifier");
-                    }
                     bytesRead += 6;
                     length -= 6;
+                    if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+                        // Skip if it's not an EXIF APP1 segment.
+                        break;
+                    }
                     if (length <= 0) {
                         throw new IOException("Invalid exif");
                     }
@@ -1246,6 +1252,7 @@
                     if (dataInputStream.read(bytes) != length) {
                         throw new IOException("Invalid exif");
                     }
+                    length = 0;
                     setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII")));
                     break;
                 }
@@ -1279,6 +1286,7 @@
                 throw new IOException("Invalid length");
             }
             dataInputStream.skipBytes(length);
+            bytesRead += length;
         }
     }
 
@@ -1292,68 +1300,84 @@
         }
         DataInputStream dataInputStream = new DataInputStream(inputStream);
         ExifDataOutputStream dataOutputStream = new ExifDataOutputStream(outputStream);
-        int bytesRead = 0;
-        ++bytesRead;
         if (dataInputStream.readByte() != MARKER) {
             throw new IOException("Invalid marker");
         }
         dataOutputStream.writeByte(MARKER);
-        ++bytesRead;
         if (dataInputStream.readByte() != MARKER_SOI) {
             throw new IOException("Invalid marker");
         }
         dataOutputStream.writeByte(MARKER_SOI);
 
+        // Write EXIF APP1 segment
+        dataOutputStream.writeByte(MARKER);
+        dataOutputStream.writeByte(MARKER_APP1);
+        writeExifSegment(dataOutputStream, 6);
+
         byte[] bytes = new byte[4096];
 
         while (true) {
-            ++bytesRead;
             if (dataInputStream.readByte() != MARKER) {
                 throw new IOException("Invalid marker");
             }
-            dataOutputStream.writeByte(MARKER);
-            ++bytesRead;
             byte marker = dataInputStream.readByte();
-            dataOutputStream.writeByte(marker);
             switch (marker) {
                 case MARKER_APP1: {
-                    // Rewrite EXIF segment
                     int length = dataInputStream.readUnsignedShort() - 2;
                     if (length < 0) {
                         throw new IOException("Invalid length");
                     }
-                    bytesRead += 2;
+                    byte[] identifier = new byte[6];
+                    if (length >= 6) {
+                        if (dataInputStream.read(identifier) != 6) {
+                            throw new IOException("Invalid exif");
+                        }
+                        if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+                            // Skip the original EXIF APP1 segment.
+                            if (dataInputStream.skip(length - 6) != length - 6) {
+                                throw new IOException("Invalid length");
+                            }
+                            break;
+                        }
+                    }
+                    // Copy non-EXIF APP1 segment.
+                    dataOutputStream.writeUnsignedShort(length + 2);
+                    if (length >= 6) {
+                        length -= 6;
+                        dataOutputStream.write(identifier);
+                    }
                     int read;
-                    while ((read = dataInputStream.read(
-                            bytes, 0, Math.min(length, bytes.length))) > 0) {
+                    while (length > 0 && (read = dataInputStream.read(
+                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
+                        dataOutputStream.write(bytes, 0, read);
                         length -= read;
                     }
-                    bytesRead += length;
-                    writeExifSegment(dataOutputStream, bytesRead);
                     break;
                 }
                 case MARKER_EOI:
                 case MARKER_SOS: {
+                    dataOutputStream.writeByte(MARKER);
+                    dataOutputStream.writeByte(marker);
                     // Copy all the remaining data
                     Streams.copy(dataInputStream, dataOutputStream);
                     return;
                 }
                 default: {
                     // Copy JPEG segment
+                    dataOutputStream.writeByte(MARKER);
+                    dataOutputStream.writeByte(marker);
                     int length = dataInputStream.readUnsignedShort();
                     dataOutputStream.writeUnsignedShort(length);
+                    length -= 2;
                     if (length < 0) {
                         throw new IOException("Invalid length");
                     }
-                    length -= 2;
-                    bytesRead += 2;
                     int read;
-                    while ((read = dataInputStream.read(
-                            bytes, 0, Math.min(length, bytes.length))) > 0) {
+                    while (length > 0 && (read = dataInputStream.read(
+                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
                         dataOutputStream.write(bytes, 0, read);
                         length -= read;
                     }
-                    bytesRead += length;
                     break;
                 }
             }
@@ -1924,7 +1948,7 @@
 
         // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10.
         dataOutputStream.writeUnsignedShort(totalSize);
-        dataOutputStream.write(IDENTIFIER_APP1);
+        dataOutputStream.write(IDENTIFIER_EXIF_APP1);
         dataOutputStream.writeShort(BYTE_ALIGN_MM);
         dataOutputStream.writeUnsignedShort(0x2a);
         dataOutputStream.writeUnsignedInt(8);
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 86087c3..ebb3969 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -21,4 +21,8 @@
     <!-- Intentionally unset. Vendors should set this in an overlay. -->
     <string name="trusted_quick_viewer_package" translatable="false"></string>
     <bool name="list_divider_inset_left">true</bool>
+    <!-- Indicates if the home directory should be hidden in the roots list, that is presented
+         in the drawer/left side panel ) -->
+    <bool name="home_root_hidden">true</bool>
+
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index e7406e68..0d098e6 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -211,13 +211,29 @@
     <!-- Text in the button asking user to deny access to a given directory. -->
     <string name="deny">Deny</string>
     <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). -->
-    <plurals name="delete_confirmation_message">
-        <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item>
-        <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item>
-    </plurals>
     <!-- Label text showing user how many items are selected. Can be one or more elements. -->
     <plurals name="elements_selected">
         <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item>
         <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
     </plurals>
+
+    <!-- Dialog text shown to users when asking if they want to delete a file (a confirmation) -->
+    <string name="delete_filename_confirmation_message">Delete \"<xliff:g id="name" example="cat.jpg">%1$s</xliff:g>\"?</string>
+    <!-- Dialog text shown to users when asking if they want to delete a folder (a confirmation) -->
+    <string name="delete_foldername_confirmation_message">Delete folder \"<xliff:g id="name" example="Photos">%1$s</xliff:g>\" and its contents?</string>
+    <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). -->
+    <plurals name="delete_files_confirmation_message">
+        <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item>
+        <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item>
+    </plurals>
+    <!-- Dialog text shown to users when asking if they want to delete folders (a confirmation). -->
+    <plurals name="delete_folders_confirmation_message">
+        <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> folder and its contents?</item>
+        <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> folders and their contents?</item>
+    </plurals>
+    <!-- Dialog text shown to users when asking if they want to delete mixed type items: files and folders (a confirmation). -->
+    <plurals name="delete_items_confirmation_message">
+        <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> item?</item>
+        <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> items?</item>
+    </plurals>
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 9f83c04..54e6287 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -317,7 +317,10 @@
 
             for (final RootInfo root : roots) {
                 final RootItem item = new RootItem(root);
-                if (root.isLibrary()) {
+
+                if (root.isHome() && isHomeRootHidden(context)) {
+                    continue;
+                } else if (root.isLibrary()) {
                     if (DEBUG) Log.d(TAG, "Adding " + root + " as library.");
                     libraries.add(item);
                 } else {
@@ -367,6 +370,13 @@
             }
         }
 
+        /*
+         * Indicates if the home directory should be hidden in the roots list.
+         */
+        private boolean isHomeRootHidden(Context context) {
+            return context.getResources().getBoolean(R.bool.home_root_hidden);
+        }
+
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             final Item item = getItem(position);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index c32bbff..6f1863e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -151,6 +151,14 @@
         return sCollator.compare(lhs, rhs);
     }
 
+    /**
+     * Compare two strings against each other using system default collator in a
+     * case-insensitive mode.
+     */
+    public static int compareToIgnoreCase(String lhs, String rhs) {
+        return sCollator.compare(lhs, rhs);
+    }
+
     public static boolean isHardwareKeyboardAvailable(Context context) {
         return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 63a834f..60e4b9a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -688,6 +688,39 @@
         }.execute(selected);
     }
 
+    private String generateDeleteMessage(final List<DocumentInfo> docs) {
+        String message;
+        int dirsCount = 0;
+
+        for (DocumentInfo doc : docs) {
+            if (doc.isDirectory()) {
+                ++dirsCount;
+            }
+        }
+
+        if (docs.size() == 1) {
+            // Deleteing 1 file xor 1 folder in cwd
+            message = dirsCount == 0
+                    ? getActivity().getString(R.string.delete_filename_confirmation_message,
+                            docs.get(0).displayName)
+                    : getActivity().getString(R.string.delete_foldername_confirmation_message,
+                            docs.get(0).displayName);
+        } else if (dirsCount == 0) {
+            // Deleting only files in cwd
+            message = Shared.getQuantityString(getActivity(),
+                    R.plurals.delete_files_confirmation_message, docs.size());
+        } else if (dirsCount == docs.size()) {
+            // Deleting only folders in cwd
+            message = Shared.getQuantityString(getActivity(),
+                    R.plurals.delete_folders_confirmation_message, docs.size());
+        } else {
+            // Deleting mixed items (files and folders) in cwd
+            message = Shared.getQuantityString(getActivity(),
+                    R.plurals.delete_items_confirmation_message, docs.size());
+        }
+        return message;
+    }
+
     private void deleteDocuments(final Selection selected) {
         assert(!selected.isEmpty());
 
@@ -698,11 +731,7 @@
 
                 TextView message =
                         (TextView) mInflater.inflate(R.layout.dialog_delete_confirmation, null);
-                message.setText(
-                        Shared.getQuantityString(
-                                getActivity(),
-                                R.plurals.delete_confirmation_message,
-                                docs.size()));
+                message.setText(generateDeleteMessage(docs));
 
                 // This "insta-hides" files that are being deleted, because
                 // the delete operation may be not execute immediately (it
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index c5ee592..3642b01 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -20,10 +20,9 @@
 import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME;
 import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED;
 import static com.android.documentsui.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.model.DocumentInfo.getCursorLong;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
 import android.database.Cursor;
+import android.database.MergeCursor;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -48,6 +47,7 @@
 @VisibleForTesting
 public class Model {
     private static final String TAG = "Model";
+    private static final String EMPTY = "";
 
     private boolean mIsLoading;
     private List<UpdateListener> mUpdateListeners = new ArrayList<>();
@@ -62,34 +62,17 @@
     private String mIds[] = new String[0];
     private int mSortOrder = SORT_ORDER_DISPLAY_NAME;
 
+    private int mAuthorityIndex = -1;
+    private int mDocIdIndex = -1;
+    private int mMimeTypeIndex = -1;
+    private int mDisplayNameIndex = -1;
+    private int mSizeIndex = -1;
+    private int mLastModifiedIndex = -1;
+
     @Nullable String info;
     @Nullable String error;
     @Nullable DocumentInfo doc;
 
-    /**
-     * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique
-     * string that can be used to identify the document referred to by the cursor.
-     *
-     * @param c A cursor that refers to a document.
-     */
-    private static String createModelId(Cursor c) {
-        // TODO: Maybe more efficient to use just the document ID, in cases where there is only one
-        // authority (which should be the majority of cases).
-        return createModelId(
-                getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY),
-                getCursorString(c, Document.COLUMN_DOCUMENT_ID));
-    }
-
-    /**
-     * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique
-     * string that can be used to identify the document referred to by the cursor.
-     *
-     * @param c A cursor that refers to a document.
-     */
-    static String createModelId(String authority, String docId) {
-        return authority + "|" + docId;
-    }
-
     private void notifyUpdateListeners() {
         for (UpdateListener listener: mUpdateListeners) {
             listener.onModelUpdate(this);
@@ -127,6 +110,14 @@
         mCursor = result.cursor;
         mCursorCount = mCursor.getCount();
         mSortOrder = result.sortOrder;
+        mAuthorityIndex = mCursor.getColumnIndex(RootCursorWrapper.COLUMN_AUTHORITY);
+        assert(mAuthorityIndex != -1);
+        mDocIdIndex = mCursor.getColumnIndex(Document.COLUMN_DOCUMENT_ID);
+        mMimeTypeIndex = mCursor.getColumnIndex(Document.COLUMN_MIME_TYPE);
+        mDisplayNameIndex = mCursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME);
+        mLastModifiedIndex = mCursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED);
+        mSizeIndex = mCursor.getColumnIndex(Document.COLUMN_SIZE);
+
         doc = result.doc;
 
         updateModelData();
@@ -173,22 +164,29 @@
         for (int pos = 0; pos < mCursorCount; ++pos) {
             mCursor.moveToNext();
             positions[pos] = pos;
-            mIds[pos] = createModelId(mCursor);
 
-            mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
+            // Generates a Model ID for a cursor entry that refers to a document. The Model ID is a
+            // unique string that can be used to identify the document referred to by the cursor.
+            // If the cursor is a merged cursor over multiple authorities, then prefix the ids
+            // with the authority to avoid collisions.
+            if (mCursor instanceof MergeCursor) {
+                mIds[pos] = getStringOrEmpty(mAuthorityIndex) + "|" + getStringOrEmpty(mDocIdIndex);
+            } else {
+                mIds[pos] = getStringOrEmpty(mDocIdIndex);
+            }
+
+            mimeType = getStringOrEmpty(mMimeTypeIndex);
             isDirs[pos] = Document.MIME_TYPE_DIR.equals(mimeType);
 
-            switch(mSortOrder) {
+            switch (mSortOrder) {
                 case SORT_ORDER_DISPLAY_NAME:
-                    final String displayName = getCursorString(
-                            mCursor, Document.COLUMN_DISPLAY_NAME);
-                    displayNames[pos] = displayName;
+                    displayNames[pos] = getStringOrEmpty(mDisplayNameIndex);
                     break;
                 case SORT_ORDER_LAST_MODIFIED:
-                    longValues[pos] = getLastModified(mCursor);
+                    longValues[pos] = getLastModified();
                     break;
                 case SORT_ORDER_SIZE:
-                    longValues[pos] = getCursorLong(mCursor, Document.COLUMN_SIZE);
+                    longValues[pos] = getDocSize();
                     break;
             }
         }
@@ -244,7 +242,7 @@
                 } else {
                     final String lhs = pivotValue;
                     final String rhs = sortKey[mid];
-                    compare = Shared.compareToIgnoreCaseNullable(lhs, rhs);
+                    compare = Shared.compareToIgnoreCase(lhs, rhs);
                 }
 
                 if (compare < 0) {
@@ -365,13 +363,42 @@
     }
 
     /**
-     * @return Timestamp for the given document. Some docs (e.g. active downloads) have a null
-     * timestamp - these will be replaced with MAX_LONG so that such files get sorted to the top
-     * when sorting by date.
+     * @return Value of the string column, or an empty string if no value, or empty value.
      */
-    long getLastModified(Cursor cursor) {
-        long l = getCursorLong(mCursor, Document.COLUMN_LAST_MODIFIED);
-        return (l == -1) ? Long.MAX_VALUE : l;
+    private String getStringOrEmpty(int columnIndex) {
+        if (columnIndex == -1)
+            return EMPTY;
+        final String result = mCursor.getString(columnIndex);
+        return result != null ? result : EMPTY;
+    }
+
+    /**
+     * @return Timestamp for the given document. Some docs (e.g. active downloads) have a null
+     * or missing timestamp - these will be replaced with MAX_LONG so that such files get sorted to
+     * the top when sorting by date.
+     */
+    private long getLastModified() {
+        if (mLastModifiedIndex == -1)
+            return Long.MAX_VALUE;
+        try {
+            final long result = mCursor.getLong(mLastModifiedIndex);
+            return result > 0 ? result : Long.MAX_VALUE;
+        } catch (NumberFormatException e) {
+            return Long.MAX_VALUE;
+        }
+    }
+
+    /**
+     * @return Size for the given document. If the size is unknown or invalid, returns 0.
+     */
+    private long getDocSize() {
+        if (mSizeIndex == -1)
+            return 0;
+        try {
+            return mCursor.getLong(mSizeIndex);
+        } catch (NumberFormatException e) {
+            return 0;
+        }
     }
 
     public @Nullable Cursor getItem(String modelId) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index e4afc3d..f294919 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -58,18 +58,23 @@
     public void testRootsListed() throws Exception {
         initTestFiles();
 
-        bots.roots.openRoot(ROOT_0_ID);
-
         // Should also have Drive, but that requires pre-configuration of devices
         // We omit for now.
-        bots.roots.assertHasRoots(
+        bots.roots.assertRootsPresent(
                 "Images",
                 "Videos",
                 "Audio",
                 "Downloads",
-                "Documents",
                 ROOT_0_ID,
                 ROOT_1_ID);
+
+        // Separate logic for "Documents" root, which presence depends on the config setting
+        boolean homeRootHidden = context.getResources().getBoolean(R.bool.home_root_hidden);
+        if (homeRootHidden) {
+            bots.roots.assertRootsAbsent("Documents");
+        } else {
+            bots.roots.assertRootsPresent("Documents");
+        }
     }
 
     public void testFilesListed() throws Exception {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java
index 356fd01..096af10 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java
@@ -69,7 +69,7 @@
         mDevice.waitForIdle();
     }
 
-    public void assertHasRoots(String... labels) throws UiObjectNotFoundException {
+    public void assertRootsPresent(String... labels) throws UiObjectNotFoundException {
         List<String> missing = new ArrayList<>();
         for (String label : labels) {
             if (!findRoot(label).exists()) {
@@ -82,6 +82,18 @@
         }
     }
 
+    public void assertRootsAbsent(String... labels) throws UiObjectNotFoundException {
+        List<String> unexpected = new ArrayList<>();
+        for (String label : labels) {
+            if (findRoot(label).exists()) {
+                unexpected.add(label);
+            }
+        }
+        if (!unexpected.isEmpty()) {
+            Assert.fail("Unexpected roots " + unexpected);
+        }
+    }
+
     public void assertHasFocus() {
         assertHasFocus(ROOTS_LIST_ID);
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
index c6ad511..3536593 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
@@ -284,7 +284,7 @@
             String id = Integer.toString(i);
             row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY);
             row.add(Document.COLUMN_DOCUMENT_ID, id);
-            currentDownloads.add(Model.createModelId(AUTHORITY, id));
+            currentDownloads.add(id);
         }
 
         DirectoryResult r = new DirectoryResult();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java
index d8c29db..2d819ff 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java
@@ -62,9 +62,7 @@
         update(r);
     }
 
-    // Note that model id includes authority qualifier and is distinct
-    // WRT documentId because of this.
     String idForPosition(int p) {
-        return createModelId(mAuthority, Integer.toString(p));
+        return Integer.toString(p);
     }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 7a6aad6..2d3935b 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -870,39 +870,56 @@
                 // AsyncCommand.AsyncCommandHandler#handleMessage
                 if (isFailed()) {
                     if (DEBUG) {
-                        Log.i(LOG_TAG, "[CALLBACK] on canceled layout command");
+                        Log.i(LOG_TAG, "[CALLBACK] on failed layout command");
                     }
 
                     return;
-                } else {
-                    if (message.what != MSG_ON_LAYOUT_STARTED) {
-                        // No need to force cancel anymore if layout finished
-                        removeForceCancel();
-                    }
                 }
 
-                switch (message.what) {
+                int sequence;
+                int what = message.what;
+                switch (what) {
+                    case MSG_ON_LAYOUT_FINISHED:
+                        removeForceCancel();
+                        sequence = message.arg2;
+                        break;
+                    case MSG_ON_LAYOUT_FAILED:
+                    case MSG_ON_LAYOUT_CANCELED:
+                        removeForceCancel();
+                        // $FALL-THROUGH - message uses the same format as "started"
+                    case MSG_ON_LAYOUT_STARTED:
+                        // Don't remote force-cancel as command is still running and might need to
+                        // be canceled later
+                        sequence = message.arg1;
+                        break;
+                    default:
+                        // not reached
+                        sequence = -1;
+                }
+
+                // If we are canceling any result is treated as a cancel
+                if (isCanceling() && what != MSG_ON_LAYOUT_STARTED) {
+                    what = MSG_ON_LAYOUT_CANCELED;
+                }
+
+                switch (what) {
                     case MSG_ON_LAYOUT_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
-                        final int sequence = message.arg1;
                         handleOnLayoutStarted(cancellation, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FINISHED: {
                         PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
                         final boolean changed = (message.arg1 == 1);
-                        final int sequence = message.arg2;
                         handleOnLayoutFinished(info, changed, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        final int sequence = message.arg1;
                         handleOnLayoutFailed(error, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_CANCELED: {
-                        final int sequence = message.arg1;
                         handleOnLayoutCanceled(sequence);
                     } break;
                 }
@@ -1142,18 +1159,30 @@
                 // AsyncCommand.AsyncCommandHandler#handleMessage
                 if (isFailed()) {
                     if (DEBUG) {
-                        Log.i(LOG_TAG, "[CALLBACK] on canceled write command");
+                        Log.i(LOG_TAG, "[CALLBACK] on failed write command");
                     }
 
                     return;
-                } else {
-                    if (message.what != MSG_ON_WRITE_STARTED) {
-                        // No need to force cancel anymore if write finished
-                        removeForceCancel();
-                    }
                 }
 
-                switch (message.what) {
+                int what = message.what;
+                switch (what) {
+                    case MSG_ON_WRITE_FINISHED:
+                    case MSG_ON_WRITE_FAILED:
+                    case MSG_ON_WRITE_CANCELED:
+                        removeForceCancel();
+                    case MSG_ON_WRITE_STARTED:
+                        // Don't remote force-cancel as command is still running and might need to
+                        // be canceled later
+                        break;
+                }
+
+                // If we are canceling any result is treated as a cancel
+                if (isCanceling() && what != MSG_ON_WRITE_STARTED) {
+                    what = MSG_ON_WRITE_CANCELED;
+                }
+
+                switch (what) {
                     case MSG_ON_WRITE_STARTED: {
                         ICancellationSignal cancellation = (ICancellationSignal) message.obj;
                         final int sequence = message.arg1;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index ad4823e..4e1180d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -159,6 +159,7 @@
     private static final int STATE_PRINTER_UNAVAILABLE = 6;
     private static final int STATE_UPDATE_SLOW = 7;
     private static final int STATE_PRINT_COMPLETED = 8;
+    private static final int STATE_FINISHING = 9;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -2029,11 +2030,17 @@
     }
 
     private void doFinish() {
-        if (mPrintedDocument.isUpdating()) {
+        if (mPrintedDocument != null && mPrintedDocument.isUpdating()) {
             // The printedDocument will call doFinish() when the current command finishes
             return;
         }
 
+        if (mState == STATE_FINISHING) {
+            return;
+        }
+
+        mState = STATE_FINISHING;
+
         if (mPrinterRegistry != null) {
             mPrinterRegistry.setTrackedPrinter(null);
         }
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 5a6553f..9c2c0ab 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -27,29 +27,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:paddingEnd="12dp"
-            android:background="@android:color/white"
-            android:textColor="#D9000000"
+            android:textColor="@color/ksh_keyword_color"
             android:textSize="16sp"
             android:maxLines="5"
             android:singleLine="false"
             android:scrollHorizontally="false"
-            android:layout_alignParentStart="true"
-            android:minWidth="100dp"
-            android:maxWidth="260dp"/>
-    <!--TODO: introduce and use a layout that allows wrapping and right align -->
-    <LinearLayout
+            android:layout_alignParentStart="true"/>
+    <com.android.systemui.statusbar.KeyboardShortcutKeysLayout
             android:id="@+id/keyboard_shortcuts_item_container"
             android:layout_toEndOf="@+id/keyboard_shortcuts_keyword"
             android:orientation="horizontal"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:background="@android:color/white"
             android:layout_alignParentEnd="true"
-            android:gravity="end"
             android:textSize="14sp"
-            android:paddingStart="0dp"
-            android:paddingEnd="0dp"
-            android:scrollHorizontally="false"
-            android:minWidth="100dp"
-            android:maxWidth="260dp"/>
+            android:scrollHorizontally="false"/>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 80a478a..6cb8470 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -22,4 +22,4 @@
           android:paddingStart="24dp"
           android:paddingTop="20dp"
           android:paddingEnd="24dp"
-          android:paddingBottom="13dp" />
+          android:paddingBottom="13dp"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
index f73ee15..7aba1cf 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -17,7 +17,7 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="488dp"
+    android:layout_width="@dimen/ksh_layout_width"
     android:layout_height="wrap_content">
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a2fa3b9..66963c4 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -104,4 +104,7 @@
 
     <!-- The side padding for the task stack. -->
     <dimen name="recents_stack_left_right_padding">64dp</dimen>
+
+    <!-- Keyboard shortcuts helper -->
+    <dimen name="ksh_layout_width">488dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index eeed0cf..a3f8b85 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -167,6 +167,7 @@
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_system_group_color">#ff00bcd4</color>
     <color name="ksh_application_group_color">#fff44336</color>
+    <color name="ksh_keyword_color">#d9000000</color>
 
     <!-- Background color of edit overflow -->
     <color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6a9f456..8b433f9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -639,4 +639,7 @@
 
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
+
+    <!-- Keyboard shortcuts helper -->
+    <dimen name="ksh_layout_width">@dimen/match_parent</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
new file mode 100644
index 0000000..ba3e774
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Layout used as a container for keyboard shortcut keys. It's children are wrapped and right
+ * aligned.
+ */
+public final class KeyboardShortcutKeysLayout extends ViewGroup {
+    private int mLineHeight;
+
+    public KeyboardShortcutKeysLayout(Context context) {
+        super(context);
+    }
+
+    public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
+        int childCount = getChildCount();
+        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
+        int lineHeight = 0;
+        int xPos = getPaddingLeft();
+        int yPos = getPaddingTop();
+
+        int childHeightMeasureSpec;
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+        } else {
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
+                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                        childHeightMeasureSpec);
+                int childWidth = child.getMeasuredWidth();
+                lineHeight = Math.max(lineHeight,
+                        child.getMeasuredHeight() + layoutParams.mVerticalSpacing);
+
+                if (xPos + childWidth > width) {
+                    xPos = getPaddingLeft();
+                    yPos += lineHeight;
+                }
+                xPos += childWidth + layoutParams.mHorizontalSpacing;
+            }
+        }
+        this.mLineHeight = lineHeight;
+
+        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
+            height = yPos + lineHeight;
+        } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+            if (yPos + lineHeight < height) {
+                height = yPos + lineHeight;
+            }
+        }
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        int spacing = getHorizontalVerticalSpacing();
+        return new LayoutParams(spacing, spacing);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) {
+        int spacing = getHorizontalVerticalSpacing();
+        return new LayoutParams(spacing, spacing, layoutParams);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return (p instanceof LayoutParams);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int childCount = getChildCount();
+        int fullRowWidth = r - l;
+        int xPos = getPaddingLeft();
+        int yPos = getPaddingTop();
+        int lastHorizontalSpacing = 0;
+        // The index of the child which starts the current row.
+        int rowStartIdx = 0;
+
+        // Go through all the children.
+        for (int i = 0; i < childCount; i++) {
+            View currentChild = getChildAt(i);
+            if (currentChild.getVisibility() != GONE) {
+                int currentChildWidth = currentChild.getMeasuredWidth();
+                LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
+
+                // If the current child does not fit on this row.
+                if (xPos + currentChildWidth > fullRowWidth) {
+                    // Layout all the children on this row but the current one.
+                    layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos,
+                            lastHorizontalSpacing);
+                    // Update the positions for starting on the new row.
+                    xPos = getPaddingLeft();
+                    yPos += mLineHeight;
+                    rowStartIdx = i;
+                }
+
+                xPos += currentChildWidth + lp.mHorizontalSpacing;
+                lastHorizontalSpacing = lp.mHorizontalSpacing;
+            }
+        }
+
+        // Lay out the children on the last row.
+        if (rowStartIdx < childCount) {
+            layoutChildrenOnRow(rowStartIdx, childCount, fullRowWidth, xPos, yPos,
+                    lastHorizontalSpacing);
+        }
+    }
+
+    private int getHorizontalVerticalSpacing() {
+        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        return (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics);
+    }
+
+    private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos,
+            int yPos, int lastHorizontalSpacing) {
+        int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing;
+        xPos = getPaddingLeft() + freeSpace;
+
+        for (int j = startIndex; j < endIndex; ++j) {
+            View currentChild = getChildAt(j);
+            currentChild.layout(
+                    xPos,
+                    yPos,
+                    xPos + currentChild.getMeasuredWidth(),
+                    yPos + currentChild.getMeasuredHeight());
+            xPos += currentChild.getMeasuredWidth()
+                    + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing;
+        }
+    }
+
+    public static class LayoutParams extends ViewGroup.LayoutParams {
+        public final int mHorizontalSpacing;
+        public final int mVerticalSpacing;
+
+        public LayoutParams(int horizontalSpacing, int verticalSpacing,
+                ViewGroup.LayoutParams viewGroupLayout) {
+            super(viewGroupLayout);
+            this.mHorizontalSpacing = horizontalSpacing;
+            this.mVerticalSpacing = verticalSpacing;
+        }
+
+        public LayoutParams(int mHorizontalSpacing, int verticalSpacing) {
+            super(0, 0);
+            this.mHorizontalSpacing = mHorizontalSpacing;
+            this.mVerticalSpacing = verticalSpacing;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 0b7bfa8..60c2fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -29,6 +29,7 @@
 import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.widget.LinearLayout;
@@ -153,7 +154,7 @@
                         .findViewById(R.id.keyboard_shortcuts_keyword);
                 textView.setText(info.getLabel());
 
-                LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView
+                ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_item_container);
                 List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
                 final int shortcutKeysSize = shortcutKeys.size();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 933b402..9ce81ed2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -56,6 +56,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -174,6 +175,8 @@
 
     private final PackageManager mPackageManager;
 
+    private final PowerManager mPowerManager;
+
     private final WindowManagerInternal mWindowManagerService;
 
     private final SecurityPolicy mSecurityPolicy;
@@ -232,6 +235,7 @@
     public AccessibilityManagerService(Context context) {
         mContext = context;
         mPackageManager = mContext.getPackageManager();
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
@@ -1802,7 +1806,8 @@
     private KeyEventDispatcher getKeyEventDispatcher() {
         if (mKeyEventDispatcher == null) {
             mKeyEventDispatcher = new KeyEventDispatcher(
-                    mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock);
+                    mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
+                    mPowerManager);
         }
         return mKeyEventDispatcher;
     }
@@ -2703,6 +2708,11 @@
             final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
+                // Regardless of whether or not the action succeeds, it was generated by an
+                // accessibility service that is driven by user actions, so note user activity.
+                mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
+
                 connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
                         interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
@@ -2724,6 +2734,8 @@
             }
             final long identity = Binder.clearCallingIdentity();
             try {
+                mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
                 switch (action) {
                     case AccessibilityService.GLOBAL_ACTION_BACK: {
                         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
index 3469565..e03c16e 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
@@ -17,8 +17,10 @@
 package com.android.server.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Pools;
@@ -69,6 +71,7 @@
     private final Handler mHandlerToSendKeyEventsToInputFilter;
     private final int mMessageTypeForSendKeyEvent;
     private final Handler mKeyEventTimeoutHandler;
+    private final PowerManager mPowerManager;
 
     /**
      * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s
@@ -77,9 +80,12 @@
      * message that carries a {@code KeyEvent} to be sent to the input filter
      * @param lock The lock used for all synchronization in this package. This lock must be held
      * when calling {@code notifyKeyEventLocked}
+     * @param powerManager The power manager to alert to user activity if a KeyEvent is processed
+     * by a service
      */
     public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter,
-                              int messageTypeForSendKeyEvent, Object lock) {
+            int messageTypeForSendKeyEvent, Object lock,
+            PowerManager powerManager) {
         if (InputEventConsistencyVerifier.isInstrumentationEnabled()) {
             mSentEventsVerifier = new InputEventConsistencyVerifier(
                     this, 0, KeyEventDispatcher.class.getSimpleName());
@@ -91,6 +97,7 @@
         mKeyEventTimeoutHandler =
                 new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback());
         mLock = lock;
+        mPowerManager = powerManager;
     }
 
     /**
@@ -165,7 +172,16 @@
             PendingKeyEvent pendingEvent =
                     removeEventFromListLocked(mPendingEventsMap.get(service), sequence);
             if (pendingEvent != null) {
-                pendingEvent.handled |= handled;
+                if (handled && !pendingEvent.handled) {
+                    pendingEvent.handled = handled;
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        mPowerManager.userActivity(pendingEvent.event.getEventTime(),
+                                PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
                 removeReferenceToPendingEventLocked(pendingEvent);
             }
         }
@@ -241,7 +257,7 @@
             int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER;
             mHandlerToSendKeyEventsToInputFilter
                     .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event)
-                            .sendToTarget();
+                    .sendToTarget();
         } else {
             pendingEvent.event.recycle();
         }
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 7209814..fb1cda7 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -37,10 +37,7 @@
 import android.graphics.Bitmap;
 import android.os.Environment;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings.System;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -116,6 +113,7 @@
         // on file when we loaded them.
         if (mPersistedTaskIds.get(userId) == null) {
             mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
+            Slog.i(TAG, "Loaded persisted task ids for user " + userId);
         }
     }
 
@@ -146,6 +144,12 @@
             TaskRecord task = get(i);
             if (task.isPersistable && (task.stack == null || !task.stack.isHomeStack())) {
                 // Set of persisted taskIds for task.userId should not be null here
+                // TODO Investigate why it can happen. For now initialize with an empty set
+                if (mPersistedTaskIds.get(task.userId) == null) {
+                    Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
+                            + " mPersistedTaskIds=" + mPersistedTaskIds);
+                    mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
+                }
                 mPersistedTaskIds.get(task.userId).put(task.taskId, true);
             }
         }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 11fd3bc..ceb7db6 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -88,6 +88,7 @@
     private final ActivityStackSupervisor mStackSupervisor;
     private final RecentTasks mRecentTasks;
     private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>();
+    private final File mTaskIdsDir;
 
     /**
      * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
@@ -139,12 +140,22 @@
             }
         }
 
+        mTaskIdsDir = new File(Environment.getDataDirectory(), "system_de");
         mStackSupervisor = stackSupervisor;
         mService = service;
         mRecentTasks = recentTasks;
         mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
     }
 
+    @VisibleForTesting
+    TaskPersister(File workingDir) {
+        mTaskIdsDir = workingDir;
+        mStackSupervisor = null;
+        mService = null;
+        mRecentTasks = null;
+        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThreadTest");
+    }
+
     void startPersisting() {
         if (!mLazyTaskWriterThread.isAlive()) {
             mLazyTaskWriterThread.start();
@@ -207,8 +218,8 @@
         return persistedTaskIds.clone();
     }
 
-    private void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds,
-            int userId) {
+    @VisibleForTesting
+    void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
         if (userId < 0) {
             return;
         }
@@ -565,8 +576,12 @@
         return BitmapFactory.decodeFile(filename);
     }
 
-    static File getUserPersistedTaskIdsFile(int userId) {
-        return new File(Environment.getDataSystemDeDirectory(userId), PERSISTED_TASK_IDS_FILENAME);
+    private File getUserPersistedTaskIdsFile(int userId) {
+        File userTaskIdsDir = new File(mTaskIdsDir, String.valueOf(userId));
+        if (!userTaskIdsDir.exists() && !userTaskIdsDir.mkdirs()) {
+            Slog.e(TAG, "Error while creating user directory: " + userTaskIdsDir);
+        }
+        return new File(userTaskIdsDir, PERSISTED_TASK_IDS_FILENAME);
     }
 
     static File getUserTasksDir(int userId) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b4df689..f471af6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1384,8 +1384,13 @@
                         synchronized (mHdmiPlaybackClient) {
                             int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
                                     KeyEvent.KEYCODE_VOLUME_UP;
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
-                            mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                            final long ident = Binder.clearCallingIdentity();
+                            try {
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+                                mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                            } finally {
+                                Binder.restoreCallingIdentity(ident);
+                            }
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java
index 8195319..d62a0b3 100644
--- a/services/core/java/com/android/server/connectivity/ApfFilter.java
+++ b/services/core/java/com/android/server/connectivity/ApfFilter.java
@@ -90,7 +90,7 @@
     }
 
     private static final String TAG = "ApfFilter";
-    private static final boolean VDBG = true;
+    private static final boolean VDBG = false;
 
     private final ConnectivityService mConnectivityService;
     private final NetworkAgentInfo mNai;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 863a5ed..2f0532a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -550,7 +550,7 @@
             }
         }
 
-        void postToPackageMonitor(Runnable r) {
+        void postToPackageMonitorHandler(Runnable r) {
             mPackageMonitor.getRegisteredHandler().post(r);
         }
 
@@ -694,7 +694,7 @@
             @Override
             public void onShortcutChanged(@NonNull String packageName,
                     @UserIdInt int userId) {
-                postToPackageMonitor(() -> onShortcutChangedInner(packageName, userId));
+                postToPackageMonitorHandler(() -> onShortcutChangedInner(packageName, userId));
             }
 
             private void onShortcutChangedInner(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 39aa455..e831bb1 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -44,6 +44,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -96,20 +97,18 @@
 /**
  * TODO:
  *
- * - Detect when already registered instances are passed to APIs again, which might break
- *   internal bitmap handling.
+ * - Default launcher check does take a few ms.  Worth caching.
  *
  * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
  *   -> Need to scan all packages when a user starts too.
  *   -> Clear data -> remove all dynamic?  but not the pinned?
  *
- * - Pinned per each launcher package (multiple launchers)
- *
- * - Make save async (should we?)
- *
  * - Scan and remove orphan bitmaps (just in case).
  *
  * - Backup & restore
+ *
+ * - Detect when already registered instances are passed to APIs again, which might break
+ *   internal bitmap handling.
  */
 public class ShortcutService extends IShortcutService.Stub {
     static final String TAG = "ShortcutService";
@@ -138,7 +137,8 @@
     @VisibleForTesting
     static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
 
-    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
+    @VisibleForTesting
+    static final int DEFAULT_SAVE_DELAY_MS = 3000;
 
     @VisibleForTesting
     static final String FILENAME_BASE_STATE = "shortcut_service.xml";
@@ -151,36 +151,19 @@
 
     static final String DIRECTORY_BITMAPS = "bitmaps";
 
-    static final String TAG_ROOT = "root";
-    static final String TAG_USER = "user";
-    static final String TAG_PACKAGE = "package";
-    static final String TAG_LAST_RESET_TIME = "last_reset_time";
-    static final String TAG_INTENT_EXTRAS = "intent-extras";
-    static final String TAG_EXTRAS = "extras";
-    static final String TAG_SHORTCUT = "shortcut";
-    static final String TAG_LAUNCHER = "launcher";
-    static final String TAG_PIN = "pin";
-    static final String TAG_LAUNCHER_PINS = "launcher-pins";
+    private static final String TAG_ROOT = "root";
+    private static final String TAG_LAST_RESET_TIME = "last_reset_time";
 
-    static final String ATTR_VALUE = "value";
-    static final String ATTR_NAME = "name";
-    static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
-    static final String ATTR_CALL_COUNT = "call-count";
-    static final String ATTR_LAST_RESET = "last-reset";
-    static final String ATTR_ID = "id";
-    static final String ATTR_ACTIVITY = "activity";
-    static final String ATTR_TITLE = "title";
-    static final String ATTR_INTENT = "intent";
-    static final String ATTR_WEIGHT = "weight";
-    static final String ATTR_TIMESTAMP = "timestamp";
-    static final String ATTR_FLAGS = "flags";
-    static final String ATTR_ICON_RES = "icon-res";
-    static final String ATTR_BITMAP_PATH = "bitmap-path";
-    static final String ATTR_PACKAGE_NAME = "package-name";
+    private static final String ATTR_VALUE = "value";
 
     @VisibleForTesting
     interface ConfigConstants {
         /**
+         * Key name for the save delay, in milliseconds. (int)
+         */
+        String KEY_SAVE_DELAY_MILLIS = "save_delay_ms";
+
+        /**
          * Key name for the throttling reset interval, in seconds. (long)
          */
         String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
@@ -257,12 +240,22 @@
     private CompressFormat mIconPersistFormat;
     private int mIconPersistQuality;
 
+    private int mSaveDelayMillis;
+
     private final PackageManagerInternal mPackageManagerInternal;
 
+    @GuardedBy("mLock")
+    private List<Integer> mDirtyUserIds = new ArrayList<>();
+
     public ShortcutService(Context context) {
+        this(context, BackgroundThread.get().getLooper());
+    }
+
+    @VisibleForTesting
+    ShortcutService(Context context, Looper looper) {
         mContext = Preconditions.checkNotNull(context);
         LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
-        mHandler = new Handler(BackgroundThread.get().getLooper());
+        mHandler = new Handler(looper);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
     }
 
@@ -290,7 +283,7 @@
         @Override
         public void onCleanupUser(int userHandle) {
             synchronized (mService.mLock) {
-                mService.onCleanupUserInner(userHandle);
+                mService.onCleanupUserLocked(userHandle);
             }
         }
 
@@ -321,7 +314,10 @@
     }
 
     /** lifecycle event */
-    void onCleanupUserInner(int userId) {
+    void onCleanupUserLocked(int userId) {
+        // Save all dirty information.
+        saveDirtyInfo();
+
         // Unload
         mUsers.delete(userId);
     }
@@ -367,6 +363,9 @@
             result = false;
         }
 
+        mSaveDelayMillis = (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS,
+                DEFAULT_SAVE_DELAY_MS);
+
         mResetInterval = parser.getLong(
                 ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
                 * 1000L;
@@ -508,7 +507,7 @@
     void saveBaseStateLocked() {
         final AtomicFile file = getBaseStateFile();
         if (DEBUG) {
-            Slog.i(TAG, "Saving to " + file.getBaseFile());
+            Slog.d(TAG, "Saving to " + file.getBaseFile());
         }
 
         FileOutputStream outs = null;
@@ -541,7 +540,7 @@
 
         final AtomicFile file = getBaseStateFile();
         if (DEBUG) {
-            Slog.i(TAG, "Loading from " + file.getBaseFile());
+            Slog.d(TAG, "Loading from " + file.getBaseFile());
         }
         try (FileInputStream in = file.openRead()) {
             XmlPullParser parser = Xml.newPullParser();
@@ -586,7 +585,7 @@
     private void saveUserLocked(@UserIdInt int userId) {
         final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
         if (DEBUG) {
-            Slog.i(TAG, "Saving to " + path);
+            Slog.d(TAG, "Saving to " + path);
         }
         path.mkdirs();
         final AtomicFile file = new AtomicFile(path);
@@ -619,7 +618,7 @@
     private UserShortcuts loadUserLocked(@UserIdInt int userId) {
         final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
         if (DEBUG) {
-            Slog.i(TAG, "Loading from " + path);
+            Slog.d(TAG, "Loading from " + path);
         }
         final AtomicFile file = new AtomicFile(path);
 
@@ -628,7 +627,7 @@
             in = file.openRead();
         } catch (FileNotFoundException e) {
             if (DEBUG) {
-                Slog.i(TAG, "Not found " + path);
+                Slog.d(TAG, "Not found " + path);
             }
             return null;
         }
@@ -649,7 +648,7 @@
                     Slog.d(TAG, String.format("depth=%d type=%d name=%s",
                             depth, type, tag));
                 }
-                if ((depth == 1) && TAG_USER.equals(tag)) {
+                if ((depth == 1) && UserShortcuts.TAG_ROOT.equals(tag)) {
                     ret = UserShortcuts.loadFromXml(parser, userId);
                     continue;
                 }
@@ -664,29 +663,58 @@
         }
     }
 
-    // TODO Actually make it async.
     private void scheduleSaveBaseState() {
-        synchronized (mLock) {
-            saveBaseStateLocked();
-        }
+        scheduleSave(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
     }
 
-    // TODO Actually make it async.
     void scheduleSaveUser(@UserIdInt int userId) {
+        scheduleSave(userId);
+    }
+
+    // In order to re-schedule, we need to reuse the same instance, so keep it in final.
+    private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
+
+    private void scheduleSave(@UserIdInt int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "Scheduling to save for " + userId);
+        }
         synchronized (mLock) {
-            saveUserLocked(userId);
+            if (!mDirtyUserIds.contains(userId)) {
+                mDirtyUserIds.add(userId);
+            }
+        }
+        // If already scheduled, remove that and re-schedule in N seconds.
+        mHandler.removeCallbacks(mSaveDirtyInfoRunner);
+        mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis);
+    }
+
+    @VisibleForTesting
+    void saveDirtyInfo() {
+        if (DEBUG) {
+            Slog.d(TAG, "saveDirtyInfo");
+        }
+        synchronized (mLock) {
+            for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
+                final int userId = mDirtyUserIds.get(i);
+                if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
+                    saveBaseStateLocked();
+                } else {
+                    saveUserLocked(userId);
+                }
+            }
+            mDirtyUserIds.clear();
         }
     }
 
     /** Return the last reset time. */
     long getLastResetTimeLocked() {
-        updateTimes();
+        updateTimesLocked();
         return mRawLastResetTime;
     }
 
     /** Return the next reset time. */
     long getNextResetTimeLocked() {
-        updateTimes();
+        updateTimesLocked();
         return mRawLastResetTime + mResetInterval;
     }
 
@@ -697,7 +725,7 @@
     /**
      * Update the last reset time.
      */
-    private void updateTimes() {
+    private void updateTimesLocked() {
 
         final long now = injectCurrentTimeMillis();
 
@@ -709,13 +737,14 @@
         } else if (now < mRawLastResetTime) {
             // Clock rewound.
             if (isClockValid(now)) {
+                Slog.w(TAG, "Clock rewound");
                 // TODO Randomize??
                 mRawLastResetTime = now;
             }
         } else {
-            // TODO Do it properly.
-            while ((mRawLastResetTime + mResetInterval) <= now) {
-                mRawLastResetTime += mResetInterval;
+            if ((mRawLastResetTime + mResetInterval) <= now) {
+                final long offset = mRawLastResetTime % mResetInterval;
+                mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset;
             }
         }
         if (prevLastResetTime != mRawLastResetTime) {
@@ -1895,6 +1924,11 @@
 class UserShortcuts {
     private static final String TAG = ShortcutService.TAG;
 
+    static final String TAG_ROOT = "user";
+    private static final String TAG_LAUNCHER = "launcher";
+
+    private static final String ATTR_VALUE = "value";
+
     @UserIdInt
     final int mUserId;
 
@@ -1935,9 +1969,9 @@
     }
 
     public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        out.startTag(null, ShortcutService.TAG_USER);
+        out.startTag(null, TAG_ROOT);
 
-        ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
+        ShortcutService.writeTagValue(out, TAG_LAUNCHER,
                 mLauncherComponent);
 
         final int lsize = mLaunchers.size();
@@ -1950,7 +1984,7 @@
             mPackages.valueAt(i).saveToXml(out);
         }
 
-        out.endTag(null, ShortcutService.TAG_USER);
+        out.endTag(null, TAG_ROOT);
     }
 
     public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
@@ -1967,12 +2001,12 @@
             final int depth = parser.getDepth();
             final String tag = parser.getName();
             switch (tag) {
-                case ShortcutService.TAG_LAUNCHER: {
+                case TAG_LAUNCHER: {
                     ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
-                            parser, ShortcutService.ATTR_VALUE);
+                            parser, ATTR_VALUE);
                     continue;
                 }
-                case ShortcutService.TAG_PACKAGE: {
+                case PackageShortcuts.TAG_ROOT: {
                     final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
 
                     // Don't use addShortcut(), we don't need to save the icon.
@@ -1980,7 +2014,7 @@
                     continue;
                 }
 
-                case ShortcutService.TAG_LAUNCHER_PINS: {
+                case LauncherShortcuts.TAG_ROOT: {
                     final LauncherShortcuts shortcuts =
                             LauncherShortcuts.loadFromXml(parser, userId);
 
@@ -2036,6 +2070,14 @@
 class LauncherShortcuts {
     private static final String TAG = ShortcutService.TAG;
 
+    static final String TAG_ROOT = "launcher-pins";
+
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_PIN = "pin";
+
+    private static final String ATTR_VALUE = "value";
+    private static final String ATTR_PACKAGE_NAME = "package-name";
+
     @UserIdInt
     final int mUserId;
 
@@ -2093,25 +2135,25 @@
      * Persist.
      */
     public void saveToXml(XmlSerializer out) throws IOException {
-        out.startTag(null, ShortcutService.TAG_LAUNCHER_PINS);
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME,
+        out.startTag(null, TAG_ROOT);
+        ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
                 mPackageName);
 
         final int size = mPinnedShortcuts.size();
         for (int i = 0; i < size; i++) {
-            out.startTag(null, ShortcutService.TAG_PACKAGE);
-            ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME,
+            out.startTag(null, TAG_PACKAGE);
+            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
                     mPinnedShortcuts.keyAt(i));
 
             final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
             final int idSize = ids.size();
             for (int j = 0; j < idSize; j++) {
-                ShortcutService.writeTagValue(out, ShortcutService.TAG_PIN, ids.valueAt(j));
+                ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
             }
-            out.endTag(null, ShortcutService.TAG_PACKAGE);
+            out.endTag(null, TAG_PACKAGE);
         }
 
-        out.endTag(null, ShortcutService.TAG_LAUNCHER_PINS);
+        out.endTag(null, TAG_ROOT);
     }
 
     /**
@@ -2120,7 +2162,7 @@
     public static LauncherShortcuts loadFromXml(XmlPullParser parser, int userId)
             throws IOException, XmlPullParserException {
         final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
-                ShortcutService.ATTR_PACKAGE_NAME);
+                ATTR_PACKAGE_NAME);
 
         final LauncherShortcuts ret = new LauncherShortcuts(userId, launcherPackageName);
 
@@ -2135,16 +2177,16 @@
             final int depth = parser.getDepth();
             final String tag = parser.getName();
             switch (tag) {
-                case ShortcutService.TAG_PACKAGE: {
+                case TAG_PACKAGE: {
                     final String packageName = ShortcutService.parseStringAttribute(parser,
-                            ShortcutService.ATTR_PACKAGE_NAME);
+                            ATTR_PACKAGE_NAME);
                     ids = new ArraySet<>();
                     ret.mPinnedShortcuts.put(packageName, ids);
                     continue;
                 }
-                case ShortcutService.TAG_PIN: {
+                case TAG_PIN: {
                     ids.add(ShortcutService.parseStringAttribute(parser,
-                            ShortcutService.ATTR_VALUE));
+                            ATTR_VALUE));
                     continue;
                 }
             }
@@ -2189,6 +2231,25 @@
 class PackageShortcuts {
     private static final String TAG = ShortcutService.TAG;
 
+    static final String TAG_ROOT = "package";
+    private static final String TAG_INTENT_EXTRAS = "intent-extras";
+    private static final String TAG_EXTRAS = "extras";
+    private static final String TAG_SHORTCUT = "shortcut";
+
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
+    private static final String ATTR_CALL_COUNT = "call-count";
+    private static final String ATTR_LAST_RESET = "last-reset";
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_ACTIVITY = "activity";
+    private static final String ATTR_TITLE = "title";
+    private static final String ATTR_INTENT = "intent";
+    private static final String ATTR_WEIGHT = "weight";
+    private static final String ATTR_TIMESTAMP = "timestamp";
+    private static final String ATTR_FLAGS = "flags";
+    private static final String ATTR_ICON_RES = "icon-res";
+    private static final String ATTR_BITMAP_PATH = "bitmap-path";
+
     @UserIdInt
     final int mUserId;
 
@@ -2377,12 +2438,19 @@
 
         final long now = s.injectCurrentTimeMillis();
         if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
-            // Clock rewound. // TODO Test it
+            Slog.w(TAG, "Clock rewound");
+            // Clock rewound.
             mLastResetTime = now;
+            mApiCallCount = 0;
+            return mApiCallCount;
         }
 
         // If not reset yet, then reset.
         if (mLastResetTime < last) {
+            if (ShortcutService.DEBUG) {
+                Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting",
+                        mLastResetTime, now, last));
+            }
             mApiCallCount = 0;
             mLastResetTime = last;
         }
@@ -2501,58 +2569,58 @@
     }
 
     public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
-        out.startTag(null, ShortcutService.TAG_PACKAGE);
+        out.startTag(null, TAG_ROOT);
 
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
+        ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
+        ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
+        ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
+        ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
 
         final int size = mShortcuts.size();
         for (int j = 0; j < size; j++) {
             saveShortcut(out, mShortcuts.valueAt(j));
         }
 
-        out.endTag(null, ShortcutService.TAG_PACKAGE);
+        out.endTag(null, TAG_ROOT);
     }
 
     private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
             throws IOException, XmlPullParserException {
-        out.startTag(null, ShortcutService.TAG_SHORTCUT);
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
+        out.startTag(null, TAG_SHORTCUT);
+        ShortcutService.writeAttr(out, ATTR_ID, si.getId());
         // writeAttr(out, "package", si.getPackageName()); // not needed
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
+        ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
         // writeAttr(out, "icon", si.getIcon());  // We don't save it.
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
+        ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
+        ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
+        ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
+        ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
                 si.getLastChangedTimestamp());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
-        ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
+        ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
+        ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
+        ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
 
-        ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
+        ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
                 si.getIntentPersistableExtras());
-        ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
+        ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
 
-        out.endTag(null, ShortcutService.TAG_SHORTCUT);
+        out.endTag(null, TAG_SHORTCUT);
     }
 
     public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
             throws IOException, XmlPullParserException {
 
         final String packageName = ShortcutService.parseStringAttribute(parser,
-                ShortcutService.ATTR_NAME);
+                ATTR_NAME);
 
         final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
 
         ret.mDynamicShortcutCount =
-                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
+                ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
         ret.mApiCallCount =
-                ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
+                ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
         ret.mLastResetTime =
-                ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
+                ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
         final int outerDepth = parser.getDepth();
         int type;
@@ -2564,7 +2632,7 @@
             final int depth = parser.getDepth();
             final String tag = parser.getName();
             switch (tag) {
-                case ShortcutService.TAG_SHORTCUT:
+                case TAG_SHORTCUT:
                     final ShortcutInfo si = parseShortcut(parser, packageName);
 
                     // Don't use addShortcut(), we don't need to save the icon.
@@ -2591,17 +2659,17 @@
         int iconRes;
         String bitmapPath;
 
-        id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
+        id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
         activityComponent = ShortcutService.parseComponentNameAttribute(parser,
-                ShortcutService.ATTR_ACTIVITY);
-        title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
-        intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
-        weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
+                ATTR_ACTIVITY);
+        title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
+        intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
+        weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
         lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
-                ShortcutService.ATTR_TIMESTAMP);
-        flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
-        iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
-        bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
+                ATTR_TIMESTAMP);
+        flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
+        iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
+        bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
 
         final int outerDepth = parser.getDepth();
         int type;
@@ -2617,10 +2685,10 @@
                         depth, type, tag));
             }
             switch (tag) {
-                case ShortcutService.TAG_INTENT_EXTRAS:
+                case TAG_INTENT_EXTRAS:
                     intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
                     continue;
-                case ShortcutService.TAG_EXTRAS:
+                case TAG_EXTRAS:
                     extras = PersistableBundle.restoreFromXml(parser);
                     continue;
             }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 2db6b5d..50699f8 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -35,14 +35,14 @@
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Settings;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
+import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
-import android.webkit.WebViewFactory;
 
 import com.android.server.SystemService;
 
@@ -66,8 +66,6 @@
     private int mNumRelroCreationsFinished = 0;
     // Implies that we need to rerun relro creation because we are using an out-of-date package
     private boolean mWebViewPackageDirty = false;
-    // Set to true when the current provider is being replaced
-    private boolean mCurrentProviderBeingReplaced = false;
     private boolean mAnyWebViewInstalled = false;
 
     private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
@@ -78,9 +76,11 @@
     private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
+    private WebViewUtilityInterface mWebViewUtility;
 
     public WebViewUpdateService(Context context) {
         super(context);
+        mWebViewUtility = new WebViewUtilityImpl();
     }
 
     @Override
@@ -92,19 +92,10 @@
                     // the removal of the old package and one representing the addition of the
                     // new package.
                     // In the case where we receive an intent to remove the old version of the
-                    // package that is being replaced we set a flag here and early-out so that we
-                    // don't change provider while replacing the current package (we will instead
-                    // change provider when the new version of the package is being installed).
+                    // package that is being replaced we early-out here so that we don't run the
+                    // update-logic twice.
                     if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
                         && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
-                        synchronized(WebViewUpdateService.this) {
-                            if (mCurrentWebViewPackage == null) return;
-
-                            String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
-                            if (webViewPackage.equals(intent.getDataString()))
-                                mCurrentProviderBeingReplaced = true;
-                        }
-
                         return;
                     }
 
@@ -125,7 +116,7 @@
 
                     updateFallbackState(context, intent);
 
-                    for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
+                    for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
                         String webviewPackage = "package:" + provider.packageName;
 
                         if (webviewPackage.equals(intent.getDataString())) {
@@ -164,11 +155,7 @@
                                 // package that was not the previous provider then we must kill
                                 // packages dependent on the old package ourselves. The framework
                                 // only kills dependents of packages that are being removed.
-                                try {
-                                    ActivityManagerNative.getDefault().killPackageDependents(
-                                        oldProviderName, UserHandle.USER_ALL);
-                                } catch (RemoteException e) {
-                                }
+                                mWebViewUtility.killPackageDependents(oldProviderName);
                             }
                             return;
                         }
@@ -181,7 +168,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         // Make sure we only receive intents for WebView packages from our config file.
-        for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
+        for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
             filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
         }
         getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
@@ -221,7 +208,7 @@
     void handleNewUser(int userId) {
         if (!isFallbackLogicEnabled()) return;
 
-        WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
         if (fallbackProvider == null) return;
         boolean existsValidNonFallbackProvider =
@@ -239,7 +226,7 @@
     void updateFallbackState(final Context context, final Intent intent) {
         if (!isFallbackLogicEnabled()) return;
 
-        WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
 
         if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
                     || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
@@ -330,10 +317,10 @@
         return false;
     }
 
-    private static boolean isFallbackPackage(String packageName) {
+    private boolean isFallbackPackage(String packageName) {
         if (packageName == null || !isFallbackLogicEnabled()) return false;
 
-        WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages();
+        WebViewProviderInfo[] webviewPackages = mWebViewUtility.getWebViewPackages();
         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
         return (fallbackProvider != null
                 && packageName.equals(fallbackProvider.packageName));
@@ -370,13 +357,13 @@
         PackageInfo newPackage = null;
         synchronized(this) {
             oldPackage = mCurrentWebViewPackage;
-            updateUserSetting(newProviderName);
+            mWebViewUtility.updateUserSetting(getContext(), newProviderName);
 
             try {
                 newPackage = findPreferredWebViewPackage();
                 if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
                     // If we don't perform the user change, revert the settings change.
-                    updateUserSetting(newPackage.packageName);
+                    mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
                     return newPackage.packageName;
                 }
             } catch (WebViewFactory.MissingWebViewPackageException e) {
@@ -389,12 +376,8 @@
             onWebViewProviderChanged(newPackage);
         }
         // Kill apps using the old provider
-        try {
-            if (oldPackage != null) {
-                ActivityManagerNative.getDefault().killPackageDependents(
-                        oldPackage.packageName, UserHandle.USER_ALL);
-            }
-        } catch (RemoteException e) {
+        if (oldPackage != null) {
+            mWebViewUtility.killPackageDependents(oldPackage.packageName);
         }
         return newPackage.packageName;
     }
@@ -406,19 +389,16 @@
     private void onWebViewProviderChanged(PackageInfo newPackage) {
         synchronized(this) {
             mAnyWebViewInstalled = true;
-            // If we have changed provider then the replacement of the old provider is
-            // irrelevant - we can only have chosen a new provider if its package is available.
-            mCurrentProviderBeingReplaced = false;
             if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
                 mCurrentWebViewPackage = newPackage;
-                updateUserSetting(newPackage.packageName);
+                mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
 
                 // The relro creations might 'finish' (not start at all) before
                 // WebViewFactory.onWebViewProviderChanged which means we might not know the number
                 // of started creations before they finish.
                 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
                 mNumRelroCreationsFinished = 0;
-                mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
+                mNumRelroCreationsStarted = mWebViewUtility.onWebViewProviderChanged(newPackage);
                 // If the relro creations finish before we know the number of started creations we
                 // will have to do any cleanup/notifying here.
                 checkIfRelrosDoneLocked();
@@ -435,7 +415,7 @@
      * */
     private void updateValidWebViewPackages() {
         List<WebViewProviderInfo> webViewProviders  =
-            new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
+            new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
         Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
         // remove non-valid packages
         while(it.hasNext()) {
@@ -449,17 +429,6 @@
         }
     }
 
-    private static String getUserChosenWebViewProvider() {
-        return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
-                Settings.Global.WEBVIEW_PROVIDER);
-    }
-
-    private void updateUserSetting(String newProviderName) {
-        Settings.Global.putString(getContext().getContentResolver(),
-                Settings.Global.WEBVIEW_PROVIDER,
-                newProviderName == null ? "" : newProviderName);
-    }
-
     /**
      * Returns either the package info of the WebView provider determined in the following way:
      * If the user has chosen a provider then use that if it is valid,
@@ -470,7 +439,7 @@
     private PackageInfo findPreferredWebViewPackage() {
         WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
 
-        String userChosenProvider = getUserChosenWebViewProvider();
+        String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());
 
         // If the user has chosen provider, use that
         for (WebViewProviderInfo provider : providers) {
@@ -504,7 +473,6 @@
     private boolean webViewIsReadyLocked() {
         return !mWebViewPackageDirty
             && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
-            && !mCurrentProviderBeingReplaced
             // The current package might be replaced though we haven't received an intent declaring
             // this yet, the following flag makes anyone loading WebView to wait in this case.
             && mAnyWebViewInstalled;
@@ -516,13 +484,8 @@
                 mWebViewPackageDirty = false;
                 // If we have changed provider since we started the relro creation we need to
                 // redo the whole process using the new package instead.
-                // Though, if the current provider package is being replaced we don't want to change
-                // provider here since we will perform the change either when the package is added
-                // again or when we switch to another provider (whichever comes first).
-                if (!mCurrentProviderBeingReplaced) {
-                    PackageInfo newPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(newPackage);
-                }
+                PackageInfo newPackage = findPreferredWebViewPackage();
+                onWebViewProviderChanged(newPackage);
             } else {
                 this.notifyAll();
             }
@@ -597,11 +560,6 @@
                 // Make sure we return the provider that was used to create the relro file
                 webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
                 if (webViewReady) {
-                } else if (mCurrentProviderBeingReplaced) {
-                    // It is important that we check this flag before the one representing WebView
-                    // being installed, otherwise we might think there is no WebView though the
-                    // current one is just being replaced.
-                    webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED;
                 } else if (!mAnyWebViewInstalled) {
                     webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
                 } else {
@@ -641,6 +599,11 @@
         }
 
         @Override // Binder call
+        public WebViewProviderInfo[] getAllWebViewPackages() {
+            return WebViewUpdateService.this.mWebViewUtility.getWebViewPackages();
+        }
+
+        @Override // Binder call
         public String getCurrentWebViewPackageName() {
             synchronized(WebViewUpdateService.this) {
                 if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
@@ -651,7 +614,7 @@
 
         @Override // Binder call
         public boolean isFallbackPackage(String packageName) {
-            return WebViewUpdateService.isFallbackPackage(packageName);
+            return WebViewUpdateService.this.isFallbackPackage(packageName);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
new file mode 100644
index 0000000..4dbd02d
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactory.MissingWebViewPackageException;
+import android.webkit.WebViewProviderInfo;
+
+import com.android.internal.util.XmlUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Default implementation for the WebView preparation Utility interface.
+ * @hide
+ */
+public class WebViewUtilityImpl implements WebViewUtilityInterface {
+    private static final String TAG = WebViewUtilityImpl.class.getSimpleName();
+    private static final String TAG_START = "webviewproviders";
+    private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
+    private static final String TAG_PACKAGE_NAME = "packageName";
+    private static final String TAG_DESCRIPTION = "description";
+    // Whether or not the provider must be explicitly chosen by the user to be used.
+    private static final String TAG_AVAILABILITY = "availableByDefault";
+    private static final String TAG_SIGNATURE = "signature";
+    private static final String TAG_FALLBACK = "isFallback";
+
+    /**
+     * Returns all packages declared in the framework resources as potential WebView providers.
+     * @hide
+     * */
+    @Override
+    public WebViewProviderInfo[] getWebViewPackages() {
+        int numFallbackPackages = 0;
+        XmlResourceParser parser = null;
+        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
+        try {
+            parser = AppGlobals.getInitialApplication().getResources().getXml(
+                    com.android.internal.R.xml.config_webview_packages);
+            XmlUtils.beginDocument(parser, TAG_START);
+            while(true) {
+                XmlUtils.nextElement(parser);
+                String element = parser.getName();
+                if (element == null) {
+                    break;
+                }
+                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
+                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
+                    if (packageName == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing package name");
+                    }
+                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
+                    if (description == null) {
+                        throw new MissingWebViewPackageException(
+                                "WebView provider in framework resources missing description");
+                    }
+                    boolean availableByDefault = "true".equals(
+                            parser.getAttributeValue(null, TAG_AVAILABILITY));
+                    boolean isFallback = "true".equals(
+                            parser.getAttributeValue(null, TAG_FALLBACK));
+                    WebViewProviderInfo currentProvider =
+                            new WebViewProviderInfo(packageName, description, availableByDefault,
+                                isFallback, readSignatures(parser));
+                    if (currentProvider.isFallbackPackage()) {
+                        numFallbackPackages++;
+                        if (numFallbackPackages > 1) {
+                            throw new AndroidRuntimeException(
+                                    "There can be at most one webview fallback package.");
+                        }
+                    }
+                    webViewProviders.add(currentProvider);
+                }
+                else {
+                    Log.e(TAG, "Found an element that is not a webview provider");
+                }
+            }
+        } catch(XmlPullParserException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } catch(IOException e) {
+            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+    }
+
+    /**
+     * Reads all signatures at the current depth (within the current provider) from the XML parser.
+     */
+    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
+            XmlPullParserException {
+        List<String> signatures = new ArrayList<String>();
+        int outerDepth = parser.getDepth();
+        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if (parser.getName().equals(TAG_SIGNATURE)) {
+                // Parse the value within the signature tag
+                String signature = parser.nextText();
+                signatures.add(signature);
+            } else {
+                Log.e(TAG, "Found an element in a webview provider that is not a signature");
+            }
+        }
+        return signatures.toArray(new String[signatures.size()]);
+    }
+
+    @Override
+    public int onWebViewProviderChanged(PackageInfo packageInfo) {
+        return WebViewFactory.onWebViewProviderChanged(packageInfo);
+    }
+
+    @Override
+    public String getUserChosenWebViewProvider(Context context) {
+        return Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER);
+    }
+
+    @Override
+    public void updateUserSetting(Context context, String newProviderName) {
+        Settings.Global.putString(context.getContentResolver(),
+                Settings.Global.WEBVIEW_PROVIDER,
+                newProviderName == null ? "" : newProviderName);
+    }
+
+    @Override
+    public void killPackageDependents(String packageName) {
+        try {
+            ActivityManagerNative.getDefault().killPackageDependents(packageName,
+                    UserHandle.USER_ALL);
+        } catch (RemoteException e) {
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
new file mode 100644
index 0000000..1919f40
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.webkit.WebViewProviderInfo;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+
+/**
+ * Utility interface for the WebViewUpdateService.
+ * This interface provides a way to test the WebView preparation mechanism - during normal use this
+ * interface is implemented using calls to the Android framework, but by providing an alternative
+ * implementation we can test the WebView preparation logic without reaching other framework code.
+ * @hide
+ */
+public interface WebViewUtilityInterface {
+    public WebViewProviderInfo[] getWebViewPackages();
+    public int onWebViewProviderChanged(PackageInfo packageInfo);
+
+    public String getUserChosenWebViewProvider(Context context);
+    public void updateUserSetting(Context context, String newProviderName);
+    public void killPackageDependents(String packageName);
+}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f2f555b..8ab1878 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -443,10 +443,7 @@
             @Nullable List<PrinterId> printerIds) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
-            // No services - nothing to do.
-            if (mActiveServices.isEmpty()) {
-                return;
-            }
+
             // No session - nothing to do.
             if (mPrinterDiscoverySession == null) {
                 return;
@@ -460,10 +457,7 @@
     public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
-            // No services - nothing to do.
-            if (mActiveServices.isEmpty()) {
-                return;
-            }
+
             // No session - nothing to do.
             if (mPrinterDiscoverySession == null) {
                 return;
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
new file mode 100644
index 0000000..e440a0d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+
+import com.android.server.am.TaskPersister;
+
+import java.io.File;
+import java.util.Random;
+
+public class TaskPersisterTest extends AndroidTestCase {
+    private static final String TEST_USER_NAME = "AM-Test-User";
+
+    private TaskPersister mTaskPersister;
+    private int testUserId;
+    private UserManager mUserManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mUserManager = UserManager.get(getContext());
+        mTaskPersister = new TaskPersister(getContext().getFilesDir());
+        testUserId = createUser(TEST_USER_NAME, 0);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        mTaskPersister.unloadUserDataFromMemory(testUserId);
+        removeUser(testUserId);
+    }
+
+    private int getRandomTaskIdForUser(int userId) {
+        int taskId = (int) (Math.random() * UserHandle.PER_USER_RANGE);
+        taskId += UserHandle.PER_USER_RANGE * userId;
+        return taskId;
+    }
+
+    public void testTaskIdsPersistence() {
+        SparseBooleanArray taskIdsOnFile = mTaskPersister.loadPersistedTaskIdsForUser(testUserId);
+        for (int i = 0; i < 100; i++) {
+            taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true);
+        }
+        mTaskPersister.maybeWritePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
+        SparseBooleanArray newTaskIdsOnFile = mTaskPersister
+                .loadPersistedTaskIdsForUser(testUserId);
+        assertTrue("TaskIds written differ from TaskIds read back from file",
+                taskIdsOnFile.equals(newTaskIdsOnFile));
+    }
+
+    private int createUser(String name, int flags) {
+        UserInfo user = mUserManager.createUser(name, flags);
+        if (user == null) {
+            fail("Error while creating the test user: " + TEST_USER_NAME);
+        }
+        return user.id;
+    }
+
+    private void removeUser(int userId) {
+        if (!mUserManager.removeUser(userId)) {
+            fail("Error while removing the test user: " + TEST_USER_NAME);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 5c51139..ad86fd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -166,8 +166,8 @@
     private final class ShortcutServiceTestable extends ShortcutService {
         final ServiceContext mContext;
 
-        public ShortcutServiceTestable(ServiceContext context) {
-            super(context);
+        public ShortcutServiceTestable(ServiceContext context, Looper looper) {
+            super(context, looper);
             mContext = context;
         }
 
@@ -248,7 +248,12 @@
         @Override
         void postToHandler(Runnable r) {
             final long token = mContext.injectClearCallingIdentity();
-            r.run();
+            super.postToHandler(r);
+            try {
+                runTestOnUiThread(() -> {});
+            } catch (Throwable e) {
+                fail("runTestOnUiThread failed: " + e);
+            }
             mContext.injectRestoreCallingIdentity(token);
         }
 
@@ -302,7 +307,7 @@
         }
 
         @Override
-        void postToPackageMonitor(Runnable r) {
+        void postToPackageMonitorHandler(Runnable r) {
             final long token = mContext.injectClearCallingIdentity();
             r.run();
             mContext.injectRestoreCallingIdentity(token);
@@ -368,7 +373,7 @@
     private static final int USER_10 = 10;
     private static final int USER_11 = 11;
 
-    private static final long START_TIME = 1234560000000L;
+    private static final long START_TIME = 1440000000101L;
 
     private static final long INTERVAL = 10000;
 
@@ -424,7 +429,7 @@
         LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
 
         // Instantiate targets.
-        mService = new ShortcutServiceTestable(mServiceContext);
+        mService = new ShortcutServiceTestable(mServiceContext, Looper.getMainLooper());
         mManager = new ShortcutManagerTestable(mClientContext, mService);
 
         mInternal = LocalServices.getService(ShortcutServiceInternal.class);
@@ -509,6 +514,7 @@
      * For debugging, dump the main state file on logcat.
      */
     private void dumpBaseStateFile() {
+        mService.saveDirtyInfo();
         dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
                 + "/system/" + ShortcutService.FILENAME_BASE_STATE);
     }
@@ -517,6 +523,7 @@
      * For debugging, dump per-user state file on logcat.
      */
     private void dumpUserFile(int userId) {
+        mService.saveDirtyInfo();
         dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
                 + "/user-" + userId
                 + "/" + ShortcutService.FILENAME_USER_PACKAGES);
@@ -890,6 +897,8 @@
 
         dumpBaseStateFile();
 
+        mService.saveDirtyInfo();
+
         // Restore.
         initService();
 
@@ -1125,51 +1134,102 @@
 
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeLillis++;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(1, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeLillis++;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         // Reached the max
 
         mInjectedCurrentTimeLillis++;
         assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         // Still throttled
         mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
         assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         // Now it should work.
         mInjectedCurrentTimeLillis++;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
         assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeLillis++;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(1, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeLillis++;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeLillis++;
         assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
 
         // 4 days later...
         mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
         assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
         assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
 
-        // Make sure getRemainingCallCount() itself gets reset withou calling setDynamicShortcuts().
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(1, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
+
+        // Make sure getRemainingCallCount() itself gets reset without calling setDynamicShortcuts().
         mInjectedCurrentTimeLillis = START_TIME + 8 * INTERVAL;
         assertEquals(3, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
+    }
+
+    public void testThrottling_rewind() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+        mInjectedCurrentTimeLillis = 12345; // Clock reset!
+
+        // Since the clock looks invalid, the counter shouldn't have reset.
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+        // Forward again.  Still haven't reset yet.
+        mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+        // Now rewind -- this will reset the counters.
+        mInjectedCurrentTimeLillis = START_TIME - 100000;
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // Forward again, should be reset now.
+        mInjectedCurrentTimeLillis += INTERVAL;
+        assertEquals(3, mManager.getRemainingCallCount());
     }
 
     public void testThrottling_perPackage() {
@@ -1291,6 +1351,7 @@
                 "none");
 
         // Re-initialize and load from the files.
+        mService.saveDirtyInfo();
         initService();
 
         // Load from launcher.
@@ -1943,6 +2004,7 @@
         });
 
         // Re-initialize and load from the files.
+        mService.saveDirtyInfo();
         initService();
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
@@ -2270,6 +2332,7 @@
         dumpUserFile(0);
 
         // Restore.
+        mService.saveDirtyInfo();
         initService();
 
         assertEquals(0, mManager.getDynamicShortcuts().size());
@@ -2366,6 +2429,7 @@
                 mService, new ComponentName("pkg1", "class"));
 
         // Restore.
+        mService.saveDirtyInfo();
         initService();
 
         // Before the load, the map should be empty.
@@ -2414,7 +2478,7 @@
         assertNull(mService.getShortcutsForTest().get(USER_10).getLauncherComponent());
 
         // Try stopping the user
-        mService.onCleanupUserInner(USER_10);
+        mService.onCleanupUserLocked(USER_10);
 
         // Now it's unloaded.
         assertEquals(1, mService.getShortcutsForTest().size());
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 2ab0525..a965342 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -44,6 +44,7 @@
     public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
 
     private static MessageDigest sMessageDigest;
+    private static final Object sMessageDigestLock = new Object();
 
     private Log() {}
 
@@ -57,7 +58,9 @@
                 } catch (NoSuchAlgorithmException e) {
                     md = null;
                 }
-                sMessageDigest = md;
+                synchronized (sMessageDigestLock) {
+                    sMessageDigest = md;
+                }
                 return null;
             }
         }.execute();
@@ -187,13 +190,15 @@
     }
 
     private static String secureHash(byte[] input) {
-        if (sMessageDigest != null) {
-            sMessageDigest.reset();
-            sMessageDigest.update(input);
-            byte[] result = sMessageDigest.digest();
-            return encodeHex(result);
-        } else {
-            return "Uninitialized SHA1";
+        synchronized (sMessageDigestLock) {
+            if (sMessageDigest != null) {
+                sMessageDigest.reset();
+                sMessageDigest.update(input);
+                byte[] result = sMessageDigest.digest();
+                return encodeHex(result);
+            } else {
+                return "Uninitialized SHA1";
+            }
         }
     }
 
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index fb172d4..c16de7b 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -164,6 +164,7 @@
 
 def check_emoji_defaults():
     default_emoji_chars = _emoji_properties['Emoji_Presentation']
+    missing_text_chars = _emoji_properties['Emoji'] - default_emoji_chars
     emoji_font_seen = False
     for name, scripts, variant, weight, style, font in _fallback_chain:
         if 'Zsye' in scripts:
@@ -176,32 +177,39 @@
         if emoji_font_seen and not scripts:
             continue
 
-        if font[1] is None:
-            emoji_to_skip = set()
-        else:
-            # CJK font, skip checking the following characters for now.
-            # See b/26153752
-            emoji_to_skip = ({
-                0x26BD, # SOCCER BALL
-                0x26BE, # BASEBALL
-                0x1F18E, # NEGATIVE SQUARED AB
-                0x1F201, # SQUARED KATAKANA KOKO
-                0x1F21A, # SQUARED CJK UNIFIED IDEOGRAPH-7121
-                0x1F22F, # SQUARED CJK UNIFIED IDEOGRAPH-6307
-            } | set(xrange(0x1F191, 0x1F19A+1))
-              | set(xrange(0x1F232, 0x1F236+1))
-              | set(xrange(0x1F238, 0x1F23A+1))
-              | set(xrange(0x1F250, 0x1F251+1)))
+        # Check default emoji-style characters
+        assert_font_supports_none_of_chars(font, sorted(default_emoji_chars))
 
-        assert_font_supports_none_of_chars(font,
-            sorted(default_emoji_chars - emoji_to_skip))
+        # Mark default text-style characters appearing in fonts above the emoji
+        # font as seen
+        if not emoji_font_seen:
+            missing_text_chars -= set(get_best_cmap(font))
+
+    # Noto does not have monochrome symbols for Unicode 7.0 wingdings and
+    # webdings
+    missing_text_chars -= _chars_by_age['7.0']
+    # TODO: Remove these after b/26113320 is fixed
+    missing_text_chars -= {
+        0x263A, # WHITE SMILING FACE
+        0x270C, # VICTORY HAND
+        0x2744, # SNOWFLAKE
+        0x2764, # HEAVY BLACK HEART
+    }
+    assert missing_text_chars == set(), (
+        'Text style version of some emoji characters are missing.')
 
 
-def parse_ucd(ucd_path):
-    global _emoji_properties
-    _emoji_properties = collections.defaultdict(set)
-    with open(path.join(ucd_path, 'emoji-data.txt')) as emoji_data_txt:
-        for line in emoji_data_txt:
+# Setting reverse to true returns a dictionary that maps the values to sets of
+# characters, useful for some binary properties. Otherwise, we get a
+# dictionary that maps characters to the property values, assuming there's only
+# one property in the file.
+def parse_unicode_datafile(file_path, reverse=False):
+    if reverse:
+        output_dict = collections.defaultdict(set)
+    else:
+        output_dict = {}
+    with open(file_path) as datafile:
+        for line in datafile:
             if '#' in line:
                 line = line[:line.index('#')]
             line = line.strip()
@@ -216,7 +224,22 @@
                 char_start = char_end = char_range
             char_start = int(char_start, 16)
             char_end = int(char_end, 16)
-            _emoji_properties[prop].update(xrange(char_start, char_end+1))
+            char_range = xrange(char_start, char_end+1)
+            if reverse:
+                output_dict[prop].update(char_range)
+            else:
+                for char in char_range:
+                    assert char not in output_dict
+                    output_dict[char] = prop
+    return output_dict
+
+
+def parse_ucd(ucd_path):
+    global _emoji_properties, _chars_by_age
+    _emoji_properties = parse_unicode_datafile(
+        path.join(ucd_path, 'emoji-data.txt'), reverse=True)
+    _chars_by_age = parse_unicode_datafile(
+        path.join(ucd_path, 'DerivedAge.txt'), reverse=True)
 
 
 def main():