diff options
55 files changed, 1881 insertions, 462 deletions
diff --git a/api/current.txt b/api/current.txt index 358911c76d40..fa05e56d483d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -58125,13 +58125,13 @@ package java.io { ctor public ByteArrayOutputStream(int); method public void reset(); method public int size(); - method public byte[] toByteArray(); + method @NonNull public byte[] toByteArray(); method @NonNull public String toString(@NonNull String) throws java.io.UnsupportedEncodingException; method @Deprecated @NonNull public String toString(int); method public void write(int); - method public void write(byte[], int, int); + method public void write(@NonNull byte[], int, int); method public void writeTo(@NonNull java.io.OutputStream) throws java.io.IOException; - field protected byte[] buf; + field @NonNull protected byte[] buf; field protected int count; } @@ -58302,12 +58302,12 @@ package java.io { method public boolean isHidden(); method public long lastModified(); method public long length(); - method public String[] list(); - method public String[] list(@Nullable java.io.FilenameFilter); - method public java.io.File[] listFiles(); - method public java.io.File[] listFiles(@Nullable java.io.FilenameFilter); - method public java.io.File[] listFiles(@Nullable java.io.FileFilter); - method public static java.io.File[] listRoots(); + method @Nullable public String[] list(); + method @Nullable public String[] list(@Nullable java.io.FilenameFilter); + method @Nullable public java.io.File[] listFiles(); + method @Nullable public java.io.File[] listFiles(@Nullable java.io.FilenameFilter); + method @Nullable public java.io.File[] listFiles(@Nullable java.io.FileFilter); + method @NonNull public static java.io.File[] listRoots(); method public boolean mkdir(); method public boolean mkdirs(); method public boolean renameTo(@NonNull java.io.File); @@ -58796,8 +58796,8 @@ package java.io { method protected void clearError(); method public void close(); method public void flush(); - method @NonNull public java.io.PrintWriter format(@NonNull String, java.lang.Object...); - method @NonNull public java.io.PrintWriter format(@Nullable java.util.Locale, @NonNull String, java.lang.Object...); + method @NonNull public java.io.PrintWriter format(@NonNull String, @NonNull java.lang.Object...); + method @NonNull public java.io.PrintWriter format(@Nullable java.util.Locale, @NonNull String, @NonNull java.lang.Object...); method public void print(boolean); method public void print(char); method public void print(int); @@ -58807,8 +58807,8 @@ package java.io { method public void print(char[]); method public void print(@Nullable String); method public void print(@Nullable Object); - method @NonNull public java.io.PrintWriter printf(@NonNull String, java.lang.Object...); - method @NonNull public java.io.PrintWriter printf(@Nullable java.util.Locale, @NonNull String, java.lang.Object...); + method @NonNull public java.io.PrintWriter printf(@NonNull String, @NonNull java.lang.Object...); + method @NonNull public java.io.PrintWriter printf(@Nullable java.util.Locale, @NonNull String, @NonNull java.lang.Object...); method public void println(); method public void println(boolean); method public void println(char); @@ -59634,45 +59634,45 @@ package java.lang { method @NonNull public static Class<?> forName(@NonNull String) throws java.lang.ClassNotFoundException; method @NonNull public static Class<?> forName(@NonNull String, boolean, @Nullable ClassLoader) throws java.lang.ClassNotFoundException; method @Nullable public <A extends java.lang.annotation.Annotation> A getAnnotation(@NonNull Class<A>); - method public java.lang.annotation.Annotation[] getAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getAnnotations(); method @NonNull public <A extends java.lang.annotation.Annotation> A[] getAnnotationsByType(@NonNull Class<A>); method @Nullable public String getCanonicalName(); method @Nullable public ClassLoader getClassLoader(); - method public Class<?>[] getClasses(); + method @NonNull public Class<?>[] getClasses(); method @Nullable public Class<?> getComponentType(); - method @NonNull public java.lang.reflect.Constructor<T> getConstructor(Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; - method public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException; + method @NonNull public java.lang.reflect.Constructor<T> getConstructor(@Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; + method @NonNull public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException; method @Nullable public <A extends java.lang.annotation.Annotation> A getDeclaredAnnotation(@NonNull Class<A>); - method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); - method public Class<?>[] getDeclaredClasses(); - method @NonNull public java.lang.reflect.Constructor<T> getDeclaredConstructor(Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; - method public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException; + method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method @NonNull public Class<?>[] getDeclaredClasses(); + method @NonNull public java.lang.reflect.Constructor<T> getDeclaredConstructor(@Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; + method @NonNull public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException; method @NonNull public java.lang.reflect.Field getDeclaredField(@NonNull String) throws java.lang.NoSuchFieldException; - method public java.lang.reflect.Field[] getDeclaredFields(); - method @NonNull public java.lang.reflect.Method getDeclaredMethod(@NonNull String, Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; - method public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException; + method @NonNull public java.lang.reflect.Field[] getDeclaredFields(); + method @NonNull public java.lang.reflect.Method getDeclaredMethod(@NonNull String, @Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; + method @NonNull public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException; method @Nullable public Class<?> getDeclaringClass(); method @Nullable public Class<?> getEnclosingClass(); method @Nullable public java.lang.reflect.Constructor<?> getEnclosingConstructor(); method @Nullable public java.lang.reflect.Method getEnclosingMethod(); - method public T[] getEnumConstants(); + method @Nullable public T[] getEnumConstants(); method @NonNull public java.lang.reflect.Field getField(@NonNull String) throws java.lang.NoSuchFieldException; - method public java.lang.reflect.Field[] getFields() throws java.lang.SecurityException; - method public java.lang.reflect.Type[] getGenericInterfaces(); + method @NonNull public java.lang.reflect.Field[] getFields() throws java.lang.SecurityException; + method @NonNull public java.lang.reflect.Type[] getGenericInterfaces(); method @Nullable public java.lang.reflect.Type getGenericSuperclass(); - method public Class<?>[] getInterfaces(); - method @NonNull public java.lang.reflect.Method getMethod(@NonNull String, Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; - method public java.lang.reflect.Method[] getMethods() throws java.lang.SecurityException; + method @NonNull public Class<?>[] getInterfaces(); + method @NonNull public java.lang.reflect.Method getMethod(@NonNull String, @Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException; + method @NonNull public java.lang.reflect.Method[] getMethods() throws java.lang.SecurityException; method public int getModifiers(); method @NonNull public String getName(); method @Nullable public Package getPackage(); method @Nullable public java.security.ProtectionDomain getProtectionDomain(); method @Nullable public java.net.URL getResource(@NonNull String); method @Nullable public java.io.InputStream getResourceAsStream(@NonNull String); - method public Object[] getSigners(); + method @Nullable public Object[] getSigners(); method @NonNull public String getSimpleName(); method @Nullable public Class<? super T> getSuperclass(); - method public java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters(); + method @NonNull public java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters(); method public boolean isAnnotation(); method public boolean isAnonymousClass(); method public boolean isArray(); @@ -60559,8 +60559,8 @@ package java.lang { method @NonNull public static String copyValueOf(char[]); method public boolean endsWith(@NonNull String); method public boolean equalsIgnoreCase(@Nullable String); - method @NonNull public static String format(@NonNull String, java.lang.Object...); - method @NonNull public static String format(@NonNull java.util.Locale, @NonNull String, java.lang.Object...); + method @NonNull public static String format(@NonNull String, @NonNull java.lang.Object...); + method @NonNull public static String format(@NonNull java.util.Locale, @NonNull String, @NonNull java.lang.Object...); method @Deprecated public void getBytes(int, int, byte[], int); method public byte[] getBytes(@NonNull String) throws java.io.UnsupportedEncodingException; method public byte[] getBytes(@NonNull java.nio.charset.Charset); @@ -60572,7 +60572,7 @@ package java.lang { method public int indexOf(@NonNull String, int); method @NonNull public String intern(); method public boolean isEmpty(); - method @NonNull public static String join(@NonNull CharSequence, java.lang.CharSequence...); + method @NonNull public static String join(@NonNull CharSequence, @Nullable java.lang.CharSequence...); method @NonNull public static String join(@NonNull CharSequence, @NonNull Iterable<? extends java.lang.CharSequence>); method public int lastIndexOf(int); method public int lastIndexOf(int, int); @@ -60587,8 +60587,8 @@ package java.lang { method @NonNull public String replace(@NonNull CharSequence, @NonNull CharSequence); method @NonNull public String replaceAll(@NonNull String, @NonNull String); method @NonNull public String replaceFirst(@NonNull String, @NonNull String); - method public String[] split(@NonNull String, int); - method public String[] split(@NonNull String); + method @NonNull public String[] split(@NonNull String, int); + method @NonNull public String[] split(@NonNull String); method public boolean startsWith(@NonNull String, int); method public boolean startsWith(@NonNull String); method @NonNull public CharSequence subSequence(int, int); @@ -60789,7 +60789,7 @@ package java.lang { method public long getId(); method @NonNull public final String getName(); method public final int getPriority(); - method public StackTraceElement[] getStackTrace(); + method @NonNull public StackTraceElement[] getStackTrace(); method @NonNull public java.lang.Thread.State getState(); method @Nullable public final ThreadGroup getThreadGroup(); method @Nullable public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler(); @@ -60887,13 +60887,13 @@ package java.lang { method @Nullable public Throwable getCause(); method @Nullable public String getLocalizedMessage(); method @Nullable public String getMessage(); - method public StackTraceElement[] getStackTrace(); - method public final Throwable[] getSuppressed(); + method @NonNull public StackTraceElement[] getStackTrace(); + method @NonNull public final Throwable[] getSuppressed(); method @NonNull public Throwable initCause(@Nullable Throwable); method public void printStackTrace(); method public void printStackTrace(@NonNull java.io.PrintStream); method public void printStackTrace(@NonNull java.io.PrintWriter); - method public void setStackTrace(StackTraceElement[]); + method public void setStackTrace(@NonNull StackTraceElement[]); } public class TypeNotPresentException extends java.lang.RuntimeException { @@ -61216,8 +61216,8 @@ package java.lang.reflect { public class AccessibleObject implements java.lang.reflect.AnnotatedElement { ctor protected AccessibleObject(); method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>); - method public java.lang.annotation.Annotation[] getAnnotations(); - method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public boolean isAccessible(); method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException; method public void setAccessible(boolean) throws java.lang.SecurityException; @@ -61225,10 +61225,10 @@ package java.lang.reflect { public interface AnnotatedElement { method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>); - method public java.lang.annotation.Annotation[] getAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getAnnotations(); method public default <T extends java.lang.annotation.Annotation> T[] getAnnotationsByType(@NonNull Class<T>); method @Nullable public default <T extends java.lang.annotation.Annotation> T getDeclaredAnnotation(@NonNull Class<T>); - method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method public default <T extends java.lang.annotation.Annotation> T[] getDeclaredAnnotationsByType(@NonNull Class<T>); method public default boolean isAnnotationPresent(@NonNull Class<? extends java.lang.annotation.Annotation>); } @@ -61263,20 +61263,20 @@ package java.lang.reflect { method public int getModifiers(); method @NonNull public String getName(); method public java.lang.annotation.Annotation[][] getParameterAnnotations(); - method public Class<?>[] getParameterTypes(); + method @NonNull public Class<?>[] getParameterTypes(); method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters(); method @NonNull public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException; method @NonNull public String toGenericString(); } public abstract class Executable extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member { - method public abstract Class<?>[] getExceptionTypes(); - method public java.lang.reflect.Type[] getGenericExceptionTypes(); - method public java.lang.reflect.Type[] getGenericParameterTypes(); - method public abstract java.lang.annotation.Annotation[][] getParameterAnnotations(); + method @NonNull public abstract Class<?>[] getExceptionTypes(); + method @NonNull public java.lang.reflect.Type[] getGenericExceptionTypes(); + method @NonNull public java.lang.reflect.Type[] getGenericParameterTypes(); + method @NonNull public abstract java.lang.annotation.Annotation[][] getParameterAnnotations(); method public int getParameterCount(); - method public abstract Class<?>[] getParameterTypes(); - method public java.lang.reflect.Parameter[] getParameters(); + method @NonNull public abstract Class<?>[] getParameterTypes(); + method @NonNull public java.lang.reflect.Parameter[] getParameters(); method public final boolean isAnnotationPresent(@NonNull Class<? extends java.lang.annotation.Annotation>); method public boolean isSynthetic(); method public boolean isVarArgs(); @@ -61365,7 +61365,7 @@ package java.lang.reflect { method @NonNull public Class<?>[] getParameterTypes(); method @NonNull public Class<?> getReturnType(); method @NonNull public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters(); - method @Nullable public Object invoke(@Nullable Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException; + method @Nullable public Object invoke(@Nullable Object, @Nullable java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException; method public boolean isBridge(); method public boolean isDefault(); method @NonNull public String toGenericString(); @@ -61408,8 +61408,8 @@ package java.lang.reflect { public final class Parameter implements java.lang.reflect.AnnotatedElement { method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>); - method public java.lang.annotation.Annotation[] getAnnotations(); - method public java.lang.annotation.Annotation[] getDeclaredAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getAnnotations(); + method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations(); method @NonNull public java.lang.reflect.Executable getDeclaringExecutable(); method public int getModifiers(); method @NonNull public String getName(); @@ -61422,7 +61422,7 @@ package java.lang.reflect { } public interface ParameterizedType extends java.lang.reflect.Type { - method public java.lang.reflect.Type[] getActualTypeArguments(); + method @NonNull public java.lang.reflect.Type[] getActualTypeArguments(); method @Nullable public java.lang.reflect.Type getOwnerType(); method @NonNull public java.lang.reflect.Type getRawType(); } @@ -61430,9 +61430,9 @@ package java.lang.reflect { public class Proxy implements java.io.Serializable { ctor protected Proxy(@NonNull java.lang.reflect.InvocationHandler); method @NonNull public static java.lang.reflect.InvocationHandler getInvocationHandler(@NonNull Object) throws java.lang.IllegalArgumentException; - method @NonNull public static Class<?> getProxyClass(@Nullable ClassLoader, Class<?>...) throws java.lang.IllegalArgumentException; + method @NonNull public static Class<?> getProxyClass(@Nullable ClassLoader, @NonNull Class<?>...) throws java.lang.IllegalArgumentException; method public static boolean isProxyClass(@NonNull Class<?>); - method @NonNull public static Object newProxyInstance(@Nullable ClassLoader, Class<?>[], @NonNull java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException; + method @NonNull public static Object newProxyInstance(@Nullable ClassLoader, @NonNull Class<?>[], @NonNull java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException; field protected java.lang.reflect.InvocationHandler h; } @@ -61446,7 +61446,7 @@ package java.lang.reflect { } public interface TypeVariable<D extends java.lang.reflect.GenericDeclaration> extends java.lang.reflect.Type { - method public java.lang.reflect.Type[] getBounds(); + method @NonNull public java.lang.reflect.Type[] getBounds(); method @NonNull public D getGenericDeclaration(); method @NonNull public String getName(); } @@ -61458,8 +61458,8 @@ package java.lang.reflect { } public interface WildcardType extends java.lang.reflect.Type { - method public java.lang.reflect.Type[] getLowerBounds(); - method public java.lang.reflect.Type[] getUpperBounds(); + method @NonNull public java.lang.reflect.Type[] getLowerBounds(); + method @NonNull public java.lang.reflect.Type[] getUpperBounds(); } } @@ -62503,7 +62503,7 @@ package java.nio { public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> { method @NonNull public static java.nio.ByteBuffer allocate(int); method @NonNull public static java.nio.ByteBuffer allocateDirect(int); - method public final byte[] array(); + method @NonNull public final byte[] array(); method public final int arrayOffset(); method @NonNull public abstract java.nio.CharBuffer asCharBuffer(); method @NonNull public abstract java.nio.DoubleBuffer asDoubleBuffer(); @@ -62517,8 +62517,8 @@ package java.nio { method @NonNull public abstract java.nio.ByteBuffer duplicate(); method public abstract byte get(); method public abstract byte get(int); - method @NonNull public java.nio.ByteBuffer get(byte[], int, int); - method @NonNull public java.nio.ByteBuffer get(byte[]); + method @NonNull public java.nio.ByteBuffer get(@NonNull byte[], int, int); + method @NonNull public java.nio.ByteBuffer get(@NonNull byte[]); method public abstract char getChar(); method public abstract char getChar(int); method public abstract double getDouble(); @@ -62537,8 +62537,8 @@ package java.nio { method @NonNull public abstract java.nio.ByteBuffer put(byte); method @NonNull public abstract java.nio.ByteBuffer put(int, byte); method @NonNull public java.nio.ByteBuffer put(@NonNull java.nio.ByteBuffer); - method @NonNull public java.nio.ByteBuffer put(byte[], int, int); - method @NonNull public final java.nio.ByteBuffer put(byte[]); + method @NonNull public java.nio.ByteBuffer put(@NonNull byte[], int, int); + method @NonNull public final java.nio.ByteBuffer put(@NonNull byte[]); method @NonNull public abstract java.nio.ByteBuffer putChar(char); method @NonNull public abstract java.nio.ByteBuffer putChar(int, char); method @NonNull public abstract java.nio.ByteBuffer putDouble(double); @@ -62552,8 +62552,8 @@ package java.nio { method @NonNull public abstract java.nio.ByteBuffer putShort(short); method @NonNull public abstract java.nio.ByteBuffer putShort(int, short); method @NonNull public abstract java.nio.ByteBuffer slice(); - method @NonNull public static java.nio.ByteBuffer wrap(byte[], int, int); - method @NonNull public static java.nio.ByteBuffer wrap(byte[]); + method @NonNull public static java.nio.ByteBuffer wrap(@NonNull byte[], int, int); + method @NonNull public static java.nio.ByteBuffer wrap(@NonNull byte[]); } public final class ByteOrder { @@ -64386,20 +64386,20 @@ package java.security { public abstract class MessageDigest extends java.security.MessageDigestSpi { ctor protected MessageDigest(@NonNull String); - method public byte[] digest(); - method public int digest(byte[], int, int) throws java.security.DigestException; - method public byte[] digest(byte[]); + method @NonNull public byte[] digest(); + method public int digest(@NonNull byte[], int, int) throws java.security.DigestException; + method @NonNull public byte[] digest(@NonNull byte[]); method @NonNull public final String getAlgorithm(); method public final int getDigestLength(); method @NonNull public static java.security.MessageDigest getInstance(@NonNull String) throws java.security.NoSuchAlgorithmException; method @NonNull public static java.security.MessageDigest getInstance(@NonNull String, @NonNull String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method @NonNull public static java.security.MessageDigest getInstance(@NonNull String, @NonNull java.security.Provider) throws java.security.NoSuchAlgorithmException; method @NonNull public final java.security.Provider getProvider(); - method public static boolean isEqual(byte[], byte[]); + method public static boolean isEqual(@Nullable byte[], @Nullable byte[]); method public void reset(); method public void update(byte); - method public void update(byte[], int, int); - method public void update(byte[]); + method public void update(@NonNull byte[], int, int); + method public void update(@NonNull byte[]); method public final void update(@NonNull java.nio.ByteBuffer); } @@ -67006,7 +67006,7 @@ package java.text { method @NonNull public final StringBuffer format(@NonNull Object, @NonNull StringBuffer, @NonNull java.text.FieldPosition); method @NonNull public abstract StringBuffer format(@NonNull java.util.Date, @NonNull StringBuffer, @NonNull java.text.FieldPosition); method @NonNull public final String format(@NonNull java.util.Date); - method public static java.util.Locale[] getAvailableLocales(); + method @NonNull public static java.util.Locale[] getAvailableLocales(); method @NonNull public java.util.Calendar getCalendar(); method @NonNull public static final java.text.DateFormat getDateInstance(); method @NonNull public static final java.text.DateFormat getDateInstance(int); @@ -67246,7 +67246,7 @@ package java.text { method @NonNull public final String format(long); method @NonNull public abstract StringBuffer format(double, @NonNull StringBuffer, @NonNull java.text.FieldPosition); method @NonNull public abstract StringBuffer format(long, @NonNull StringBuffer, @NonNull java.text.FieldPosition); - method public static java.util.Locale[] getAvailableLocales(); + method @NonNull public static java.util.Locale[] getAvailableLocales(); method @Nullable public java.util.Currency getCurrency(); method @NonNull public static final java.text.NumberFormat getCurrencyInstance(); method @NonNull public static java.text.NumberFormat getCurrencyInstance(@NonNull java.util.Locale); @@ -68900,8 +68900,8 @@ package java.util { method public boolean remove(@Nullable Object); method public boolean removeAll(@NonNull java.util.Collection<?>); method public boolean retainAll(@NonNull java.util.Collection<?>); - method public Object[] toArray(); - method public <T> T[] toArray(T[]); + method @NonNull public Object[] toArray(); + method @NonNull public <T> T[] toArray(@NonNull T[]); } public abstract class AbstractList<E> extends java.util.AbstractCollection<E> implements java.util.List<E> { @@ -69010,161 +69010,161 @@ package java.util { } public class Arrays { - method @NonNull @java.lang.SafeVarargs public static <T> java.util.List<T> asList(T...); - method public static int binarySearch(long[], long); - method public static int binarySearch(long[], int, int, long); - method public static int binarySearch(int[], int); - method public static int binarySearch(int[], int, int, int); - method public static int binarySearch(short[], short); - method public static int binarySearch(short[], int, int, short); - method public static int binarySearch(char[], char); - method public static int binarySearch(char[], int, int, char); - method public static int binarySearch(byte[], byte); - method public static int binarySearch(byte[], int, int, byte); - method public static int binarySearch(double[], double); - method public static int binarySearch(double[], int, int, double); - method public static int binarySearch(float[], float); - method public static int binarySearch(float[], int, int, float); - method public static int binarySearch(Object[], @NonNull Object); - method public static int binarySearch(Object[], int, int, @NonNull Object); - method public static <T> int binarySearch(T[], T, @Nullable java.util.Comparator<? super T>); - method public static <T> int binarySearch(T[], int, int, T, @Nullable java.util.Comparator<? super T>); - method public static <T> T[] copyOf(T[], int); - method public static <T, U> T[] copyOf(U[], int, @NonNull Class<? extends T[]>); - method public static byte[] copyOf(byte[], int); - method public static short[] copyOf(short[], int); - method public static int[] copyOf(int[], int); - method public static long[] copyOf(long[], int); - method public static char[] copyOf(char[], int); - method public static float[] copyOf(float[], int); - method public static double[] copyOf(double[], int); - method public static boolean[] copyOf(boolean[], int); - method public static <T> T[] copyOfRange(T[], int, int); - method public static <T, U> T[] copyOfRange(U[], int, int, @NonNull Class<? extends T[]>); - method public static byte[] copyOfRange(byte[], int, int); - method public static short[] copyOfRange(short[], int, int); - method public static int[] copyOfRange(int[], int, int); - method public static long[] copyOfRange(long[], int, int); - method public static char[] copyOfRange(char[], int, int); - method public static float[] copyOfRange(float[], int, int); - method public static double[] copyOfRange(double[], int, int); - method public static boolean[] copyOfRange(boolean[], int, int); - method public static boolean deepEquals(Object[], Object[]); - method public static int deepHashCode(Object[]); - method @NonNull public static String deepToString(Object[]); - method public static boolean equals(long[], long[]); - method public static boolean equals(int[], int[]); - method public static boolean equals(short[], short[]); - method public static boolean equals(char[], char[]); - method public static boolean equals(byte[], byte[]); - method public static boolean equals(boolean[], boolean[]); - method public static boolean equals(double[], double[]); - method public static boolean equals(float[], float[]); - method public static boolean equals(Object[], Object[]); - method public static void fill(long[], long); - method public static void fill(long[], int, int, long); - method public static void fill(int[], int); - method public static void fill(int[], int, int, int); - method public static void fill(short[], short); - method public static void fill(short[], int, int, short); - method public static void fill(char[], char); - method public static void fill(char[], int, int, char); - method public static void fill(byte[], byte); - method public static void fill(byte[], int, int, byte); - method public static void fill(boolean[], boolean); - method public static void fill(boolean[], int, int, boolean); - method public static void fill(double[], double); - method public static void fill(double[], int, int, double); - method public static void fill(float[], float); - method public static void fill(float[], int, int, float); - method public static void fill(Object[], @Nullable Object); - method public static void fill(Object[], int, int, @Nullable Object); - method public static int hashCode(long[]); - method public static int hashCode(int[]); - method public static int hashCode(short[]); - method public static int hashCode(char[]); - method public static int hashCode(byte[]); - method public static int hashCode(boolean[]); - method public static int hashCode(float[]); - method public static int hashCode(double[]); - method public static int hashCode(Object[]); - method public static <T> void parallelPrefix(T[], @NonNull java.util.function.BinaryOperator<T>); - method public static <T> void parallelPrefix(T[], int, int, @NonNull java.util.function.BinaryOperator<T>); - method public static void parallelPrefix(long[], @NonNull java.util.function.LongBinaryOperator); - method public static void parallelPrefix(long[], int, int, @NonNull java.util.function.LongBinaryOperator); - method public static void parallelPrefix(double[], @NonNull java.util.function.DoubleBinaryOperator); - method public static void parallelPrefix(double[], int, int, @NonNull java.util.function.DoubleBinaryOperator); - method public static void parallelPrefix(int[], @NonNull java.util.function.IntBinaryOperator); - method public static void parallelPrefix(int[], int, int, @NonNull java.util.function.IntBinaryOperator); - method public static <T> void parallelSetAll(T[], @NonNull java.util.function.IntFunction<? extends T>); - method public static void parallelSetAll(int[], @NonNull java.util.function.IntUnaryOperator); - method public static void parallelSetAll(long[], @NonNull java.util.function.IntToLongFunction); - method public static void parallelSetAll(double[], @NonNull java.util.function.IntToDoubleFunction); - method public static void parallelSort(byte[]); - method public static void parallelSort(byte[], int, int); - method public static void parallelSort(char[]); - method public static void parallelSort(char[], int, int); - method public static void parallelSort(short[]); - method public static void parallelSort(short[], int, int); - method public static void parallelSort(int[]); - method public static void parallelSort(int[], int, int); - method public static void parallelSort(long[]); - method public static void parallelSort(long[], int, int); - method public static void parallelSort(float[]); - method public static void parallelSort(float[], int, int); - method public static void parallelSort(double[]); - method public static void parallelSort(double[], int, int); - method public static <T extends java.lang.Comparable<? super T>> void parallelSort(T[]); - method public static <T extends java.lang.Comparable<? super T>> void parallelSort(T[], int, int); - method public static <T> void parallelSort(T[], @Nullable java.util.Comparator<? super T>); - method public static <T> void parallelSort(T[], int, int, @Nullable java.util.Comparator<? super T>); - method public static <T> void setAll(T[], @NonNull java.util.function.IntFunction<? extends T>); - method public static void setAll(int[], @NonNull java.util.function.IntUnaryOperator); - method public static void setAll(long[], @NonNull java.util.function.IntToLongFunction); - method public static void setAll(double[], @NonNull java.util.function.IntToDoubleFunction); - method public static void sort(int[]); - method public static void sort(int[], int, int); - method public static void sort(long[]); - method public static void sort(long[], int, int); - method public static void sort(short[]); - method public static void sort(short[], int, int); - method public static void sort(char[]); - method public static void sort(char[], int, int); - method public static void sort(byte[]); - method public static void sort(byte[], int, int); - method public static void sort(float[]); - method public static void sort(float[], int, int); - method public static void sort(double[]); - method public static void sort(double[], int, int); - method public static void sort(Object[]); - method public static void sort(Object[], int, int); - method public static <T> void sort(T[], @Nullable java.util.Comparator<? super T>); - method public static <T> void sort(T[], int, int, @Nullable java.util.Comparator<? super T>); - method @NonNull public static <T> java.util.Spliterator<T> spliterator(T[]); - method @NonNull public static <T> java.util.Spliterator<T> spliterator(T[], int, int); - method @NonNull public static java.util.Spliterator.OfInt spliterator(int[]); - method @NonNull public static java.util.Spliterator.OfInt spliterator(int[], int, int); - method @NonNull public static java.util.Spliterator.OfLong spliterator(long[]); - method @NonNull public static java.util.Spliterator.OfLong spliterator(long[], int, int); - method @NonNull public static java.util.Spliterator.OfDouble spliterator(double[]); - method @NonNull public static java.util.Spliterator.OfDouble spliterator(double[], int, int); - method @NonNull public static <T> java.util.stream.Stream<T> stream(T[]); - method @NonNull public static <T> java.util.stream.Stream<T> stream(T[], int, int); - method @NonNull public static java.util.stream.IntStream stream(int[]); - method @NonNull public static java.util.stream.IntStream stream(int[], int, int); - method @NonNull public static java.util.stream.LongStream stream(long[]); - method @NonNull public static java.util.stream.LongStream stream(long[], int, int); - method @NonNull public static java.util.stream.DoubleStream stream(double[]); - method @NonNull public static java.util.stream.DoubleStream stream(double[], int, int); - method @NonNull public static String toString(long[]); - method @NonNull public static String toString(int[]); - method @NonNull public static String toString(short[]); - method @NonNull public static String toString(char[]); - method @NonNull public static String toString(byte[]); - method @NonNull public static String toString(boolean[]); - method @NonNull public static String toString(float[]); - method @NonNull public static String toString(double[]); - method @NonNull public static String toString(Object[]); + method @NonNull @java.lang.SafeVarargs public static <T> java.util.List<T> asList(@NonNull T...); + method public static int binarySearch(@NonNull long[], long); + method public static int binarySearch(@NonNull long[], int, int, long); + method public static int binarySearch(@NonNull int[], int); + method public static int binarySearch(@NonNull int[], int, int, int); + method public static int binarySearch(@NonNull short[], short); + method public static int binarySearch(@NonNull short[], int, int, short); + method public static int binarySearch(@NonNull char[], char); + method public static int binarySearch(@NonNull char[], int, int, char); + method public static int binarySearch(@NonNull byte[], byte); + method public static int binarySearch(@NonNull byte[], int, int, byte); + method public static int binarySearch(@NonNull double[], double); + method public static int binarySearch(@NonNull double[], int, int, double); + method public static int binarySearch(@NonNull float[], float); + method public static int binarySearch(@NonNull float[], int, int, float); + method public static int binarySearch(@NonNull Object[], @NonNull Object); + method public static int binarySearch(@NonNull Object[], int, int, @NonNull Object); + method public static <T> int binarySearch(@NonNull T[], T, @Nullable java.util.Comparator<? super T>); + method public static <T> int binarySearch(@NonNull T[], int, int, T, @Nullable java.util.Comparator<? super T>); + method @NonNull public static <T> T[] copyOf(@NonNull T[], int); + method @NonNull public static <T, U> T[] copyOf(@NonNull U[], int, @NonNull Class<? extends T[]>); + method @NonNull public static byte[] copyOf(@NonNull byte[], int); + method @NonNull public static short[] copyOf(@NonNull short[], int); + method @NonNull public static int[] copyOf(@NonNull int[], int); + method @NonNull public static long[] copyOf(@NonNull long[], int); + method @NonNull public static char[] copyOf(@NonNull char[], int); + method @NonNull public static float[] copyOf(@NonNull float[], int); + method @NonNull public static double[] copyOf(@NonNull double[], int); + method @NonNull public static boolean[] copyOf(@NonNull boolean[], int); + method @NonNull public static <T> T[] copyOfRange(@NonNull T[], int, int); + method @NonNull public static <T, U> T[] copyOfRange(@NonNull U[], int, int, @NonNull Class<? extends T[]>); + method @NonNull public static byte[] copyOfRange(@NonNull byte[], int, int); + method @NonNull public static short[] copyOfRange(@NonNull short[], int, int); + method @NonNull public static int[] copyOfRange(@NonNull int[], int, int); + method @NonNull public static long[] copyOfRange(@NonNull long[], int, int); + method @NonNull public static char[] copyOfRange(@NonNull char[], int, int); + method @NonNull public static float[] copyOfRange(@NonNull float[], int, int); + method @NonNull public static double[] copyOfRange(@NonNull double[], int, int); + method @NonNull public static boolean[] copyOfRange(@NonNull boolean[], int, int); + method public static boolean deepEquals(@Nullable Object[], @Nullable Object[]); + method public static int deepHashCode(@Nullable Object[]); + method @NonNull public static String deepToString(@Nullable Object[]); + method public static boolean equals(@Nullable long[], @Nullable long[]); + method public static boolean equals(@Nullable int[], @Nullable int[]); + method public static boolean equals(@Nullable short[], @Nullable short[]); + method public static boolean equals(@Nullable char[], @Nullable char[]); + method public static boolean equals(@Nullable byte[], @Nullable byte[]); + method public static boolean equals(@Nullable boolean[], @Nullable boolean[]); + method public static boolean equals(@Nullable double[], @Nullable double[]); + method public static boolean equals(@Nullable float[], @Nullable float[]); + method public static boolean equals(@Nullable Object[], @Nullable Object[]); + method public static void fill(@NonNull long[], long); + method public static void fill(@NonNull long[], int, int, long); + method public static void fill(@NonNull int[], int); + method public static void fill(@NonNull int[], int, int, int); + method public static void fill(@NonNull short[], short); + method public static void fill(@NonNull short[], int, int, short); + method public static void fill(@NonNull char[], char); + method public static void fill(@NonNull char[], int, int, char); + method public static void fill(@NonNull byte[], byte); + method public static void fill(@NonNull byte[], int, int, byte); + method public static void fill(@NonNull boolean[], boolean); + method public static void fill(@NonNull boolean[], int, int, boolean); + method public static void fill(@NonNull double[], double); + method public static void fill(@NonNull double[], int, int, double); + method public static void fill(@NonNull float[], float); + method public static void fill(@NonNull float[], int, int, float); + method public static void fill(@NonNull Object[], @Nullable Object); + method public static void fill(@NonNull Object[], int, int, @Nullable Object); + method public static int hashCode(@Nullable long[]); + method public static int hashCode(@Nullable int[]); + method public static int hashCode(@Nullable short[]); + method public static int hashCode(@Nullable char[]); + method public static int hashCode(@Nullable byte[]); + method public static int hashCode(@Nullable boolean[]); + method public static int hashCode(@Nullable float[]); + method public static int hashCode(@Nullable double[]); + method public static int hashCode(@Nullable Object[]); + method public static <T> void parallelPrefix(@NonNull T[], @NonNull java.util.function.BinaryOperator<T>); + method public static <T> void parallelPrefix(@NonNull T[], int, int, @NonNull java.util.function.BinaryOperator<T>); + method public static void parallelPrefix(@NonNull long[], @NonNull java.util.function.LongBinaryOperator); + method public static void parallelPrefix(@NonNull long[], int, int, @NonNull java.util.function.LongBinaryOperator); + method public static void parallelPrefix(@NonNull double[], @NonNull java.util.function.DoubleBinaryOperator); + method public static void parallelPrefix(@NonNull double[], int, int, @NonNull java.util.function.DoubleBinaryOperator); + method public static void parallelPrefix(@NonNull int[], @NonNull java.util.function.IntBinaryOperator); + method public static void parallelPrefix(@NonNull int[], int, int, @NonNull java.util.function.IntBinaryOperator); + method public static <T> void parallelSetAll(@NonNull T[], @NonNull java.util.function.IntFunction<? extends T>); + method public static void parallelSetAll(@NonNull int[], @NonNull java.util.function.IntUnaryOperator); + method public static void parallelSetAll(@NonNull long[], @NonNull java.util.function.IntToLongFunction); + method public static void parallelSetAll(@NonNull double[], @NonNull java.util.function.IntToDoubleFunction); + method public static void parallelSort(@NonNull byte[]); + method public static void parallelSort(@NonNull byte[], int, int); + method public static void parallelSort(@NonNull char[]); + method public static void parallelSort(@NonNull char[], int, int); + method public static void parallelSort(@NonNull short[]); + method public static void parallelSort(@NonNull short[], int, int); + method public static void parallelSort(@NonNull int[]); + method public static void parallelSort(@NonNull int[], int, int); + method public static void parallelSort(@NonNull long[]); + method public static void parallelSort(@NonNull long[], int, int); + method public static void parallelSort(@NonNull float[]); + method public static void parallelSort(@NonNull float[], int, int); + method public static void parallelSort(@NonNull double[]); + method public static void parallelSort(@NonNull double[], int, int); + method public static <T extends java.lang.Comparable<? super T>> void parallelSort(@NonNull T[]); + method public static <T extends java.lang.Comparable<? super T>> void parallelSort(@NonNull T[], int, int); + method public static <T> void parallelSort(@NonNull T[], @Nullable java.util.Comparator<? super T>); + method public static <T> void parallelSort(@NonNull T[], int, int, @Nullable java.util.Comparator<? super T>); + method public static <T> void setAll(@NonNull T[], @NonNull java.util.function.IntFunction<? extends T>); + method public static void setAll(@NonNull int[], @NonNull java.util.function.IntUnaryOperator); + method public static void setAll(@NonNull long[], @NonNull java.util.function.IntToLongFunction); + method public static void setAll(@NonNull double[], @NonNull java.util.function.IntToDoubleFunction); + method public static void sort(@NonNull int[]); + method public static void sort(@NonNull int[], int, int); + method public static void sort(@NonNull long[]); + method public static void sort(@NonNull long[], int, int); + method public static void sort(@NonNull short[]); + method public static void sort(@NonNull short[], int, int); + method public static void sort(@NonNull char[]); + method public static void sort(@NonNull char[], int, int); + method public static void sort(@NonNull byte[]); + method public static void sort(@NonNull byte[], int, int); + method public static void sort(@NonNull float[]); + method public static void sort(@NonNull float[], int, int); + method public static void sort(@NonNull double[]); + method public static void sort(@NonNull double[], int, int); + method public static void sort(@NonNull Object[]); + method public static void sort(@NonNull Object[], int, int); + method public static <T> void sort(@NonNull T[], @Nullable java.util.Comparator<? super T>); + method public static <T> void sort(@NonNull T[], int, int, @Nullable java.util.Comparator<? super T>); + method @NonNull public static <T> java.util.Spliterator<T> spliterator(@NonNull T[]); + method @NonNull public static <T> java.util.Spliterator<T> spliterator(@NonNull T[], int, int); + method @NonNull public static java.util.Spliterator.OfInt spliterator(@NonNull int[]); + method @NonNull public static java.util.Spliterator.OfInt spliterator(@NonNull int[], int, int); + method @NonNull public static java.util.Spliterator.OfLong spliterator(@NonNull long[]); + method @NonNull public static java.util.Spliterator.OfLong spliterator(@NonNull long[], int, int); + method @NonNull public static java.util.Spliterator.OfDouble spliterator(@NonNull double[]); + method @NonNull public static java.util.Spliterator.OfDouble spliterator(@NonNull double[], int, int); + method @NonNull public static <T> java.util.stream.Stream<T> stream(@NonNull T[]); + method @NonNull public static <T> java.util.stream.Stream<T> stream(@NonNull T[], int, int); + method @NonNull public static java.util.stream.IntStream stream(@NonNull int[]); + method @NonNull public static java.util.stream.IntStream stream(@NonNull int[], int, int); + method @NonNull public static java.util.stream.LongStream stream(@NonNull long[]); + method @NonNull public static java.util.stream.LongStream stream(@NonNull long[], int, int); + method @NonNull public static java.util.stream.DoubleStream stream(@NonNull double[]); + method @NonNull public static java.util.stream.DoubleStream stream(@NonNull double[], int, int); + method @NonNull public static String toString(@Nullable long[]); + method @NonNull public static String toString(@Nullable int[]); + method @NonNull public static String toString(@Nullable short[]); + method @NonNull public static String toString(@Nullable char[]); + method @NonNull public static String toString(@Nullable byte[]); + method @NonNull public static String toString(@Nullable boolean[]); + method @NonNull public static String toString(@Nullable float[]); + method @NonNull public static String toString(@Nullable double[]); + method @NonNull public static String toString(@Nullable Object[]); } public class Base64 { @@ -69248,7 +69248,7 @@ package java.util { method public int getActualMaximum(int); method public int getActualMinimum(int); method @NonNull public static java.util.Set<java.lang.String> getAvailableCalendarTypes(); - method public static java.util.Locale[] getAvailableLocales(); + method @NonNull public static java.util.Locale[] getAvailableLocales(); method @NonNull public String getCalendarType(); method @Nullable public String getDisplayName(int, int, @NonNull java.util.Locale); method @Nullable public java.util.Map<java.lang.String,java.lang.Integer> getDisplayNames(int, int, @NonNull java.util.Locale); @@ -69336,8 +69336,8 @@ package java.util { field public static final int YEAR = 1; // 0x1 field public static final int ZONE_OFFSET = 15; // 0xf field protected boolean areFieldsSet; - field protected int[] fields; - field protected boolean[] isSet; + field @NonNull protected int[] fields; + field @NonNull protected boolean[] isSet; field protected boolean isTimeSet; field protected long time; } @@ -69348,7 +69348,7 @@ package java.util { method @NonNull public java.util.Calendar.Builder set(int, int); method @NonNull public java.util.Calendar.Builder setCalendarType(@NonNull String); method @NonNull public java.util.Calendar.Builder setDate(int, int, int); - method @NonNull public java.util.Calendar.Builder setFields(int...); + method @NonNull public java.util.Calendar.Builder setFields(@NonNull int...); method @NonNull public java.util.Calendar.Builder setInstant(long); method @NonNull public java.util.Calendar.Builder setInstant(@NonNull java.util.Date); method @NonNull public java.util.Calendar.Builder setLenient(boolean); @@ -69378,12 +69378,12 @@ package java.util { method public int size(); method @NonNull public default java.util.Spliterator<E> spliterator(); method @NonNull public default java.util.stream.Stream<E> stream(); - method public Object[] toArray(); - method public <T> T[] toArray(T[]); + method @NonNull public Object[] toArray(); + method @NonNull public <T> T[] toArray(@NonNull T[]); } public class Collections { - method @java.lang.SafeVarargs public static <T> boolean addAll(@NonNull java.util.Collection<? super T>, T...); + method @java.lang.SafeVarargs public static <T> boolean addAll(@NonNull java.util.Collection<? super T>, @NonNull T...); method @NonNull public static <T> java.util.Queue<T> asLifoQueue(@NonNull java.util.Deque<T>); method public static <T> int binarySearch(@NonNull java.util.List<? extends java.lang.Comparable<? super T>>, @NonNull T); method public static <T> int binarySearch(@NonNull java.util.List<? extends T>, T, @Nullable java.util.Comparator<? super T>); @@ -69901,7 +69901,7 @@ package java.util { method @NonNull public static java.util.List<java.lang.String> filterTags(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.lang.String>, @NonNull java.util.Locale.FilteringMode); method @NonNull public static java.util.List<java.lang.String> filterTags(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.lang.String>); method @NonNull public static java.util.Locale forLanguageTag(@NonNull String); - method public static java.util.Locale[] getAvailableLocales(); + method @NonNull public static java.util.Locale[] getAvailableLocales(); method @NonNull public String getCountry(); method @NonNull public static java.util.Locale getDefault(); method @NonNull public static java.util.Locale getDefault(@NonNull java.util.Locale.Category); @@ -69919,8 +69919,8 @@ package java.util { method @NonNull public java.util.Set<java.lang.Character> getExtensionKeys(); method @NonNull public String getISO3Country() throws java.util.MissingResourceException; method @NonNull public String getISO3Language() throws java.util.MissingResourceException; - method public static String[] getISOCountries(); - method public static String[] getISOLanguages(); + method @NonNull public static String[] getISOCountries(); + method @NonNull public static String[] getISOLanguages(); method @NonNull public String getLanguage(); method @NonNull public String getScript(); method @NonNull public java.util.Set<java.lang.String> getUnicodeLocaleAttributes(); @@ -70114,7 +70114,7 @@ package java.util { method public static <T> int compare(T, T, @NonNull java.util.Comparator<? super T>); method public static boolean deepEquals(@Nullable Object, @Nullable Object); method public static boolean equals(@Nullable Object, @Nullable Object); - method public static int hash(java.lang.Object...); + method public static int hash(@Nullable java.lang.Object...); method public static int hashCode(@Nullable Object); method public static boolean isNull(@Nullable Object); method public static boolean nonNull(@Nullable Object); @@ -70779,7 +70779,7 @@ package java.util { method public void addElement(E); method public int capacity(); method @NonNull public Object clone(); - method public void copyInto(Object[]); + method public void copyInto(@NonNull Object[]); method public E elementAt(int); method @NonNull public java.util.Enumeration<E> elements(); method public void ensureCapacity(int); @@ -70799,7 +70799,7 @@ package java.util { method public void trimToSize(); field protected int capacityIncrement; field protected int elementCount; - field protected Object[] elementData; + field @NonNull protected Object[] elementData; } public class WeakHashMap<K, V> extends java.util.AbstractMap<K,V> implements java.util.Map<K,V> { @@ -71190,7 +71190,7 @@ package java.util.concurrent { public class CopyOnWriteArrayList<E> implements java.lang.Cloneable java.util.List<E> java.util.RandomAccess java.io.Serializable { ctor public CopyOnWriteArrayList(); ctor public CopyOnWriteArrayList(@NonNull java.util.Collection<? extends E>); - ctor public CopyOnWriteArrayList(E[]); + ctor public CopyOnWriteArrayList(@NonNull E[]); method public boolean add(E); method public void add(int, E); method public boolean addAll(@NonNull java.util.Collection<? extends E>); @@ -71218,8 +71218,8 @@ package java.util.concurrent { method public E set(int, E); method public int size(); method @NonNull public java.util.List<E> subList(int, int); - method public Object[] toArray(); - method public <T> T[] toArray(T[]); + method @NonNull public Object[] toArray(); + method @NonNull public <T> T[] toArray(@NonNull T[]); } public class CopyOnWriteArraySet<E> extends java.util.AbstractSet<E> implements java.io.Serializable { @@ -72971,7 +72971,7 @@ package java.util.logging { method public void config(@NonNull java.util.function.Supplier<java.lang.String>); method public void entering(@Nullable String, @Nullable String); method public void entering(@Nullable String, @Nullable String, @Nullable Object); - method public void entering(@Nullable String, @Nullable String, Object[]); + method public void entering(@Nullable String, @Nullable String, @Nullable Object[]); method public void exiting(@Nullable String, @Nullable String); method public void exiting(@Nullable String, @Nullable String, @Nullable Object); method public void fine(@Nullable String); @@ -72984,7 +72984,7 @@ package java.util.logging { method @NonNull public static java.util.logging.Logger getAnonymousLogger(@Nullable String); method @Nullable public java.util.logging.Filter getFilter(); method @NonNull public static final java.util.logging.Logger getGlobal(); - method public java.util.logging.Handler[] getHandlers(); + method @NonNull public java.util.logging.Handler[] getHandlers(); method @Nullable public java.util.logging.Level getLevel(); method @NonNull public static java.util.logging.Logger getLogger(@NonNull String); method @NonNull public static java.util.logging.Logger getLogger(@NonNull String, @Nullable String); @@ -73000,7 +73000,7 @@ package java.util.logging { method public void log(@NonNull java.util.logging.Level, @Nullable String); method public void log(@NonNull java.util.logging.Level, @NonNull java.util.function.Supplier<java.lang.String>); method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Object); - method public void log(@NonNull java.util.logging.Level, @Nullable String, Object[]); + method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Object[]); method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Throwable); method public void log(@NonNull java.util.logging.Level, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>); method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String); @@ -73011,8 +73011,8 @@ package java.util.logging { method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>); method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String); method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Object); - method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, Object[]); - method public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable java.util.ResourceBundle, @Nullable String, java.lang.Object...); + method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]); + method public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable java.util.ResourceBundle, @Nullable String, @Nullable java.lang.Object...); method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable); method public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable java.util.ResourceBundle, @Nullable String, @Nullable Throwable); method public void removeHandler(@Nullable java.util.logging.Handler) throws java.lang.SecurityException; @@ -73274,8 +73274,8 @@ package java.util.regex { method public static boolean matches(@NonNull String, @NonNull CharSequence); method @NonNull public String pattern(); method @NonNull public static String quote(@NonNull String); - method public String[] split(@NonNull CharSequence, int); - method public String[] split(@NonNull CharSequence); + method @NonNull public String[] split(@NonNull CharSequence, int); + method @NonNull public String[] split(@NonNull CharSequence); method @NonNull public java.util.stream.Stream<java.lang.String> splitAsStream(@NonNull CharSequence); field public static final int CANON_EQ = 128; // 0x80 field public static final int CASE_INSENSITIVE = 2; // 0x2 diff --git a/api/system-current.txt b/api/system-current.txt index 88725e128e8b..19b3f0e6cae2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1718,9 +1718,9 @@ package android.content.rollback { public final class RollbackInfo implements android.os.Parcelable { method public int describeContents(); method public java.util.List<android.content.pm.VersionedPackage> getCausePackages(); + method public int getCommittedSessionId(); method public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages(); method public int getRollbackId(); - method public int getSessionId(); method public boolean isStaged(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR; diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp index 4e4b8f35726d..f37d2bedf8c2 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp @@ -55,6 +55,10 @@ bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* da Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value); if (!status.isOk()) { ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId); + StatsdStats::getInstance().noteStatsCompanionPullFailed(mTagId); + if (status.exceptionCode() == Status::Exception::EX_TRANSACTION_FAILED) { + StatsdStats::getInstance().noteStatsCompanionPullBinderTransactionFailed(mTagId); + } return false; } data->clear(); diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ba7bcc437d74..a6ba2cae2889 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -231,6 +231,9 @@ bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) { if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) { bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data); VLOG("pulled %d items", (int)data->size()); + if (!ret) { + StatsdStats::getInstance().notePullFailed(tagId); + } return ret; } else { VLOG("Unknown tagId %d", tagId); diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index f4d0144e8878..37ccad5f4a49 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -403,6 +403,60 @@ void StatsdStats::noteSystemServerRestart(int32_t timeSec) { mSystemServerRestartSec.push_back(timeSec); } +void StatsdStats::notePullFailed(int atomId) { + lock_guard<std::mutex> lock(mLock); + mPulledAtomStats[atomId].pullFailed++; +} + +void StatsdStats::noteStatsCompanionPullFailed(int atomId) { + lock_guard<std::mutex> lock(mLock); + mPulledAtomStats[atomId].statsCompanionPullFailed++; +} + +void StatsdStats::noteStatsCompanionPullBinderTransactionFailed(int atomId) { + lock_guard<std::mutex> lock(mLock); + mPulledAtomStats[atomId].statsCompanionPullBinderTransactionFailed++; +} + +void StatsdStats::noteEmptyData(int atomId) { + lock_guard<std::mutex> lock(mLock); + mPulledAtomStats[atomId].emptyData++; +} + +void StatsdStats::noteHardDimensionLimitReached(int metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).hardDimensionLimitReached++; +} + +void StatsdStats::noteLateLogEventSkipped(int metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).lateLogEventSkipped++; +} + +void StatsdStats::noteSkippedForwardBuckets(int metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).skippedForwardBuckets++; +} + +void StatsdStats::noteBadValueType(int metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).badValueType++; +} + +void StatsdStats::noteConditionChangeInNextBucket(int metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).conditionChangeInNextBucket++; +} + +StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) { + auto atomMetricStatsIter = mAtomMetricStats.find(metricId); + if (atomMetricStatsIter != mAtomMetricStats.end()) { + return atomMetricStatsIter->second; + } + auto emplaceResult = mAtomMetricStats.emplace(metricId, AtomMetricStats()); + return emplaceResult.first->second; +} + void StatsdStats::reset() { lock_guard<std::mutex> lock(mLock); resetInternalLocked(); @@ -442,6 +496,7 @@ void StatsdStats::resetInternalLocked() { pullStats.second.pullTimeout = 0; pullStats.second.pullExceedMaxDelay = 0; } + mAtomMetricStats.clear(); } string buildTimeString(int64_t timeSec) { @@ -713,6 +768,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { android::os::statsd::writePullerStatsToStream(pair, &proto); } + for (const auto& pair : mAtomMetricStats) { + android::os::statsd::writeAtomMetricStatsToStream(pair, &proto); + } + if (mAnomalyAlarmRegisteredStats > 0) { uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index dc647f888b6f..01e9ca17e5fd 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -318,6 +318,53 @@ public: void noteLogLost(int32_t wallClockTimeSec, int32_t count, int lastError); /** + * Records that the pull of an atom has failed + */ + void notePullFailed(int atomId); + + /** + * Records that the pull of StatsCompanionService atom has failed + */ + void noteStatsCompanionPullFailed(int atomId); + + /** + * Records that the pull of a StatsCompanionService atom has failed due to a failed binder + * transaction. This can happen when StatsCompanionService returns too + * much data (the max Binder parcel size is 1MB) + */ + void noteStatsCompanionPullBinderTransactionFailed(int atomId); + + /** + * A pull with no data occurred + */ + void noteEmptyData(int atomId); + + /** + * Hard limit was reached in the cardinality of an atom + */ + void noteHardDimensionLimitReached(int atomId); + + /** + * A log event was too late, arrived in the wrong bucket and was skipped + */ + void noteLateLogEventSkipped(int atomId); + + /** + * Buckets were skipped as time elapsed without any data for them + */ + void noteSkippedForwardBuckets(int atomId); + + /** + * An unsupported value type was received + */ + void noteBadValueType(int atomId); + + /** + * A condition change was too late, arrived in the wrong bucket and was skipped + */ + void noteConditionChangeInNextBucket(int atomId); + + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue * to collect stats after reset() has been called. @@ -349,8 +396,20 @@ public: long dataError = 0; long pullTimeout = 0; long pullExceedMaxDelay = 0; + long pullFailed = 0; + long statsCompanionPullFailed = 0; + long statsCompanionPullBinderTransactionFailed = 0; + long emptyData = 0; } PulledAtomStats; + typedef struct { + long hardDimensionLimitReached = 0; + long lateLogEventSkipped = 0; + long skippedForwardBuckets = 0; + long badValueType = 0; + long conditionChangeInNextBucket = 0; + } AtomMetricStats; + private: StatsdStats(); @@ -378,6 +437,9 @@ private: // Maps PullAtomId to its stats. The size is capped by the puller atom counts. std::map<int, PulledAtomStats> mPulledAtomStats; + // Maps metric ID to its stats. The size is capped by the number of metrics. + std::map<int, AtomMetricStats> mAtomMetricStats; + struct LogLossStats { LogLossStats(int32_t sec, int32_t count, int32_t error) : mWallClockSec(sec), mCount(count), mLastError(error) { @@ -414,6 +476,12 @@ private: void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); + /** + * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference + * will live as long as `this`. + */ + StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId); + FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestConfigRemove); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 5645461cc3ab..6aa8e842b021 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -322,6 +322,7 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); + StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); return; } @@ -359,6 +360,12 @@ void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { } StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs); + if (timestampNs < mCurrentBucketStartTimeNs) { + // The data will be skipped in onMatchedLogEventInternalLocked, but we don't want to report + // for every event, just the pull + StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); + } + for (const auto& data : allData) { // make a copy before doing and changes LogEvent localCopy = data->makeCopy(); @@ -380,6 +387,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven if (mCondition) { if (allData.size() == 0) { VLOG("Data pulled is empty"); + StatsdStats::getInstance().noteEmptyData(mPullTagId); return; } // For scheduled pulled data, the effective event time is snap to the nearest @@ -394,6 +402,7 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven if (bucketEndTime < mCurrentBucketStartTimeNs) { VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", (long long)bucketEndTime, (long long)mCurrentBucketStartTimeNs); + StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId); return; } for (const auto& data : allData) { @@ -442,6 +451,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { if (newTupleCount > mDimensionHardLimit) { ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId, newKey.toString().c_str()); + StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); return true; } } @@ -539,6 +549,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn Value value; if (!getDoubleOrLong(event, matcher, value)) { VLOG("Failed to get value %d from event %s", i, event.ToString().c_str()); + StatsdStats::getInstance().noteBadValueType(mMetricId); return; } interval.seenNewData = true; @@ -656,6 +667,7 @@ void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { if (numBucketsForward > 1) { VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); + StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); // take base again in future good bucket. resetBase(); } diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index e8de875a461b..cca09ac017a3 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -403,9 +403,23 @@ message StatsdStatsReport { optional int64 data_error = 9; optional int64 pull_timeout = 10; optional int64 pull_exceed_max_delay = 11; + optional int64 pull_failed = 12; + optional int64 stats_companion_pull_failed = 13; + optional int64 stats_companion_pull_binder_transaction_failed = 14; + optional int64 empty_data = 15; } repeated PulledAtomStats pulled_atom_stats = 10; + message AtomMetricStats { + optional int64 metric_id = 1; + optional int64 hard_dimension_limit_reached = 2; + optional int64 late_log_event_skipped = 3; + optional int64 skipped_forward_buckets = 4; + optional int64 bad_value_type = 5; + optional int64 condition_change_in_next_bucket = 6; + } + repeated AtomMetricStats atom_metric_stats = 17; + message LoggerErrorStats { optional int32 logger_disconnection_sec = 1; optional int32 error_code = 2; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 7de0bb3290a7..9c9985ed271c 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -66,6 +66,18 @@ const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8; const int FIELD_ID_DATA_ERROR = 9; const int FIELD_ID_PULL_TIMEOUT = 10; const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11; +const int FIELD_ID_PULL_FAILED = 12; +const int FIELD_ID_STATS_COMPANION_FAILED = 13; +const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14; +const int FIELD_ID_EMPTY_DATA = 15; +// for AtomMetricStats proto +const int FIELD_ID_ATOM_METRIC_STATS = 17; +const int FIELD_ID_METRIC_ID = 1; +const int FIELD_ID_HARD_DIMENSION_LIMIT_REACHED = 2; +const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3; +const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4; +const int FIELD_ID_BAD_VALUE_TYPE = 5; +const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6; namespace { @@ -456,6 +468,32 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> (long long)pair.second.pullTimeout); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY, (long long)pair.second.pullExceedMaxDelay); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED, + (long long)pair.second.pullFailed); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED, + (long long)pair.second.statsCompanionPullFailed); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED, + (long long)pair.second.statsCompanionPullBinderTransactionFailed); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, + (long long)pair.second.emptyData); + protoOutput->end(token); +} + +void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, + util::ProtoOutputStream *protoOutput) { + uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS | + FIELD_COUNT_REPEATED); + protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, (int32_t)pair.first); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED, + (long long)pair.second.hardDimensionLimitReached); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED, + (long long)pair.second.lateLogEventSkipped); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS, + (long long)pair.second.skippedForwardBuckets); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE, + (long long)pair.second.badValueType); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET, + (long long)pair.second.conditionChangeInNextBucket); protoOutput->end(token); } diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index 61f31eb3fa17..dcea0e653e22 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -73,6 +73,10 @@ int64_t MillisToNano(const int64_t millis); void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair, util::ProtoOutputStream* protoOutput); +// Helper function to write AtomMetricStats to ProtoOutputStream +void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair, + util::ProtoOutputStream *protoOutput); + template<class T> bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { std::string pbBytes; diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index ce5d8a5cfeb2..eae7d6b79f67 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -28,15 +28,12 @@ import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; -import android.hardware.input.InputManager; import android.os.RemoteException; import android.os.UserHandle; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.IWindowManager; -import android.view.InputDevice; -import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceSession; @@ -51,9 +48,7 @@ import dalvik.system.CloseGuard; import java.util.List; /** - * Activity container that allows launching activities into itself and does input forwarding. - * <p>Creation of this view is only allowed to callers who have - * {@link android.Manifest.permission#INJECT_EVENTS} permission. + * Activity container that allows launching activities into itself. * <p>Activity launching into this container is restricted by the same rules that apply to launching * on VirtualDisplays. * @hide @@ -76,9 +71,8 @@ public class ActivityView extends ViewGroup { private StateCallback mActivityViewCallback; private IActivityTaskManager mActivityTaskManager; - private IInputForwarder mInputForwarder; - // Temp container to store view coordinates on screen. - private final int[] mLocationOnScreen = new int[2]; + // Temp container to store view coordinates in window. + private final int[] mLocationInWindow = new int[2]; private TaskStackListener mTaskStackListener; @@ -280,7 +274,7 @@ public class ActivityView extends ViewGroup { } /** - * Triggers an update of {@link ActivityView}'s location on screen to properly set touch exclude + * Triggers an update of {@link ActivityView}'s location in window to properly set touch exclude * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { @@ -295,45 +289,14 @@ public class ActivityView extends ViewGroup { /** Send current location and size to the WM to set tap exclude region for this view. */ private void updateLocation() { try { - getLocationOnScreen(mLocationOnScreen); + getLocationInWindow(mLocationInWindow); WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mLocationOnScreen[0], mLocationOnScreen[1], getWidth(), getHeight()); + mLocationInWindow[0], mLocationInWindow[1], getWidth(), getHeight()); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } - @Override - public boolean onTouchEvent(MotionEvent event) { - return injectInputEvent(event) || super.onTouchEvent(event); - } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { - if (injectInputEvent(event)) { - return true; - } - } - return super.onGenericMotionEvent(event); - } - - private boolean injectInputEvent(MotionEvent event) { - if (mInputForwarder != null) { - try { - // The touch event that the ActivityView gets is in View space, but the event needs - // to get forwarded in screen space. This offsets the touch event by the location - // the ActivityView is on screen and sends it to the input forwarder. - getLocationOnScreen(mLocationOnScreen); - event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]); - return mInputForwarder.forwardEvent(event); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } - } - return false; - } - private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { @@ -416,7 +379,6 @@ public class ActivityView extends ViewGroup { } mTmpTransaction.show(mRootSurfaceControl).apply(); - mInputForwarder = InputManager.getInstance().createInputForwarder(displayId); mTaskStackListener = new TaskStackListenerImpl(); try { mActivityTaskManager.registerTaskStackListener(mTaskStackListener); @@ -432,9 +394,6 @@ public class ActivityView extends ViewGroup { mSurfaceView.getHolder().removeCallback(mSurfaceCallback); - if (mInputForwarder != null) { - mInputForwarder = null; - } cleanTapExcludeRegion(); if (mTaskStackListener != null) { diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index 4644a83de462..d4ed35a9cb71 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -16,10 +16,14 @@ package android.content.rollback; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; +import android.util.IntArray; + +import java.util.ArrayList; /** * Information about a rollback available for a particular package. @@ -33,6 +37,38 @@ public final class PackageRollbackInfo implements Parcelable { private final VersionedPackage mVersionRolledBackTo; /** + * Encapsulates information required to restore a snapshot of an app's userdata. + * + * @hide + */ + public static class RestoreInfo { + public final int userId; + public final int appId; + public final String seInfo; + + public RestoreInfo(int userId, int appId, String seInfo) { + this.userId = userId; + this.appId = appId; + this.seInfo = seInfo; + } + } + + /* + * The list of users for which we need to backup userdata for this package. Backups of + * credential encrypted data are listed as pending if the user hasn't unlocked their device + * with credentials yet. + */ + // NOTE: Not a part of the Parcelable representation of this object. + private final IntArray mPendingBackups; + + /** + * The list of users for which we need to restore userdata for this package. This field is + * non-null only after a rollback for this package has been committed. + */ + // NOTE: Not a part of the Parcelable representation of this object. + private final ArrayList<RestoreInfo> mPendingRestores; + + /** * Returns the name of the package to roll back from. */ public String getPackageName() { @@ -54,15 +90,46 @@ public final class PackageRollbackInfo implements Parcelable { } /** @hide */ + public IntArray getPendingBackups() { + return mPendingBackups; + } + + /** @hide */ + public ArrayList<RestoreInfo> getPendingRestores() { + return mPendingRestores; + } + + /** @hide */ + public RestoreInfo getRestoreInfo(int userId) { + for (RestoreInfo ri : mPendingRestores) { + if (ri.userId == userId) { + return ri; + } + } + + return null; + } + + /** @hide */ + public void removeRestoreInfo(RestoreInfo ri) { + mPendingRestores.remove(ri); + } + + /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, - VersionedPackage packageRolledBackTo) { + VersionedPackage packageRolledBackTo, + @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores) { this.mVersionRolledBackFrom = packageRolledBackFrom; this.mVersionRolledBackTo = packageRolledBackTo; + this.mPendingBackups = pendingBackups; + this.mPendingRestores = pendingRestores; } private PackageRollbackInfo(Parcel in) { this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in); this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in); + this.mPendingRestores = null; + this.mPendingBackups = null; } @Override diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java index 1111b4320eb9..3fd247670a9d 100644 --- a/core/java/android/content/rollback/RollbackInfo.java +++ b/core/java/android/content/rollback/RollbackInfo.java @@ -83,7 +83,7 @@ public final class RollbackInfo implements Parcelable { * Returns the session ID for the committed rollback for staged rollbacks. * Only applicable for rollbacks that have been committed. */ - public int getSessionId() { + public int getCommittedSessionId() { // TODO: Support rollback of staged installs. return PackageInstaller.SessionInfo.INVALID_ID; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 794c2f1b991b..5151e29e97ff 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10521,8 +10521,10 @@ public final class Settings { /** * Setting to enable connected MAC randomization in Wi-Fi; disabled by default, and * setting to 1 will enable it. In the future, additional values may be supported. + * @deprecated MAC randomization is now a per-network setting * @hide */ + @Deprecated public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED = "wifi_connected_mac_randomization_enabled"; diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index eb41e078e59f..59e562fe8071 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -41,12 +41,14 @@ import android.view.View.AttachInfo; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityRequestPreparer; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import java.util.ArrayList; @@ -64,8 +66,11 @@ import java.util.function.Predicate; * called from the interaction connection ViewAncestor gives the system to * talk to it and a corresponding *UiThread method that is executed on the * UI thread. + * + * @hide */ -final class AccessibilityInteractionController { +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class AccessibilityInteractionController { private static final String LOG_TAG = "AccessibilityInteractionController"; @@ -85,7 +90,7 @@ final class AccessibilityInteractionController { private final Object mLock = new Object(); - private final Handler mHandler; + private final PrivateHandler mHandler; private final ViewRootImpl mViewRootImpl; @@ -131,11 +136,19 @@ final class AccessibilityInteractionController { // thread in this process, set the message as a static reference so // after this call completes the same thread but in the interrogating // client can handle the message to generate the result. - if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId + && mHandler.hasAccessibilityCallback(message)) { AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - mHandler.sendMessage(message); + // For messages without callback of interrogating client, just handle the + // message immediately if this is UI thread. + if (!mHandler.hasAccessibilityCallback(message) + && Thread.currentThread().getId() == mMyLooperThreadId) { + mHandler.handleMessage(message); + } else { + mHandler.sendMessage(message); + } } } } @@ -731,6 +744,52 @@ final class AccessibilityInteractionController { } } + /** + * Finds the accessibility focused node in the root, and clears the accessibility focus. + */ + public void clearAccessibilityFocusClientThread() { + final Message message = mHandler.obtainMessage(); + message.what = PrivateHandler.MSG_CLEAR_ACCESSIBILITY_FOCUS; + + // Don't care about pid and tid because there's no interrogating client for this message. + scheduleMessage(message, 0, 0, CONSIDER_REQUEST_PREPARERS); + } + + private void clearAccessibilityFocusUiThread() { + if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { + return; + } + try { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = + AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; + final View root = mViewRootImpl.mView; + if (root != null && isShown(root)) { + final View host = mViewRootImpl.mAccessibilityFocusedHost; + // If there is no accessibility focus host or it is not a descendant + // of the root from which to start the search, then the search failed. + if (host == null || !ViewRootImpl.isViewDescendantOf(host, root)) { + return; + } + final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); + final AccessibilityNodeInfo focusNode = + mViewRootImpl.mAccessibilityFocusedVirtualView; + if (provider != null && focusNode != null) { + final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( + focusNode.getSourceNodeId()); + provider.performAction(virtualNodeId, + AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), + null); + } else { + host.performAccessibilityAction( + AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS.getId(), + null); + } + } + } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + } + } + private View findViewByAccessibilityId(int accessibilityId) { View root = mViewRootImpl.mView; if (root == null) { @@ -1294,6 +1353,12 @@ final class AccessibilityInteractionController { private static final int MSG_APP_PREPARATION_FINISHED = 8; private static final int MSG_APP_PREPARATION_TIMEOUT = 9; + // Uses FIRST_NO_ACCESSIBILITY_CALLBACK_MSG for messages that don't need to call back + // results to interrogating client. + private static final int FIRST_NO_ACCESSIBILITY_CALLBACK_MSG = 100; + private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = + FIRST_NO_ACCESSIBILITY_CALLBACK_MSG + 1; + public PrivateHandler(Looper looper) { super(looper); } @@ -1320,6 +1385,8 @@ final class AccessibilityInteractionController { return "MSG_APP_PREPARATION_FINISHED"; case MSG_APP_PREPARATION_TIMEOUT: return "MSG_APP_PREPARATION_TIMEOUT"; + case MSG_CLEAR_ACCESSIBILITY_FOCUS: + return "MSG_CLEAR_ACCESSIBILITY_FOCUS"; default: throw new IllegalArgumentException("Unknown message type: " + type); } @@ -1356,10 +1423,17 @@ final class AccessibilityInteractionController { case MSG_APP_PREPARATION_TIMEOUT: { requestPreparerTimeoutUiThread(); } break; + case MSG_CLEAR_ACCESSIBILITY_FOCUS: { + clearAccessibilityFocusUiThread(); + } break; default: throw new IllegalArgumentException("Unknown message type: " + type); } } + + boolean hasAccessibilityCallback(Message message) { + return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false; + } } private final class AddNodeInfosForViewId implements Predicate<View> { diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 92e0009bc21a..ec79eea45ee6 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -16,10 +16,10 @@ package android.view; +import static android.view.Display.INVALID_DISPLAY; + import android.graphics.Region; import android.os.IBinder; -import android.view.IWindow; -import android.view.InputChannel; /** * Functions as a handle for a window that can receive input. @@ -94,6 +94,10 @@ public final class InputWindowHandle { // Display this input is on. public int displayId; + // If this value is set to a valid display ID, it indicates this window is a portal which + // transports the touch of this window to the display indicated by portalToDisplayId. + public int portalToDisplayId = INVALID_DISPLAY; + private native void nativeDispose(); public InputWindowHandle(InputApplicationHandle inputApplicationHandle, diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f47eb10efd29..9213f32326b4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8718,6 +8718,15 @@ public final class ViewRootImpl implements ViewParent, } } } + + @Override + public void clearAccessibilityFocus() { + ViewRootImpl viewRootImpl = mViewRootImpl.get(); + if (viewRootImpl != null && viewRootImpl.mView != null) { + viewRootImpl.getAccessibilityInteractionController() + .clearAccessibilityFocusClientThread(); + } + } } private class SendWindowContentChangedAccessibilityEvent implements Runnable { diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index 4c0fdfd5afdf..947ff056627e 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -55,4 +55,6 @@ oneway interface IAccessibilityInteractionConnection { void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid); + + void clearAccessibilityFocus(); } diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java index afe6dade62ac..6ba584d76ee8 100644 --- a/core/java/com/android/internal/os/AppZygoteInit.java +++ b/core/java/com/android/internal/os/AppZygoteInit.java @@ -73,6 +73,9 @@ class AppZygoteInit { Log.i(TAG, "Beginning application preload for " + appInfo.packageName); LoadedApk loadedApk = new LoadedApk(null, appInfo, null, null, false, true, false); ClassLoader loader = loadedApk.getClassLoader(); + + Zygote.allowAppFilesAcrossFork(appInfo); + Class<?> cl; Method m; try { diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index c8d30b27d4dc..9ed738406707 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -78,18 +78,7 @@ class WebViewZygoteInit { ClassLoader loader = loadedApk.getClassLoader(); doPreload(loader, WebViewFactory.getWebViewLibrary(appInfo)); - // Add the APK to the Zygote's list of allowed files for children. - Zygote.nativeAllowFileAcrossFork(appInfo.sourceDir); - if (appInfo.splitSourceDirs != null) { - for (String path : appInfo.splitSourceDirs) { - Zygote.nativeAllowFileAcrossFork(path); - } - } - if (appInfo.sharedLibraryFiles != null) { - for (String path : appInfo.sharedLibraryFiles) { - Zygote.nativeAllowFileAcrossFork(path); - } - } + Zygote.allowAppFilesAcrossFork(appInfo); Log.i(TAG, "Application preload done"); } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index bc7cf8755786..474d4d7cd90e 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -20,6 +20,7 @@ import static android.system.OsConstants.O_CLOEXEC; import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC; +import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; @@ -370,6 +371,27 @@ public final class Zygote { protected static native void nativeAllowFileAcrossFork(String path); /** + * Lets children of the zygote inherit open file descriptors that belong to the + * ApplicationInfo that is passed in. + * + * @param appInfo ApplicationInfo of the application + */ + protected static void allowAppFilesAcrossFork(ApplicationInfo appInfo) { + Zygote.nativeAllowFileAcrossFork(appInfo.sourceDir); + if (appInfo.splitSourceDirs != null) { + for (String path : appInfo.splitSourceDirs) { + Zygote.nativeAllowFileAcrossFork(path); + } + } + // As well as its shared libs + if (appInfo.sharedLibraryFiles != null) { + for (String path : appInfo.sharedLibraryFiles) { + Zygote.nativeAllowFileAcrossFork(path); + } + } + } + + /** * Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range * @param uidGidMin The smallest allowed uid/gid * @param uidGidMax The largest allowed uid/gid diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index c0e45b12cf76..67a74419e771 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -55,6 +55,7 @@ static struct { jfieldID ownerUid; jfieldID inputFeatures; jfieldID displayId; + jfieldID portalToDisplayId; } gInputWindowHandleClassInfo; static Mutex gHandleMutex; @@ -154,6 +155,8 @@ bool NativeInputWindowHandle::updateInfo() { gInputWindowHandleClassInfo.inputFeatures); mInfo.displayId = env->GetIntField(obj, gInputWindowHandleClassInfo.displayId); + mInfo.portalToDisplayId = env->GetIntField(obj, + gInputWindowHandleClassInfo.portalToDisplayId); jobject inputApplicationHandleObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.inputApplicationHandle); @@ -307,6 +310,9 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz, "displayId", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz, + "portalToDisplayId", "I"); return 0; } diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index e0eaf14d24c8..712994bb0d14 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -999,7 +999,7 @@ message GlobalSettingsProto { optional SettingProto watchdog_poor_network_test_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto suspend_optimizations_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto verbose_logging_enabled = 24 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto connected_mac_randomization_enabled = 25 [ (android.privacy).dest = DEST_AUTOMATIC ]; + reserved 25; // connected_mac_randomization_enabled optional SettingProto max_dhcp_retry_count = 26 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto mobile_data_transition_wakelock_timeout_ms = 27 [ (android.privacy).dest = DEST_AUTOMATIC ]; // Controls whether WiFi configurations created by a Device Owner app should diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 86818c611c1f..fffee903b3f0 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1144,6 +1144,14 @@ </intent-filter> </activity> + <activity android:name="android.view.accessibility.AccessibilityTestActivity" + android:label="AccessibilityTestActivity" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + <!-- Activity-level metadata --> <meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" /> <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" /> diff --git a/core/tests/coretests/res/layout/accessibility_test.xml b/core/tests/coretests/res/layout/accessibility_test.xml new file mode 100644 index 000000000000..1bdd21b2acc8 --- /dev/null +++ b/core/tests/coretests/res/layout/accessibility_test.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button android:id="@+id/appNameBtn" + android:text="@string/app_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="60dp" + android:bufferType="normal"> + </Button> +</LinearLayout> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 0b5bde767ac9..e32b41223d94 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -509,7 +509,6 @@ public class SettingsBackupTest { Settings.Global.WIFI_ALWAYS_REQUESTED, Settings.Global.WIFI_BADGING_THRESHOLDS, Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS, - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, Settings.Global.WIFI_COUNTRY_CODE, Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD, Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX, diff --git a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java new file mode 100644 index 000000000000..d0719cbea86e --- /dev/null +++ b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Activity; +import android.app.Instrumentation; +import android.app.Service; +import android.app.UiAutomation; +import android.graphics.Rect; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.text.TextUtils; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityTestActivity; +import android.view.accessibility.AccessibilityWindowInfo; + +import com.android.compatibility.common.util.TestUtils; +import com.android.frameworks.coretests.R; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class AccessibilityInteractionControllerTest { + static final long TIMEOUT_DEFAULT = 10000; // 10 seconds + + private static Instrumentation sInstrumentation; + private static UiAutomation sUiAutomation; + + @Rule + public ActivityTestRule<AccessibilityTestActivity> mActivityRule = new ActivityTestRule<>( + AccessibilityTestActivity.class, false, false); + + private AccessibilityInteractionController mAccessibilityInteractionController; + private ViewRootImpl mViewRootImpl; + private View mButton; + + @BeforeClass + public static void oneTimeSetup() { + sInstrumentation = InstrumentationRegistry.getInstrumentation(); + sUiAutomation = sInstrumentation.getUiAutomation(); + } + + @AfterClass + public static void postTestTearDown() { + sUiAutomation.destroy(); + } + + @Before + public void setUp() throws Throwable { + launchActivity(); + enableTouchExploration(true); + mActivityRule.runOnUiThread(() -> { + mViewRootImpl = mActivityRule.getActivity().getWindow().getDecorView() + .getViewRootImpl(); + mButton = mActivityRule.getActivity().findViewById(R.id.appNameBtn); + }); + mAccessibilityInteractionController = + mViewRootImpl.getAccessibilityInteractionController(); + } + + @After + public void tearDown() { + enableTouchExploration(false); + } + + @Test + public void clearAccessibilityFocus_shouldClearFocus() throws Exception { + performAccessibilityFocus("com.android.frameworks.coretests:id/appNameBtn"); + assertTrue("Button should have a11y focus", + mButton.isAccessibilityFocused()); + mAccessibilityInteractionController.clearAccessibilityFocusClientThread(); + sInstrumentation.waitForIdleSync(); + assertFalse("Button should not have a11y focus", + mButton.isAccessibilityFocused()); + } + + @Test + public void clearAccessibilityFocus_uiThread_shouldClearFocus() throws Exception { + performAccessibilityFocus("com.android.frameworks.coretests:id/appNameBtn"); + assertTrue("Button should have a11y focus", + mButton.isAccessibilityFocused()); + sInstrumentation.runOnMainSync(() -> { + mAccessibilityInteractionController.clearAccessibilityFocusClientThread(); + }); + assertFalse("Button should not have a11y focus", + mButton.isAccessibilityFocused()); + } + + private void launchActivity() { + final Object waitObject = new Object(); + final int[] location = new int[2]; + final StringBuilder activityPackage = new StringBuilder(); + final Rect bounds = new Rect(); + final StringBuilder activityTitle = new StringBuilder(); + try { + final long executionStartTimeMillis = SystemClock.uptimeMillis(); + sUiAutomation.setOnAccessibilityEventListener((event) -> { + if (event.getEventTime() < executionStartTimeMillis) { + return; + } + synchronized (waitObject) { + waitObject.notifyAll(); + } + }); + enableRetrieveAccessibilityWindows(); + + final Activity activity = mActivityRule.launchActivity(null); + sInstrumentation.runOnMainSync(() -> { + activity.getWindow().getDecorView().getLocationOnScreen(location); + activityPackage.append(activity.getPackageName()); + activityTitle.append(activity.getTitle()); + }); + sInstrumentation.waitForIdleSync(); + + TestUtils.waitOn(waitObject, () -> { + final AccessibilityWindowInfo window = findWindowByTitle(activityTitle); + if (window == null) return false; + window.getBoundsInScreen(bounds); + activity.getWindow().getDecorView().getLocationOnScreen(location); + if (bounds.isEmpty()) { + return false; + } + return (!bounds.isEmpty()) + && (bounds.left == location[0]) && (bounds.top == location[1]); + }, TIMEOUT_DEFAULT, "Launch Activity"); + } finally { + sUiAutomation.setOnAccessibilityEventListener(null); + } + } + + private void enableRetrieveAccessibilityWindows() { + AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); + info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; + sUiAutomation.setServiceInfo(info); + } + + private void enableTouchExploration(boolean enabled) { + final Object waitObject = new Object(); + final AccessibilityManager accessibilityManager = + (AccessibilityManager) sInstrumentation.getContext().getSystemService( + Service.ACCESSIBILITY_SERVICE); + final AccessibilityManager.TouchExplorationStateChangeListener listener = status -> { + synchronized (waitObject) { + waitObject.notifyAll(); + } + }; + try { + accessibilityManager.addTouchExplorationStateChangeListener(listener); + final AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); + if (enabled) { + info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; + } else { + info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; + } + sUiAutomation.setServiceInfo(info); + TestUtils.waitOn(waitObject, + () -> accessibilityManager.isTouchExplorationEnabled() == enabled, + TIMEOUT_DEFAULT, + (enabled ? "Enable" : "Disable") + "touch exploration"); + } finally { + accessibilityManager.removeTouchExplorationStateChangeListener(listener); + } + } + + private void performAccessibilityFocus(String viewId) throws TimeoutException { + final AccessibilityNodeInfo node = sUiAutomation.getRootInActiveWindow() + .findAccessibilityNodeInfosByViewId(viewId).get(0); + // Perform an action and wait for an event + sUiAutomation.executeAndWaitForEvent( + () -> node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS), + event -> event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, + TIMEOUT_DEFAULT); + node.refresh(); + } + + private AccessibilityWindowInfo findWindowByTitle(CharSequence title) { + final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); + AccessibilityWindowInfo returnValue = null; + for (int i = 0; i < windows.size(); i++) { + final AccessibilityWindowInfo window = windows.get(i); + if (TextUtils.equals(title, window.getTitle())) { + returnValue = window; + } else { + window.recycle(); + } + } + return returnValue; + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityTestActivity.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityTestActivity.java new file mode 100644 index 000000000000..a17fa7326a5d --- /dev/null +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityTestActivity.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Context; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +/** + * An activity for accessibility test. + */ +public class AccessibilityTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + turnOnScreen(); + super.onCreate(savedInstanceState); + setContentView(R.layout.accessibility_test); + } + + private void turnOnScreen() { + setTurnScreenOn(true); + setShowWhenLocked(true); + KeyguardManager keyguardManager = (KeyguardManager) getSystemService( + Context.KEYGUARD_SERVICE); + keyguardManager.requestDismissKeyguard(this, null); + } +} diff --git a/media/Android.bp b/media/Android.bp index 3181a2988d99..e7d5faf52d69 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -66,7 +66,9 @@ filegroup { filegroup { name: "media1-srcs", srcs: [ + "apex/java/android/media/MediaDescription.java", "apex/java/android/media/MediaParceledListSlice.java", + "apex/java/android/media/Rating.java", "apex/java/android/media/VolumeProvider.java", "apex/java/android/media/browse/MediaBrowser.java", "apex/java/android/media/browse/MediaBrowserUtils.java", diff --git a/media/java/android/media/MediaDescription.aidl b/media/apex/java/android/media/MediaDescription.aidl index 6f934f75aa28..6f934f75aa28 100644 --- a/media/java/android/media/MediaDescription.aidl +++ b/media/apex/java/android/media/MediaDescription.aidl diff --git a/media/java/android/media/MediaDescription.java b/media/apex/java/android/media/MediaDescription.java index 31079e5c54d4..39eeb3eae49f 100644 --- a/media/java/android/media/MediaDescription.java +++ b/media/apex/java/android/media/MediaDescription.java @@ -1,3 +1,19 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.media; import android.annotation.Nullable; @@ -226,7 +242,7 @@ public class MediaDescription implements Parcelable { return false; } - if (!(o instanceof MediaDescription)){ + if (!(o instanceof MediaDescription)) { return false; } @@ -375,6 +391,11 @@ public class MediaDescription implements Parcelable { return this; } + /** + * Build {@link MediaDescription}. + * + * @return a new media description. + */ public MediaDescription build() { return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri, mExtras, mMediaUri); diff --git a/media/java/android/media/Rating.aidl b/media/apex/java/android/media/Rating.aidl index 1dc336aeb79d..1dc336aeb79d 100644 --- a/media/java/android/media/Rating.aidl +++ b/media/apex/java/android/media/Rating.aidl diff --git a/media/java/android/media/Rating.java b/media/apex/java/android/media/Rating.java index 04d5364f7c81..ffe7e48f25e4 100644 --- a/media/java/android/media/Rating.java +++ b/media/apex/java/android/media/Rating.java @@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy; * through one of the factory methods. */ public final class Rating implements Parcelable { - private final static String TAG = "Rating"; + private static final String TAG = "Rating"; /** * @hide @@ -55,40 +55,40 @@ public final class Rating implements Parcelable { * type, but can be used by other classes to indicate they do not support * Rating. */ - public final static int RATING_NONE = 0; + public static final int RATING_NONE = 0; /** * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to * indicate the content referred to is a favorite (or not). */ - public final static int RATING_HEART = 1; + public static final int RATING_HEART = 1; /** * A rating style for "thumb up" vs "thumb down". */ - public final static int RATING_THUMB_UP_DOWN = 2; + public static final int RATING_THUMB_UP_DOWN = 2; /** * A rating style with 0 to 3 stars. */ - public final static int RATING_3_STARS = 3; + public static final int RATING_3_STARS = 3; /** * A rating style with 0 to 4 stars. */ - public final static int RATING_4_STARS = 4; + public static final int RATING_4_STARS = 4; /** * A rating style with 0 to 5 stars. */ - public final static int RATING_5_STARS = 5; + public static final int RATING_5_STARS = 5; /** * A rating style expressed as a percentage. */ - public final static int RATING_PERCENTAGE = 6; + public static final int RATING_PERCENTAGE = 6; - private final static float RATING_NOT_RATED = -1.0f; + private static final float RATING_NOT_RATED = -1.0f; private final int mRatingStyle; @@ -116,8 +116,7 @@ public final class Rating implements Parcelable { dest.writeFloat(mRatingValue); } - public static final Parcelable.Creator<Rating> CREATOR - = new Parcelable.Creator<Rating>() { + public static final Parcelable.Creator<Rating> CREATOR = new Parcelable.Creator<Rating>() { /** * Rebuilds a Rating previously stored with writeToParcel(). * @param p Parcel object to read the Rating from @@ -205,7 +204,7 @@ public final class Rating implements Parcelable { break; default: Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); - return null; + return null; } if ((starRating < 0.0f) || (starRating > maxRating)) { Log.e(TAG, "Trying to set out of range star-based rating"); @@ -281,16 +280,16 @@ public final class Rating implements Parcelable { * not star-based, or if it is unrated. */ public float getStarRating() { + float ratingValue = -1.0f; switch (mRatingStyle) { case RATING_3_STARS: case RATING_4_STARS: case RATING_5_STARS: if (isRated()) { - return mRatingValue; + ratingValue = mRatingValue; } - default: - return -1.0f; } + return ratingValue; } /** diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml index 010a810cd791..7fb51b9496b9 100644 --- a/packages/ExtServices/AndroidManifest.xml +++ b/packages/ExtServices/AndroidManifest.xml @@ -22,6 +22,7 @@ coreApp="true"> <uses-permission android:name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application android:label="@string/app_name" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index f523481e61d4..ec70f8c32bcb 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -618,8 +618,6 @@ <string name="wifi_display_certification">Wireless display certification</string> <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] --> <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string> - <!-- Setting Checkbox title whether to enable connected MAC randomization --> - <string name="wifi_connected_mac_randomization">Connected MAC Randomization</string> <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] --> <string name="mobile_data_always_on">Mobile data always active</string> <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] --> @@ -675,8 +673,6 @@ <string name="wifi_display_certification_summary">Show options for wireless display certification</string> <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] --> <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string> - <!-- Setting Checkbox title whether to enable connected MAC randomization --> - <string name="wifi_connected_mac_randomization_summary">Randomize MAC address when connecting to Wi\u2011Fi networks</string> <!-- Label indicating network has been manually marked as metered --> <string name="wifi_metered_label">Metered</string> <!-- Label indicating network has been manually marked as unmetered --> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 3877c90fee23..6ca8261ac4f9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1511,9 +1511,6 @@ class SettingsProtoDumpUtil { Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, GlobalSettingsProto.Wifi.VERBOSE_LOGGING_ENABLED); dumpSetting(s, p, - Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, - GlobalSettingsProto.Wifi.CONNECTED_MAC_RANDOMIZATION_ENABLED); - dumpSetting(s, p, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, GlobalSettingsProto.Wifi.MAX_DHCP_RETRY_COUNT); dumpSetting(s, p, diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java index 968bd283c4b1..60dceef23f7c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java @@ -165,6 +165,11 @@ public class PipAccessibilityInteractionConnection } } + @Override + public void clearAccessibilityFocus() { + // We should not be here. + } + public static AccessibilityNodeInfo obtainRootAccessibilityNodeInfo() { AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); info.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index f88d5217ab2f..47cd917bf84e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3024,9 +3024,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } public void clearAccessibilityFocusNotLocked(int windowId) { - AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked(windowId); - if (focus != null) { - focus.performAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); + RemoteAccessibilityConnection connection; + synchronized (mLock) { + connection = getConnectionLocked(windowId); + if (connection == null) { + return; + } + } + try { + connection.getRemote().clearAccessibilityFocus(); + } catch (RemoteException re) { + if (DEBUG) { + Slog.e(LOG_TAG, "Error calling clearAccessibilityFocus()"); + } } } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 84577f125f1d..4507193a5aff 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.VersionedPackage; import android.os.Environment; import android.os.Handler; import android.os.Looper; @@ -230,7 +231,6 @@ public class PackageWatchdog { return null; } - // TODO(zezeozue:) Accept current versionCodes of failing packages? /** * Called when a process fails either due to a crash or ANR. * @@ -239,15 +239,16 @@ public class PackageWatchdog { * * <p>This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(String[] packages) { + public void onPackageFailure(List<VersionedPackage> packages) { mWorkerHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { return; } - for (int pIndex = 0; pIndex < packages.length; pIndex++) { - String packageToReport = packages[pIndex]; + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + String packageToReport = packages.get(pIndex).getPackageName(); + long packageVersionCode = packages.get(pIndex).getVersionCode(); // Observer that will receive failure for packageToReport PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; @@ -258,7 +259,8 @@ public class PackageWatchdog { PackageHealthObserver registeredObserver = observer.mRegisteredObserver; if (registeredObserver != null && observer.onPackageFailure(packageToReport)) { - int impact = registeredObserver.onHealthCheckFailed(packageToReport); + int impact = registeredObserver.onHealthCheckFailed(packageToReport, + packageVersionCode); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; @@ -269,7 +271,7 @@ public class PackageWatchdog { // Execute action with least user impact if (currentObserverToNotify != null) { - currentObserverToNotify.execute(packageToReport); + currentObserverToNotify.execute(packageToReport, packageVersionCode); } } } @@ -313,14 +315,14 @@ public class PackageWatchdog { * @return any one of {@link PackageHealthObserverImpact} to express the impact * to the user on {@link #execute} */ - @PackageHealthObserverImpact int onHealthCheckFailed(String packageName); + @PackageHealthObserverImpact int onHealthCheckFailed(String packageName, long versionCdoe); /** * Executes mitigation for {@link #onHealthCheckFailed}. * * @return {@code true} if action was executed successfully, {@code false} otherwise */ - boolean execute(String packageName); + boolean execute(String packageName, long versionCode); // TODO(zezeozue): Ensure uniqueness? /** diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index a634b577f506..f153ab9cfa9e 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -34,6 +34,7 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.VersionedPackage; import android.net.Uri; import android.os.Binder; import android.os.Message; @@ -60,6 +61,7 @@ import com.android.server.wm.WindowProcessController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collections; +import java.util.List; /** * Controls error conditions in applications. @@ -411,7 +413,7 @@ class AppErrors { } else { // If a non-persistent app is stuck in crash loop, we want to inform // the package watchdog, maybe an update or experiment can be rolled back. - mPackageWatchdog.onPackageFailure(r.getPackageList()); + mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode()); } } @@ -830,7 +832,7 @@ class AppErrors { void handleShowAnrUi(Message msg) { Dialog dialogToShow = null; - String[] packageList = null; + List<VersionedPackage> packageList = null; synchronized (mService) { AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj; final ProcessRecord proc = data.proc; @@ -839,7 +841,7 @@ class AppErrors { return; } if (!proc.isPersistent()) { - packageList = proc.getPackageList(); + packageList = proc.getPackageListWithVersionCode(); } if (proc.anrDialog != null) { Slog.e(TAG, "App already has anr dialog: " + proc); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 580d6882a82e..7ae77d5f09dd 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -32,6 +32,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; +import android.content.pm.VersionedPackage; import android.content.res.CompatibilityInfo; import android.os.Binder; import android.os.Debug; @@ -66,6 +67,7 @@ import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Full information about a particular process that @@ -974,6 +976,18 @@ final class ProcessRecord implements WindowProcessListener { return list; } + public List<VersionedPackage> getPackageListWithVersionCode() { + int size = pkgList.size(); + if (size == 0) { + return null; + } + List<VersionedPackage> list = new ArrayList<>(); + for (int i = 0; i < pkgList.size(); i++) { + list.add(new VersionedPackage(pkgList.keyAt(i), pkgList.valueAt(i).appVersion)); + } + return list; + } + WindowProcessController getWindowProcessController() { return mWindowProcessController; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 6f1eeeb7de7a..dc18dfcf8613 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -61,6 +61,10 @@ import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.content.res.AssetManager; import android.content.res.Resources; +import android.content.rollback.IRollbackManager; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -263,6 +267,8 @@ class PackageManagerShellCommand extends ShellCommand { return getStagedSessions(); case "uninstall-system-updates": return uninstallSystemUpdates(); + case "rollback-app": + return runRollbackApp(); default: { String nextArg = getNextArg(); if (nextArg == null) { @@ -348,6 +354,55 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } + private int runRollbackApp() { + final PrintWriter pw = getOutPrintWriter(); + + final String packageName = getNextArgRequired(); + if (packageName == null) { + pw.println("Error: package name not specified"); + return 1; + } + + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + try { + IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + + RollbackInfo rollback = null; + for (RollbackInfo r : (List<RollbackInfo>) rm.getAvailableRollbacks().getList()) { + for (PackageRollbackInfo info : r.getPackages()) { + if (packageName.equals(info.getPackageName())) { + rollback = r; + break; + } + } + } + + if (rollback == null) { + pw.println("No available rollbacks for: " + packageName); + return 1; + } + + rm.commitRollback(rollback.getRollbackId(), + ParceledListSlice.<VersionedPackage>emptyList(), + "com.android.shell", receiver.getIntentSender()); + } catch (RemoteException re) { + // Cannot happen. + } + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + if (status == RollbackManager.STATUS_SUCCESS) { + pw.println("Success"); + return 0; + } else { + pw.println("Failure [" + + result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE) + "]"); + return 1; + } + } + private void setParamsSize(InstallParams params, String inPath) { if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) { final ParcelFileDescriptor fd = openFileForSystem(inPath, "r"); diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java new file mode 100644 index 000000000000..8dd076028b43 --- /dev/null +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2019 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.rollback; + +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.PackageRollbackInfo.RestoreInfo; +import android.content.rollback.RollbackInfo; +import android.os.storage.StorageManager; +import android.util.IntArray; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.Installer; +import com.android.server.pm.Installer.InstallerException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Encapsulates the logic for initiating userdata snapshots and rollbacks via installd. + */ +@VisibleForTesting +// TODO(narayan): Reason about the failure scenarios that involve one or more IPCs to installd +// failing. We need to decide what course of action to take if calls to snapshotAppData or +// restoreAppDataSnapshot fail. +public class AppDataRollbackHelper { + private static final String TAG = "RollbackManager"; + + private final Installer mInstaller; + + public AppDataRollbackHelper(Installer installer) { + mInstaller = installer; + } + + /** + * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers}, + * a specified set of users for whom the package is installed. + * + * @return a list of users for which the snapshot is pending, usually because data for one or + * more users is still credential locked. + */ + public IntArray snapshotAppData(String packageName, int[] installedUsers) { + final IntArray pendingBackups = new IntArray(); + for (int user : installedUsers) { + final int storageFlags; + if (isUserCredentialLocked(user)) { + // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy + // across app user data until the user unlocks their device. + Log.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); + storageFlags = Installer.FLAG_STORAGE_DE; + pendingBackups.add(user); + } else { + storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; + } + + try { + mInstaller.snapshotAppData(packageName, user, storageFlags); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to create app data snapshot for: " + packageName + + ", userId: " + user, ie); + } + } + + return pendingBackups; + } + + /** + * Restores an app data snapshot for a specified package ({@code packageName}, + * {@code rollbackData}) for a specified {@code userId}. + * + * @return {@code true} iff. a change to the {@code rollbackData} has been made. Changes to + * {@code rollbackData} are restricted to the removal or addition of {@code userId} to + * the list of pending backups or restores. + */ + public boolean restoreAppData(String packageName, RollbackData rollbackData, + int userId, int appId, long ceDataInode, String seInfo) { + if (rollbackData == null) { + return false; + } + + if (!rollbackData.inProgress) { + Log.e(TAG, "Request to restore userData for: " + packageName + + ", but no rollback in progress."); + return false; + } + + PackageRollbackInfo packageInfo = RollbackManagerServiceImpl.getPackageRollbackInfo( + rollbackData, packageName); + int storageFlags = Installer.FLAG_STORAGE_DE; + + final IntArray pendingBackups = packageInfo.getPendingBackups(); + final List<RestoreInfo> pendingRestores = packageInfo.getPendingRestores(); + boolean changedRollbackData = false; + + // If we still have a userdata backup pending for this user, it implies that the user + // hasn't unlocked their device between the point of backup and the point of restore, + // so the data cannot have changed. We simply skip restoring CE data in this case. + if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) { + pendingBackups.remove(pendingBackups.indexOf(userId)); + changedRollbackData = true; + } else { + // There's no pending CE backup for this user, which means that we successfully + // managed to backup data for the user, which means we seek to restore it + if (isUserCredentialLocked(userId)) { + // We've encountered a user that hasn't unlocked on a FBE device, so we can't + // copy across app user data until the user unlocks their device. + pendingRestores.add(new RestoreInfo(userId, appId, seInfo)); + changedRollbackData = true; + } else { + // This user has unlocked, we can proceed to restore both CE and DE data. + storageFlags = storageFlags | Installer.FLAG_STORAGE_CE; + } + } + + try { + mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, + seInfo, userId, storageFlags); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); + } + + return changedRollbackData; + } + + /** + * Computes the list of pending backups and restores for {@code userId} given lists of + * available and recent rollbacks. Packages pending backup for the given user are added + * to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores} + * along with their corresponding {@code RestoreInfo}. + * + * @return the list of {@code RollbackData} that have been modified during this computation. + */ + public List<RollbackData> computePendingBackupsAndRestores(int userId, + ArrayList<String> pendingBackupPackages, Map<String, RestoreInfo> pendingRestores, + List<RollbackData> availableRollbacks, List<RollbackInfo> recentRollbacks) { + List<RollbackData> rd = new ArrayList<>(); + // First check with the list of available rollbacks to see whether there are any + // pending backup operations that we've not managed to execute. + for (RollbackData data : availableRollbacks) { + for (PackageRollbackInfo info : data.packages) { + final IntArray pendingBackupUsers = info.getPendingBackups(); + if (pendingBackupUsers != null) { + final int idx = pendingBackupUsers.indexOf(userId); + if (idx != -1) { + pendingBackupPackages.add(info.getPackageName()); + pendingBackupUsers.remove(idx); + if (rd.indexOf(data) == -1) { + rd.add(data); + } + } + } + } + } + + // Then check with the list of recently executed rollbacks to see whether there are + // any rollback operations + for (RollbackInfo data : recentRollbacks) { + for (PackageRollbackInfo info : data.getPackages()) { + final RestoreInfo ri = info.getRestoreInfo(userId); + if (ri != null) { + if (pendingBackupPackages.contains(info.getPackageName())) { + // This implies that the user hasn't unlocked their device between + // the request to backup data for this user and the request to restore + // it, so we do nothing here. + pendingBackupPackages.remove(info.getPackageName()); + } else { + pendingRestores.put(info.getPackageName(), ri); + } + + info.removeRestoreInfo(ri); + } + } + } + + return rd; + } + + /** + * Commits the list of pending backups and restores for a given {@code userId}. + */ + public void commitPendingBackupAndRestoreForUser(int userId, + ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores) { + if (!pendingBackups.isEmpty()) { + for (String packageName : pendingBackups) { + try { + mInstaller.snapshotAppData(packageName, userId, Installer.FLAG_STORAGE_CE); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); + } + } + } + + // TODO(narayan): Should we perform the restore before the backup for packages that have + // both backups and restores pending ? We could get into this case if we have a pending + // restore from a rollback + a snapshot request from a new restore. + if (!pendingRestores.isEmpty()) { + for (String packageName : pendingRestores.keySet()) { + try { + final RestoreInfo ri = pendingRestores.get(packageName); + + // TODO(narayan): Verify that the user of "0" for ceDataInode is accurate + // here. We know that the user has unlocked (and that their CE data is + // available) so we shouldn't need to resort to the fallback path. + mInstaller.restoreAppDataSnapshot(packageName, ri.appId, + 0 /* ceDataInode */, ri.seInfo, userId, Installer.FLAG_STORAGE_CE); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to restore app data snapshot for: " + packageName, ie); + } + } + } + } + + /** + * @return {@code true} iff. {@code userId} is locked on an FBE device. + */ + @VisibleForTesting + public boolean isUserCredentialLocked(int userId) { + return StorageManager.isFileEncryptedNativeOrEmulated() + && !StorageManager.isUserKeyUnlocked(userId); + } +} diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java index 4b5e76497f62..ba6cdddb4cc8 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerService.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java @@ -39,4 +39,9 @@ public final class RollbackManagerService extends SystemService { mService = new RollbackManagerServiceImpl(getContext()); publishBinderService(Context.ROLLBACK_SERVICE, mService); } + + @Override + public void onUnlockUser(int user) { + mService.onUnlockUser(user); + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 289618eb133c..5eb137b4c50c 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -31,6 +31,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; @@ -39,14 +40,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; -import android.os.storage.StorageManager; +import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.pm.Installer; -import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageManagerServiceUtils; import java.io.File; @@ -111,6 +111,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final HandlerThread mHandlerThread; private final Installer mInstaller; private final RollbackPackageHealthObserver mPackageHealthObserver; + private final AppDataRollbackHelper mUserdataHelper; RollbackManagerServiceImpl(Context context) { mContext = context; @@ -124,6 +125,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback")); mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); + mUserdataHelper = new AppDataRollbackHelper(mInstaller); // Kick off loading of the rollback data from strorage in a background // thread. @@ -424,6 +426,35 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } + void onUnlockUser(int userId) { + getHandler().post(() -> { + final ArrayList<String> pendingBackupPackages = new ArrayList<>(); + final Map<String, RestoreInfo> pendingRestorePackages = new HashMap<>(); + final List<RollbackData> changed; + synchronized (mLock) { + ensureRollbackDataLoadedLocked(); + changed = mUserdataHelper.computePendingBackupsAndRestores(userId, + pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks, + mRecentlyExecutedRollbacks); + } + + mUserdataHelper.commitPendingBackupAndRestoreForUser(userId, + pendingBackupPackages, pendingRestorePackages); + + for (RollbackData rd : changed) { + try { + mRollbackStore.saveAvailableRollback(rd); + } catch (IOException ioe) { + Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + } + } + + synchronized (mLock) { + mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); + } + }); + } + /** * Load rollback data from storage if it has not already been loaded. * After calling this funciton, mAvailableRollbacks and @@ -533,6 +564,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // that are necessary to keep track of. synchronized (mLock) { ensureRollbackDataLoadedLocked(); + + // This should never happen because we can't have any pending backups left after + // a rollback has been executed. See AppDataRollbackHelper#restoreAppData where we + // clear all pending backups at the point of restore because they're guaranteed to be + // no-ops. + // + // We may, however, have one or more pending restores left to handle. + for (PackageRollbackInfo target : rollback.getPackages()) { + if (target.getPendingBackups().size() > 0) { + Log.e(TAG, "No backups allowed to be pending for: " + target); + target.getPendingBackups().clear(); + } + } + mRecentlyExecutedRollbacks.add(rollback); mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks); } @@ -701,27 +746,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, installedPackage.getLongVersionCode()); - for (int user : installedUsers) { - final int storageFlags; - if (StorageManager.isFileEncryptedNativeOrEmulated() - && !StorageManager.isUserKeyUnlocked(user)) { - // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy - // across app user data until the user unlocks their device. - Log.e(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); - storageFlags = Installer.FLAG_STORAGE_DE; - } else { - storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; - } - - try { - mInstaller.snapshotAppData(packageName, user, storageFlags); - } catch (InstallerException ie) { - Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie); - } - } - PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion); + final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName, + installedUsers); + PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, + pendingBackups, new ArrayList<>()); RollbackData data; try { synchronized (mLock) { @@ -760,40 +790,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } getHandler().post(() -> { - PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); final RollbackData rollbackData = getRollbackForPackage(packageName); - if (rollbackData == null) { - pmi.finishPackageInstall(token, false); - return; - } - - if (!rollbackData.inProgress) { - Log.e(TAG, "Request to restore userData for: " + packageName - + ", but no rollback in progress."); - pmi.finishPackageInstall(token, false); - return; - } - - final int storageFlags; - if (StorageManager.isFileEncryptedNativeOrEmulated() - && !StorageManager.isUserKeyUnlocked(userId)) { - // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy - // across app user data until the user unlocks their device. - Log.e(TAG, "User: " + userId + " isn't unlocked, skipping CE userdata restore."); - - storageFlags = Installer.FLAG_STORAGE_DE; - } else { - storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; - } + final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName, + rollbackData, userId, appId, ceDataInode, seInfo); + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + pmi.finishPackageInstall(token, false); - try { - mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, - seInfo, userId, storageFlags); - } catch (InstallerException ie) { - Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); + // We've updated metadata about this rollback, so save it to flash. + if (changedRollbackData) { + try { + mRollbackStore.saveAvailableRollback(rollbackData); + } catch (IOException ioe) { + // TODO(narayan): What is the right thing to do here ? This isn't a fatal error, + // since it will only result in us trying to restore data again, which will be + // a no-op if there's no data available. + Log.e(TAG, "Unable to save available rollback: " + packageName, ioe); + } } - - pmi.finishPackageInstall(token, false); }); } @@ -900,10 +914,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); - for (PackageRollbackInfo info : data.packages) { - if (info.getPackageName().equals(packageName)) { - return data; - } + if (getPackageRollbackInfo(data, packageName) != null) { + return data; } } } @@ -926,6 +938,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } } + + return null; + } + + /** + * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from + * a specified {@code RollbackData}. + */ + static PackageRollbackInfo getPackageRollbackInfo(RollbackData data, + String packageName) { + for (PackageRollbackInfo info : data.packages) { + if (info.getPackageName().equals(packageName)) { + return info; + } + } + return null; } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 28801038ebd1..f50e776ad22d 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -54,8 +54,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public int onHealthCheckFailed(String packageName) { - RollbackInfo rollback = getAvailableRollback(packageName); + public int onHealthCheckFailed(String packageName, long versionCode) { + RollbackInfo rollback = getAvailableRollback(packageName, versionCode); if (rollback == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; @@ -65,8 +65,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean execute(String packageName) { - RollbackInfo rollback = getAvailableRollback(packageName); + public boolean execute(String packageName, long versionCode) { + RollbackInfo rollback = getAvailableRollback(packageName, versionCode); if (rollback == null) { // Expected a rollback to be available, what happened? return false; @@ -110,11 +110,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } - private RollbackInfo getAvailableRollback(String packageName) { + private RollbackInfo getAvailableRollback(String packageName, long versionCode) { for (RollbackInfo rollback : mRollbackManager.getAvailableRollbacks()) { for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - if (packageName.equals(packageRollback.getPackageName())) { - // TODO(zezeozue): Only rollback if rollback version == failed package version + if (packageName.equals(packageRollback.getPackageName()) + && packageRollback.getVersionRolledBackFrom().getVersionCode() + == versionCode) { return rollback; } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 98ebb0943f43..c70f47dc5ed3 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -16,9 +16,12 @@ package com.android.server.rollback; +import android.annotation.NonNull; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; +import android.util.IntArray; import android.util.Log; import libcore.io.IoUtils; @@ -99,6 +102,64 @@ class RollbackStore { } /** + * Converts an {@code JSONArray} of integers to an {@code IntArray}. + */ + private static @NonNull IntArray convertToIntArray(@NonNull JSONArray jsonArray) + throws JSONException { + if (jsonArray.length() == 0) { + return new IntArray(); + } + + final int[] ret = new int[jsonArray.length()]; + for (int i = 0; i < ret.length; ++i) { + ret[i] = jsonArray.getInt(i); + } + + return IntArray.wrap(ret); + } + + /** + * Converts an {@code IntArray} into an {@code JSONArray} of integers. + */ + private static @NonNull JSONArray convertToJsonArray(@NonNull IntArray intArray) { + JSONArray jsonArray = new JSONArray(); + for (int i = 0; i < intArray.size(); ++i) { + jsonArray.put(intArray.get(i)); + } + + return jsonArray; + } + + private static @NonNull JSONArray convertToJsonArray(@NonNull List<RestoreInfo> list) + throws JSONException { + JSONArray jsonArray = new JSONArray(); + for (RestoreInfo ri : list) { + JSONObject jo = new JSONObject(); + jo.put("userId", ri.userId); + jo.put("appId", ri.appId); + jo.put("seInfo", ri.seInfo); + jsonArray.put(jo); + } + + return jsonArray; + } + + private static @NonNull ArrayList<RestoreInfo> convertToRestoreInfoArray( + @NonNull JSONArray array) throws JSONException { + ArrayList<RestoreInfo> restoreInfos = new ArrayList<>(); + + for (int i = 0; i < array.length(); ++i) { + JSONObject jo = array.getJSONObject(i); + restoreInfos.add(new RestoreInfo( + jo.getInt("userId"), + jo.getInt("appId"), + jo.getString("seInfo"))); + } + + return restoreInfos; + } + + /** * Reads the list of recently executed rollbacks from persistent storage. */ List<RollbackInfo> loadRecentlyExecutedRollbacks() { @@ -239,6 +300,12 @@ class RollbackStore { JSONObject json = new JSONObject(); json.put("versionRolledBackFrom", toJson(info.getVersionRolledBackFrom())); json.put("versionRolledBackTo", toJson(info.getVersionRolledBackTo())); + + IntArray pendingBackups = info.getPendingBackups(); + List<RestoreInfo> pendingRestores = info.getPendingRestores(); + json.put("pendingBackups", convertToJsonArray(pendingBackups)); + json.put("pendingRestores", convertToJsonArray(pendingRestores)); + return json; } @@ -247,7 +314,14 @@ class RollbackStore { json.getJSONObject("versionRolledBackFrom")); VersionedPackage versionRolledBackTo = versionedPackageFromJson( json.getJSONObject("versionRolledBackTo")); - return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo); + + final IntArray pendingBackups = convertToIntArray( + json.getJSONArray("pendingBackups")); + final ArrayList<RestoreInfo> pendingRestores = convertToRestoreInfoArray( + json.getJSONArray("pendingRestores")); + + return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo, + pendingBackups, pendingRestores); } private JSONArray versionedPackagesToJson(List<VersionedPackage> packages) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3426ba611c86..a8492fbb4160 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -29,6 +29,7 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; +import static android.view.Display.INVALID_DISPLAY; import static android.view.InsetsState.TYPE_IME; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; @@ -43,6 +44,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; @@ -140,9 +142,11 @@ import android.graphics.RectF; import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.display.DisplayManagerInternal; +import android.os.Binder; import android.os.Debug; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -157,6 +161,7 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputWindowHandle; import android.view.InsetsState.InternalInsetType; import android.view.MagnificationSpec; import android.view.RemoteAnimationDefinition; @@ -515,6 +520,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final InsetsStateController mInsetsStateController; + private SurfaceControl mParentSurfaceControl; + private InputWindowHandle mPortalWindowHandle; + // Last systemUiVisibility we received from status bar. private int mLastStatusBarVisibility = 0; // Last systemUiVisibility we dispatched to windows. @@ -2410,10 +2418,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo win.getTouchableRegion(mTmpRegion); mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); } - for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) { - final WindowState win = mTapExcludeProvidingWindows.valueAt(i); - win.amendTapExcludeRegion(mTouchExcludeRegion); - } + amendWindowTapExcludeRegion(mTouchExcludeRegion); // TODO(multi-display): Support docked stacks on secondary displays. if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) { mDividerControllerLocked.getTouchRegion(mTmpRect); @@ -2425,6 +2430,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + /** + * Union the region with all the tap exclude region provided by windows on this display. + * + * @param inOutRegion The region to be amended. + */ + void amendWindowTapExcludeRegion(Region inOutRegion) { + for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) { + final WindowState win = mTapExcludeProvidingWindows.valueAt(i); + win.amendTapExcludeRegion(inOutRegion); + } + } + @Override void switchUser() { super.switchUser(); @@ -3586,6 +3603,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private void updateBounds() { calculateBounds(mDisplayInfo, mTmpBounds); setBounds(mTmpBounds); + if (mPortalWindowHandle != null && mParentSurfaceControl != null) { + mPortalWindowHandle.touchableRegion.getBounds(mTmpRect); + if (!mTmpBounds.equals(mTmpRect)) { + mPortalWindowHandle.touchableRegion.set(mTmpBounds); + mPendingTransaction.setInputWindowInfo(mParentSurfaceControl, mPortalWindowHandle); + } + } } // Determines the current display bounds based on the current state @@ -4830,15 +4854,43 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo || mWmService.mForceDesktopModeOnExternalDisplays; } - /** + /** * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and * {@link #mOverlayLayer} to the specified surfaceControl. * - * @param surfaceControlHandle The new SurfaceControl, where the DisplayContent's - * surfaces will be re-parented to. + * @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to. */ void reparentDisplayContent(SurfaceControl sc) { - mPendingTransaction.reparent(mWindowingLayer, sc) - .reparent(mOverlayLayer, sc); + mParentSurfaceControl = sc; + if (mPortalWindowHandle == null) { + mPortalWindowHandle = createPortalWindowHandle(sc.toString()); + } + mPendingTransaction.setInputWindowInfo(sc, mPortalWindowHandle) + .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc); + } + + /** + * Create a portal window handle for input. This window transports any touch to the display + * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window. + * + * @param name The name of the portal window handle. + * @return the new portal window handle. + */ + private InputWindowHandle createPortalWindowHandle(String name) { + // Let surface flinger to set the display ID of this input window handle because we don't + // know which display the parent surface control is on. + final InputWindowHandle portalWindowHandle = new InputWindowHandle( + null /* inputApplicationHandle */, null /* clientWindow */, INVALID_DISPLAY); + portalWindowHandle.name = name; + portalWindowHandle.token = new Binder(); + portalWindowHandle.layoutParamsFlags = + FLAG_SPLIT_TOUCH | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL; + getBounds(mTmpBounds); + portalWindowHandle.touchableRegion.set(mTmpBounds); + portalWindowHandle.scaleFactor = 1f; + portalWindowHandle.ownerPid = Process.myPid(); + portalWindowHandle.ownerUid = Process.myUid(); + portalWindowHandle.portalToDisplayId = mDisplayId; + return portalWindowHandle; } } diff --git a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java index cbc936f2f1d7..0a4ab67f033d 100644 --- a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java +++ b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java @@ -49,7 +49,9 @@ class TapExcludeRegionHolder { void amendRegion(Region region, Rect boundingRegion) { for (int i = mTapExcludeRects.size() - 1; i>= 0 ; --i) { final Rect rect = mTapExcludeRects.valueAt(i); - rect.intersect(boundingRegion); + if (boundingRegion != null) { + rect.intersect(boundingRegion); + } region.union(rect); } } diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index 2e5df45f9080..dd94af657039 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -70,6 +70,18 @@ public class TaskTapPointerEventListener implements PointerEventListener { // method target window will lose the focus. return; } + final Region windowTapExcludeRegion = Region.obtain(); + mDisplayContent.amendWindowTapExcludeRegion(windowTapExcludeRegion); + if (windowTapExcludeRegion.contains(x, y)) { + windowTapExcludeRegion.recycle(); + // The user is tapping on the window tap exclude region. We don't move this + // display to top. A window tap exclude region, for example, may be set by an + // ActivityView, and the region would match the bounds of both the ActivityView + // and the virtual display in it. In this case, we would take the tap that is on + // the embedded virtual display instead of this display. + return; + } + windowTapExcludeRegion.recycle(); WindowContainer parent = mDisplayContent.getParent(); if (parent != null && parent.getTopChild() != mDisplayContent) { parent.positionChildAt(WindowContainer.POSITION_TOP, mDisplayContent, @@ -81,9 +93,6 @@ public class TaskTapPointerEventListener implements PointerEventListener { @Override public void onPointerEvent(MotionEvent motionEvent) { - if (motionEvent.getDisplayId() != getDisplayId()) { - return; - } switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int x = (int) motionEvent.getX(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 752c24e7edb6..975e62a44b0a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6532,8 +6532,13 @@ public class WindowManagerService extends IWindowManager.Stub /** * Update a tap exclude region with a rectangular area in the window identified by the provided - * id. Touches on this region will not switch focus to this window. Passing an empty rect will - * remove the area from the exclude region of this window. + * id. Touches down on this region will not: + * <ol> + * <li>Switch focus to this window.</li> + * <li>Move the display of this window to top.</li> + * <li>Send the touch events to this window.</li> + * </ol> + * Passing an empty rect will remove the area from the exclude region of this window. */ void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width, int height) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4f120100693a..62e7200ef112 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -536,7 +536,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final Point mSurfacePosition = new Point(); /** - * A region inside of this window to be excluded from touch-related focus switches. + * A region inside of this window to be excluded from touch. */ private TapExcludeRegionHolder mTapExcludeRegionHolder; @@ -2168,6 +2168,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } region.set(mTmpRect); cropRegionToStackBoundsIfNeeded(region); + subtractTouchExcludeRegionIfNeeded(region); + } else if (modal && mTapExcludeRegionHolder != null) { + final Region touchExcludeRegion = Region.obtain(); + amendTapExcludeRegion(touchExcludeRegion); + if (!touchExcludeRegion.isEmpty()) { + // Remove touch modal because there are some areas that cannot be touched. + flags |= FLAG_NOT_TOUCH_MODAL; + // Give it a large touchable region at first because it was touch modal. The window + // might be moved on the display, so the touchable region should be large enough to + // ensure it covers the whole display, no matter where it is moved. + getDisplayContent().getBounds(mTmpRect); + final int dw = mTmpRect.width(); + final int dh = mTmpRect.height(); + region.set(-dw, -dh, dw + dw, dh + dh); + // Subtract the area that cannot be touched. + region.op(touchExcludeRegion, Region.Op.DIFFERENCE); + } + touchExcludeRegion.recycle(); } else { // Not modal or full screen modal getTouchableRegion(region); @@ -2837,6 +2855,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } cropRegionToStackBoundsIfNeeded(outRegion); + subtractTouchExcludeRegionIfNeeded(outRegion); } private void cropRegionToStackBoundsIfNeeded(Region region) { @@ -2855,6 +2874,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } /** + * If this window has areas that cannot be touched, we subtract those areas from its touchable + * region. + */ + private void subtractTouchExcludeRegionIfNeeded(Region touchableRegion) { + if (mTapExcludeRegionHolder == null) { + return; + } + final Region touchExcludeRegion = Region.obtain(); + amendTapExcludeRegion(touchExcludeRegion); + if (!touchExcludeRegion.isEmpty()) { + touchableRegion.op(touchExcludeRegion, Region.Op.DIFFERENCE); + } + touchExcludeRegion.recycle(); + } + + /** * Report a focus change. Must be called with no locks held, and consistently * from the same serialized thread (such as dispatched from a handler). */ @@ -4728,11 +4763,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height); // Trigger touch exclude region update on current display. currentDisplay.updateTouchExcludeRegion(); + // Trigger touchable region update for this window. + currentDisplay.getInputMonitor().updateInputWindowsLw(true /* force */); } - /** Union the region with current tap exclude region that this window provides. */ + /** + * Union the region with current tap exclude region that this window provides. + * + * @param region The region to be amended. It is on the screen coordinates. + */ void amendTapExcludeRegion(Region region) { - mTapExcludeRegionHolder.amendRegion(region, getBounds()); + final Region tempRegion = Region.obtain(); + mTmpRect.set(mWindowFrames.mFrame); + mTmpRect.offsetTo(0, 0); + mTapExcludeRegionHolder.amendRegion(tempRegion, mTmpRect); + // The region held by the holder is on the window coordinates. We need to translate it to + // the screen coordinates. + tempRegion.translate(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top); + region.op(tempRegion, Region.Op.UNION); + tempRegion.recycle(); } @Override diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java new file mode 100644 index 000000000000..33cbf7ae8ba6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019 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.rollback; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.content.pm.VersionedPackage; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.PackageRollbackInfo.RestoreInfo; +import android.util.IntArray; + +import com.android.server.pm.Installer; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import java.io.File; +import java.util.ArrayList; + +@RunWith(JUnit4.class) +public class AppDataRollbackHelperTest { + + @Test + public void testSnapshotAppData() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + + // All users are unlocked so we should snapshot data for them. + doReturn(true).when(helper).isUserCredentialLocked(eq(10)); + doReturn(true).when(helper).isUserCredentialLocked(eq(11)); + IntArray pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); + assertEquals(2, pending.size()); + assertEquals(10, pending.get(0)); + assertEquals(11, pending.get(1)); + + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).snapshotAppData( + eq("com.foo.bar"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + inOrder.verify(installer).snapshotAppData( + eq("com.foo.bar"), eq(11), eq(Installer.FLAG_STORAGE_DE)); + inOrder.verifyNoMoreInteractions(); + + // One of the users is unlocked but the other isn't + doReturn(false).when(helper).isUserCredentialLocked(eq(10)); + doReturn(true).when(helper).isUserCredentialLocked(eq(11)); + + pending = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); + assertEquals(1, pending.size()); + assertEquals(11, pending.get(0)); + + inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).snapshotAppData( + eq("com.foo.bar"), eq(10), + eq(Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE)); + inOrder.verify(installer).snapshotAppData( + eq("com.foo.bar"), eq(11), eq(Installer.FLAG_STORAGE_DE)); + inOrder.verifyNoMoreInteractions(); + } + + private static RollbackData createInProgressRollbackData(String packageName) { + RollbackData data = new RollbackData(1, new File("/does/not/exist")); + data.packages.add(new PackageRollbackInfo( + new VersionedPackage(packageName, 1), new VersionedPackage(packageName, 1), + new IntArray(), new ArrayList<>())); + data.inProgress = true; + + return data; + } + + @Test + public void testRestoreAppDataSnapshot_noRollbackData() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + + assertFalse(helper.restoreAppData("com.foo", null, 0, 0, 0, "seinfo")); + verifyZeroInteractions(installer); + } + + @Test + public void testRestoreAppDataSnapshot_noRollbackInProgress() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + + RollbackData rd = createInProgressRollbackData("com.foo"); + // Override the in progress flag. + rd.inProgress = false; + assertFalse(helper.restoreAppData("com.foo", rd, 0, 0, 0, "seinfo")); + verifyZeroInteractions(installer); + } + + @Test + public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + + RollbackData rd = createInProgressRollbackData("com.foo"); + IntArray pendingBackups = rd.packages.get(0).getPendingBackups(); + pendingBackups.add(10); + pendingBackups.add(11); + + assertTrue(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + + // Should only require FLAG_STORAGE_DE here because we have a pending backup that we + // didn't manage to execute. + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).restoreAppDataSnapshot( + eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + inOrder.verifyNoMoreInteractions(); + + assertEquals(1, pendingBackups.size()); + assertEquals(11, pendingBackups.get(0)); + } + + @Test + public void testRestoreAppDataSnapshot_availableBackupForLockedUser() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + doReturn(true).when(helper).isUserCredentialLocked(eq(10)); + + RollbackData rd = createInProgressRollbackData("com.foo"); + + assertTrue(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).restoreAppDataSnapshot( + eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + inOrder.verifyNoMoreInteractions(); + + ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores(); + assertEquals(1, pendingRestores.size()); + assertEquals(10, pendingRestores.get(0).userId); + assertEquals(1, pendingRestores.get(0).appId); + assertEquals("seinfo", pendingRestores.get(0).seInfo); + } + + @Test + public void testRestoreAppDataSnapshot_availableBackupForUnockedUser() throws Exception { + Installer installer = mock(Installer.class); + AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); + doReturn(false).when(helper).isUserCredentialLocked(eq(10)); + + RollbackData rd = createInProgressRollbackData("com.foo"); + assertFalse(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + + InOrder inOrder = Mockito.inOrder(installer); + inOrder.verify(installer).restoreAppDataSnapshot( + eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), + eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); + inOrder.verifyNoMoreInteractions(); + + ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores(); + assertEquals(0, pendingRestores.size()); + } +} diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 44756307a4b3..33178766f3a3 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -540,7 +540,7 @@ public class ServiceState implements Parcelable { * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage public int getDataRegState() { return mDataRegState; } diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 86af6422dad3..c1c598d52676 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.content.pm.VersionedPackage; import android.os.test.TestLooper; import android.support.test.InstrumentationRegistry; @@ -47,6 +48,7 @@ public class PackageWatchdogTest { private static final String APP_B = "com.package.b"; private static final String APP_C = "com.package.c"; private static final String APP_D = "com.package.d"; + private static final long VERSION_CODE = 1L; private static final String OBSERVER_NAME_1 = "observer1"; private static final String OBSERVER_NAME_2 = "observer2"; private static final String OBSERVER_NAME_3 = "observer3"; @@ -193,7 +195,7 @@ public class PackageWatchdogTest { // Then fail APP_A below the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -209,12 +211,10 @@ public class PackageWatchdogTest { * the failed packages. */ @Test - public void testPackageFailureNotifyNone() throws Exception { + public void testPackageFailureDifferentPackageNotifyNone() throws Exception { PackageWatchdog watchdog = createWatchdog(); - TestObserver observer1 = new TestObserver(OBSERVER_NAME_1, - PackageHealthObserverImpact.USER_IMPACT_HIGH); - TestObserver observer2 = new TestObserver(OBSERVER_NAME_2, - PackageHealthObserverImpact.USER_IMPACT_HIGH); + TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); + TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION); @@ -222,7 +222,7 @@ public class PackageWatchdogTest { // Then fail APP_C (not observed) above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_C}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -234,6 +234,40 @@ public class PackageWatchdogTest { } /** + * Test package failure and does not notify any observer because the failed package version + * does not match the available rollback-from-version. + */ + @Test + public void testPackageFailureDifferentVersionNotifyNone() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + long differentVersionCode = 2L; + TestObserver observer = new TestObserver(OBSERVER_NAME_1) { + public int onHealthCheckFailed(String packageName, long versionCode) { + if (versionCode == VERSION_CODE) { + // Only rollback for specific versionCode + return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; + } + return PackageHealthObserverImpact.USER_IMPACT_NONE; + } + }; + + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); + + // Then fail APP_A (different version) above the threshold + for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + watchdog.onPackageFailure(Arrays.asList( + new VersionedPackage(APP_A, differentVersionCode))); + } + + // Run handler so package failures are dispatched to observers + mTestLooper.dispatchAll(); + + // Verify that observers are not notified + assertEquals(0, observer.mFailedPackages.size()); + } + + + /** * Test package failure and notifies only least impact observers. */ @Test @@ -260,7 +294,10 @@ public class PackageWatchdogTest { // Then fail all apps above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A, APP_B, APP_C, APP_D}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), + new VersionedPackage(APP_B, VERSION_CODE), + new VersionedPackage(APP_C, VERSION_CODE), + new VersionedPackage(APP_D, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -297,7 +334,7 @@ public class PackageWatchdogTest { * <ul> */ @Test - public void testPackageFailureNotifyLeastSuccessively() throws Exception { + public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception { PackageWatchdog watchdog = createWatchdog(); TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1, PackageHealthObserverImpact.USER_IMPACT_LOW); @@ -310,7 +347,7 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -327,7 +364,7 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -344,7 +381,7 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -361,7 +398,7 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -388,7 +425,7 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { - watchdog.onPackageFailure(new String[]{APP_A}); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -420,11 +457,11 @@ public class PackageWatchdogTest { mImpact = impact; } - public int onHealthCheckFailed(String packageName) { + public int onHealthCheckFailed(String packageName, long versionCode) { return mImpact; } - public boolean execute(String packageName) { + public boolean execute(String packageName, long versionCode) { mFailedPackages.add(packageName); return true; } |