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">✗</td>
- </tr>
- <tr>
- <td class="label_neg">PixelXorXfermode</td>
- <td class="value_neg">✗</td>
- </tr>
- <tr>
<td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td>
<td class="value_neg">✗</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():