Merge "Throw exception on mismatched system vs vendor"
diff --git a/api/current.txt b/api/current.txt
index afd09ad..c10c072b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5811,6 +5811,7 @@
method public final void setInterruptionFilter(int);
method public void setNotificationDelegate(@NonNull String);
method public void setNotificationPolicy(@NonNull android.app.NotificationManager.Policy);
+ method public boolean shouldHideSilentStatusBarIcons();
method public boolean updateAutomaticZenRule(String, android.app.AutomaticZenRule);
field public static final String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED";
field public static final String ACTION_AUTOMATIC_ZEN_RULE = "android.app.action.AUTOMATIC_ZEN_RULE";
@@ -12978,7 +12979,7 @@
method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder removeOpenFlags(int);
method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(@Nullable android.database.DatabaseErrorHandler);
- method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(@IntRange(from=0) long);
+ method @Deprecated @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(@IntRange(from=0) long);
method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(@NonNull String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(@IntRange(from=0) int, @IntRange(from=0) int);
method @NonNull public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
@@ -13039,7 +13040,7 @@
method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
method public void onOpen(android.database.sqlite.SQLiteDatabase);
method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int);
- method public void setIdleConnectionTimeout(@IntRange(from=0) long);
+ method @Deprecated public void setIdleConnectionTimeout(@IntRange(from=0) long);
method public void setLookasideConfig(@IntRange(from=0) int, @IntRange(from=0) int);
method public void setOpenParams(@NonNull android.database.sqlite.SQLiteDatabase.OpenParams);
method public void setWriteAheadLoggingEnabled(boolean);
@@ -36527,7 +36528,7 @@
field public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
field public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
field public static final String ACTION_HANDLE_CUSTOM_EVENT = "android.provider.calendar.action.HANDLE_CUSTOM_EVENT";
- field public static final String ACTION_VIEW_WORK_CALENDAR_EVENT = "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
+ field public static final String ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT = "android.provider.calendar.action.VIEW_MANAGED_PROFILE_CALENDAR_EVENT";
field public static final String AUTHORITY = "com.android.calendar";
field public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
field public static final android.net.Uri CONTENT_URI;
@@ -41555,6 +41556,7 @@
method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int);
+ method public void onStatusBarIconsBehaviorChanged(boolean);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
method public static void requestRebind(android.content.ComponentName);
@@ -52887,6 +52889,9 @@
public final class UserDataRemovalRequest implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public String getPackageName();
+ method @NonNull public java.util.List<android.view.contentcapture.UserDataRemovalRequest.UriRequest> getUriRequests();
+ method public boolean isForEverything();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.UserDataRemovalRequest> CREATOR;
}
@@ -52898,6 +52903,11 @@
method @NonNull public android.view.contentcapture.UserDataRemovalRequest.Builder forEverything();
}
+ public final class UserDataRemovalRequest.UriRequest {
+ method @NonNull public android.net.Uri getUri();
+ method @NonNull public boolean isRecursive();
+ }
+
}
package android.view.inputmethod {
@@ -58117,13 +58127,13 @@
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;
}
@@ -58294,12 +58304,12 @@
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);
@@ -58788,8 +58798,8 @@
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);
@@ -58799,8 +58809,8 @@
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);
@@ -59626,45 +59636,45 @@
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();
@@ -60551,8 +60561,8 @@
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);
@@ -60564,7 +60574,7 @@
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);
@@ -60579,8 +60589,8 @@
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);
@@ -60781,7 +60791,7 @@
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();
@@ -60879,13 +60889,13 @@
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 {
@@ -61208,8 +61218,8 @@
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;
@@ -61217,10 +61227,10 @@
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>);
}
@@ -61255,20 +61265,20 @@
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();
@@ -61357,7 +61367,7 @@
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();
@@ -61400,8 +61410,8 @@
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();
@@ -61414,7 +61424,7 @@
}
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();
}
@@ -61422,9 +61432,9 @@
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;
}
@@ -61438,7 +61448,7 @@
}
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();
}
@@ -61450,8 +61460,8 @@
}
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();
}
}
@@ -62495,7 +62505,7 @@
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();
@@ -62509,8 +62519,8 @@
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();
@@ -62529,8 +62539,8 @@
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);
@@ -62544,8 +62554,8 @@
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 {
@@ -64378,20 +64388,20 @@
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);
}
@@ -66998,7 +67008,7 @@
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);
@@ -67238,7 +67248,7 @@
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);
@@ -68892,8 +68902,8 @@
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> {
@@ -69002,161 +69012,161 @@
}
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 {
@@ -69240,7 +69250,7 @@
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);
@@ -69328,8 +69338,8 @@
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;
}
@@ -69340,7 +69350,7 @@
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);
@@ -69370,12 +69380,12 @@
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>);
@@ -69893,7 +69903,7 @@
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);
@@ -69911,8 +69921,8 @@
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();
@@ -70106,7 +70116,7 @@
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);
@@ -70771,7 +70781,7 @@
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);
@@ -70791,7 +70801,7 @@
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> {
@@ -71182,7 +71192,7 @@
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>);
@@ -71210,8 +71220,8 @@
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 {
@@ -72963,7 +72973,7 @@
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);
@@ -72976,7 +72986,7 @@
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);
@@ -72992,7 +73002,7 @@
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);
@@ -73003,8 +73013,8 @@
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;
@@ -73266,8 +73276,8 @@
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 e7c7b57..8989344 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -501,6 +501,13 @@
method public org.json.JSONObject toJson() throws org.json.JSONException;
}
+ public class NotificationManager {
+ method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
+ method @Nullable public android.content.ComponentName getAllowedNotificationAssistantForUser(android.os.UserHandle);
+ method public void setNotificationAssistantAccessGranted(android.content.ComponentName, boolean);
+ method public void setNotificationAssistantAccessGrantedForUser(android.content.ComponentName, android.os.UserHandle, boolean);
+ }
+
public final class StatsManager {
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean addConfiguration(long, byte[]);
@@ -1718,9 +1725,9 @@
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;
@@ -4095,6 +4102,7 @@
public class CaptivePortal implements android.os.Parcelable {
ctor public CaptivePortal(android.os.IBinder);
+ method public void logEvent(int, String);
method public void useNetwork();
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
@@ -9404,17 +9412,6 @@
method public void setContentCaptureFeatureEnabled(boolean);
}
- public final class UserDataRemovalRequest implements android.os.Parcelable {
- method @NonNull public String getPackageName();
- method @NonNull public java.util.List<android.view.contentcapture.UserDataRemovalRequest.UriRequest> getUriRequests();
- method public boolean isForEverything();
- }
-
- public final class UserDataRemovalRequest.UriRequest {
- method @NonNull public android.net.Uri getUri();
- method @NonNull public boolean isRecursive();
- }
-
public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
method @Nullable public android.view.autofill.AutofillId getParentAutofillId();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 4fc1757..91d0019 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -804,6 +804,7 @@
public class CaptivePortal implements android.os.Parcelable {
ctor public CaptivePortal(android.os.IBinder);
+ method public void logEvent(int, String);
method public void useNetwork();
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
@@ -2386,6 +2387,10 @@
method public boolean isSystemGroup();
}
+ public abstract class LayoutInflater {
+ method public void setPrecompiledLayoutsEnabledForTesting(boolean);
+ }
+
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
method public void setActionButton(int);
method public void setButtonState(int);
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 4e4b8f3..f37d2be 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -55,6 +55,10 @@
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 ba7bcc4..a6ba2ca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -231,6 +231,9 @@
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 f4d0144..37ccad5 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -403,6 +403,60 @@
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 @@
pullStats.second.pullTimeout = 0;
pullStats.second.pullExceedMaxDelay = 0;
}
+ mAtomMetricStats.clear();
}
string buildTimeString(int64_t timeSec) {
@@ -713,6 +768,10 @@
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 dc647f8..01e9ca1 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -318,6 +318,53 @@
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 @@
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 @@
// 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 @@
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 5645461..6aa8e84 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -322,6 +322,7 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 e8de875..cca09ac 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -403,9 +403,23 @@
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 7de0bb3..9c9985e 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_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 @@
(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 61f31eb..dcea0e6 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -73,6 +73,10 @@
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/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 8e7a58b..a748959 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1942,7 +1942,6 @@
Lcom/android/internal/R$id;->titleDivider:I
Lcom/android/internal/R$id;->titleDividerTop:I
Lcom/android/internal/R$id;->title_container:I
-Lcom/android/internal/R$id;->title_icon:I
Lcom/android/internal/R$id;->title_template:I
Lcom/android/internal/R$id;->topPanel:I
Lcom/android/internal/R$id;->up:I
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index ce5d8a5..eae7d6b 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -28,15 +28,12 @@
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 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 @@
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 @@
}
/**
- * 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 @@
/** 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 @@
}
mTmpTransaction.show(mRootSurfaceControl).apply();
- mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
mTaskStackListener = new TaskStackListenerImpl();
try {
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
@@ -432,9 +394,6 @@
mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
- if (mInputForwarder != null) {
- mInputForwarder = null;
- }
cleanTapExcludeRegion();
if (mTaskStackListener != null) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8953940..df04a6b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,6 +65,9 @@
boolean areNotificationsEnabled(String pkg);
int getPackageImportance(String pkg);
+ boolean shouldHideSilentStatusIcons(String callingPkg);
+ void setHideSilentStatusIcons(boolean hide);
+
void setBubblesAllowed(String pkg, int uid, boolean allowed);
boolean areBubblesAllowed(String pkg);
boolean areBubblesAllowedForPackage(String pkg, int uid);
@@ -151,6 +154,8 @@
void setNotificationAssistantAccessGrantedForUser(in ComponentName assistant, int userId, boolean enabled);
List<String> getEnabledNotificationListenerPackages();
List<ComponentName> getEnabledNotificationListeners(int userId);
+ ComponentName getAllowedNotificationAssistantForUser(int userId);
+ ComponentName getAllowedNotificationAssistant();
int getZenMode();
ZenModeConfig getZenModeConfig();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 621f134..6a1ff29 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -353,7 +354,8 @@
public static final int IMPORTANCE_MIN = 1;
/**
- * Low notification importance: shows everywhere, but is not intrusive.
+ * Low notification importance: Shows in the shade, and potentially in the status bar
+ * (see {@link #shouldHideSilentStatusBarIcons()}), but is not audibly intrusive.
*/
public static final int IMPORTANCE_LOW = 2;
@@ -1153,6 +1155,19 @@
}
}
+ /**
+ * Checks whether the user has approved a given
+ * {@link android.service.notification.NotificationAssistantService}.
+ *
+ * <p>
+ * The assistant service must belong to the calling app.
+ *
+ * <p>
+ * Apps can request notification assistant access by sending the user to the activity that
+ * matches the system intent action
+ * TODO: STOPSHIP: Add correct intent
+ * {@link android.provider.Settings#ACTION_MANAGE_DEFAULT_APPS_SETTINGS}.
+ */
public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
INotificationManager service = getService();
try {
@@ -1162,6 +1177,22 @@
}
}
+ /**
+ * Returns whether the user wants silent notifications (see {@link #IMPORTANCE_LOW} to appear
+ * in the status bar.
+ *
+ * <p>Only available for {@link #isNotificationListenerAccessGranted(ComponentName) notification
+ * listeners}.
+ */
+ public boolean shouldHideSilentStatusBarIcons() {
+ INotificationManager service = getService();
+ try {
+ return service.shouldHideSilentStatusIcons(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
INotificationManager service = getService();
@@ -1248,6 +1279,45 @@
}
}
+ /**
+ * Grants/revokes Notification Assistant access to {@code assistant} for current user.
+ *
+ * @param assistant Name of component to grant/revoke access or {@code null} to revoke access to
+ * current assistant
+ * @param granted Grant/revoke access
+ * @hide
+ */
+ @SystemApi
+ public void setNotificationAssistantAccessGranted(ComponentName assistant, boolean granted) {
+ INotificationManager service = getService();
+ try {
+ service.setNotificationAssistantAccessGranted(assistant, granted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Grants/revokes Notification Assistant access to {@code assistant} for given user.
+ *
+ * @param assistant Name of component to grant/revoke access or {@code null} to revoke access to
+ * current assistant
+ * @param user handle to associate assistant with
+ * @param granted Grant/revoke access
+ * @hide
+ */
+ @SystemApi
+ public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
+ UserHandle user, boolean granted) {
+ INotificationManager service = getService();
+ try {
+ service.setNotificationAssistantAccessGrantedForUser(assistant, user.getIdentifier(),
+ granted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public List<ComponentName> getEnabledNotificationListeners(int userId) {
INotificationManager service = getService();
@@ -1258,6 +1328,29 @@
}
}
+ /** @hide */
+ @SystemApi
+ public @Nullable ComponentName getAllowedNotificationAssistantForUser(UserHandle user) {
+ INotificationManager service = getService();
+ try {
+ return service.getAllowedNotificationAssistantForUser(user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ @SystemApi
+ public @Nullable ComponentName getAllowedNotificationAssistant() {
+ INotificationManager service = getService();
+ try {
+ return service.getAllowedNotificationAssistant();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
private Context mContext;
private static void checkRequired(String name, Object value) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 65ea635..a3021f3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -971,7 +971,8 @@
*
* @param target The Intent that the user will be selecting an activity
* to perform.
- * @param title Optional title that will be displayed in the chooser.
+ * @param title Optional title that will be displayed in the chooser,
+ * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
* @return Return a new Intent object that you can hand to
* {@link Context#startActivity(Intent) Context.startActivity()} and
* related methods.
@@ -998,7 +999,8 @@
*
* @param target The Intent that the user will be selecting an activity
* to perform.
- * @param title Optional title that will be displayed in the chooser.
+ * @param title Optional title that will be displayed in the chooser,
+ * only when the target action is not ACTION_SEND or ACTION_SEND_MULTIPLE.
* @param sender Optional IntentSender to be called when a choice is made.
* @return Return a new Intent object that you can hand to
* {@link Context#startActivity(Intent) Context.startActivity()} and
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 4644a83..d4ed35a 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 @@
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 @@
}
/** @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 1111b43..3fd2476 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -83,7 +83,7 @@
* 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/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index f9c2c3e..a1b0803 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2644,10 +2644,29 @@
* Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
* before it is closed and removed from the pool.
*
+ * <p>DO NOT USE this method unless you fully understand the implication
+ * of what it does.
+ * A connection timeout allows the system to internally close a connection to a SQLite
+ * database after a given timeout.
+ * This is good for reducing app's memory consumption, but it has
+ * side effects that are hard to predict. For example, SQLite internally maintains
+ * a lot of "per-connection" states that apps can typically modify with a {@code PRAGMA}
+ * statement, and such states will be reset once the connection is closed.
+ * The system does not provide a callback that would allow apps to
+ * reconfigure a newly created connection and thus there's no way to re-configure
+ * connections when they're re-made internally. Do not use it unless you're sure
+ * your app uses no per-connection states.
+ *
* @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE}
* to allow unlimited idle connections.
+ *
+ * @see SQLiteOpenHelper#setIdleConnectionTimeout(long)
+ *
+ * @deprecated DO NOT USE this method unless you fully understand the implication
+ * of what it does.
*/
@NonNull
+ @Deprecated
public Builder setIdleConnectionTimeout(
@IntRange(from = 0) long idleConnectionTimeoutMs) {
Preconditions.checkArgument(idleConnectionTimeoutMs >= 0,
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 19c6745..ceeecbc 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -263,9 +263,21 @@
* <p>This method should be called from the constructor of the subclass,
* before opening the database
*
+ * <p>DO NOT USE this method unless you fully understand the implication
+ * of what it does.
+ * See the javadoc of
+ * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
+ * for the details.
+ *
* @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value
* to allow unlimited idle connections.
+ *
+ * @see SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)
+ *
+ * @deprecated DO NOT USE this method unless you fully understand the implication
+ * of what it does.
*/
+ @Deprecated
public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
synchronized (this) {
if (mDatabase != null && mDatabase.isOpen()) {
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index 3b01266..3ab35e1 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -117,4 +117,17 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Log a captive portal login event.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void logEvent(int eventId, String packageName) {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl
index 56ae57d..707b4f6 100644
--- a/core/java/android/net/ICaptivePortal.aidl
+++ b/core/java/android/net/ICaptivePortal.aidl
@@ -22,4 +22,5 @@
*/
oneway interface ICaptivePortal {
void appResponse(int response);
+ void logEvent(int eventId, String packageName);
}
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index 0bc2575..a8682f9 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -26,4 +26,5 @@
void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
void showProvisioningNotification(String action);
void hideProvisioningNotification();
+ void logCaptivePortalLoginEvent(int eventId, String packageName);
}
\ No newline at end of file
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 8a52f1f..3e5bd4b 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -138,8 +138,8 @@
* Action used to help apps show calendar events in the managed profile.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_VIEW_WORK_CALENDAR_EVENT =
- "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT";
+ public static final String ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT =
+ "android.provider.calendar.action.VIEW_MANAGED_PROFILE_CALENDAR_EVENT";
/**
* Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in
@@ -166,7 +166,7 @@
public static final String EXTRA_EVENT_ALL_DAY = "allDay";
/**
- * Intent Extras key: The id of an event.
+ * Intent Extras key: An extra of type {@code long} holding the id of an event.
*/
public static final String EXTRA_EVENT_ID = "id";
@@ -218,7 +218,7 @@
* When this API is called, the system will attempt to start an activity
* in the managed profile with an intent targeting the same caller package.
* The intent will have its action set to
- * {@link CalendarContract#ACTION_VIEW_WORK_CALENDAR_EVENT} and contain extras
+ * {@link CalendarContract#ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT} and contain extras
* corresponding to the API's arguments. A calendar app intending to support
* cross-profile events viewing should handle this intent, parse the arguments
* and show the appropriate UI.
@@ -226,10 +226,10 @@
* @param context the context.
* @param eventId the id of the event to be viewed. Will be put into {@link #EXTRA_EVENT_ID}
* field of the intent.
- * @param start the start time of the event. Will be put into {@link #EXTRA_EVENT_BEGIN_TIME}
- * field of the intent.
- * @param end the end time of the event. Will be put into {@link #EXTRA_EVENT_END_TIME} field
- * of the intent.
+ * @param startMs the start time of the event in milliseconds since epoch.
+ * Will be put into {@link #EXTRA_EVENT_BEGIN_TIME} field of the intent.
+ * @param endMs the end time of the event in milliseconds since epoch.
+ * Will be put into {@link #EXTRA_EVENT_END_TIME} field of the intent.
* @param allDay if the event is an all-day event. Will be put into
* {@link #EXTRA_EVENT_ALL_DAY} field of the intent.
* @param flags flags to be set on the intent via {@link Intent#setFlags}
@@ -241,12 +241,12 @@
* @see #EXTRA_EVENT_ALL_DAY
*/
public static boolean startViewCalendarEventInManagedProfile(@NonNull Context context,
- long eventId, long start, long end, boolean allDay, int flags) {
+ long eventId, long startMs, long endMs, boolean allDay, int flags) {
Preconditions.checkNotNull(context, "Context is null");
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- return dpm.startViewCalendarEventInManagedProfile(eventId, start,
- end, allDay, flags);
+ return dpm.startViewCalendarEventInManagedProfile(eventId, startMs,
+ endMs, allDay, flags);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 794c2f1..5151e29 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10521,8 +10521,10 @@
/**
* 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/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 1ddc099e..22104b5 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -33,6 +33,7 @@
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
+ void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons);
// stats only for assistant
void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update, in NotificationStats stats, int reason);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c734b63..d4e8879 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -469,6 +469,17 @@
}
/**
+ * Implement this method to be notified when the behavior of silent notifications in the status
+ * bar changes. See {@link NotificationManager#shouldHideSilentStatusBarIcons()}.
+ *
+ * @param hideSilentStatusIcons whether or not status bar icons should be hidden for silent
+ * notifications
+ */
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ // optional
+ }
+
+ /**
* Implement this method to learn about notification channel modifications.
*
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
@@ -1411,6 +1422,12 @@
mHandler.obtainMessage(
MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
}
+
+ @Override
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
+ hideSilentStatusIcons).sendToTarget();
+ }
}
/**
@@ -2142,6 +2159,7 @@
public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
+ public static final int MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED = 9;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -2207,6 +2225,10 @@
int modificationType = (int) args.arg4;
onNotificationChannelGroupModified(pkgName, user, group, modificationType);
} break;
+
+ case MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED: {
+ onStatusBarIconsBehaviorChanged((Boolean) msg.obj);
+ } break;
}
}
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index eb41e07..59e562f 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -41,12 +41,14 @@
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 @@
* 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 @@
private final Object mLock = new Object();
- private final Handler mHandler;
+ private final PrivateHandler mHandler;
private final ViewRootImpl mViewRootImpl;
@@ -131,11 +136,19 @@
// 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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 @@
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 92e0009..ec79eea 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 @@
// 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/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index dc7c343..6061cb2 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -399,10 +400,15 @@
}
private void initPrecompiledViews() {
- // Check if precompiled layouts are enabled by a system property.
- mUseCompiledView =
- SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false);
+ initPrecompiledViews(
+ SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false));
+ }
+
+ private void initPrecompiledViews(boolean enablePrecompiledViews) {
+ mUseCompiledView = enablePrecompiledViews;
+
if (!mUseCompiledView) {
+ mPrecompiledClassLoader = null;
return;
}
@@ -431,6 +437,17 @@
}
mUseCompiledView = false;
}
+ if (!mUseCompiledView) {
+ mPrecompiledClassLoader = null;
+ }
+ }
+
+ /**
+ * @hide for use by CTS tests
+ */
+ @TestApi
+ public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) {
+ initPrecompiledViews(enablePrecompiledLayouts);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f47eb10..9213f32 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8718,6 +8718,15 @@
}
}
}
+
+ @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 4c0fdfd..947ff05 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -55,4 +55,6 @@
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/android/view/contentcapture/UserDataRemovalRequest.java b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
index 8ee63ef..8fedcd5 100644
--- a/core/java/android/view/contentcapture/UserDataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/UserDataRemovalRequest.java
@@ -16,7 +16,6 @@
package android.view.contentcapture;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.net.Uri;
import android.os.Parcel;
@@ -67,9 +66,7 @@
/**
* Gets the name of the app that's making the request.
- * @hide
*/
- @SystemApi
@NonNull
public String getPackageName() {
return mPackageName;
@@ -77,20 +74,14 @@
/**
* Checks if app is requesting to remove all user data associated with its package.
- *
- * @hide
*/
- @SystemApi
public boolean isForEverything() {
return mForEverything;
}
/**
* Gets the list of {@code Uri}s the apps is requesting to remove.
- *
- * @hide
*/
- @SystemApi
@NonNull
public List<UriRequest> getUriRequests() {
return mUriRequests;
@@ -203,9 +194,7 @@
/**
* Representation of a request to remove data associated with an {@link Uri}.
- * @hide
*/
- @SystemApi
public final class UriRequest {
private final @NonNull Uri mUri;
private final boolean mRecursive;
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 5a56136..8a688d8 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -164,9 +164,10 @@
TextClassifier NO_OP = new TextClassifier() {};
/**
- * Used as a boolean value to indicate the intent is generated by TextClassifier.
+ * Extra that is included on activity intents coming from a TextClassifier when
+ * it suggests actions to its caller.
* <p>
- * All {@link TextClassifier} implementations should set this boolean extra to be true in their
+ * All {@link TextClassifier} implementations should make sure this extra exists in their
* generated intents.
*/
String EXTRA_FROM_TEXT_CLASSIFIER = "android.view.textclassifier.extra.FROM_TEXT_CLASSIFIER";
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 299801a..46f42f7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,6 +24,7 @@
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -41,9 +42,13 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
@@ -62,7 +67,9 @@
import android.service.chooser.IChooserTargetResult;
import android.service.chooser.IChooserTargetService;
import android.text.TextUtils;
+import android.util.AttributeSet;
import android.util.Log;
+import android.util.Size;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -73,9 +80,11 @@
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Space;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -85,6 +94,7 @@
import com.google.android.collect.Lists;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -262,16 +272,31 @@
}
mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
- CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
+
+ // Do not allow the title to be changed when sharing content
+ CharSequence title = null;
+ if (target != null) {
+ String targetAction = target.getAction();
+ if (!(Intent.ACTION_SEND.equals(targetAction) || Intent.ACTION_SEND_MULTIPLE.equals(
+ targetAction))) {
+ title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
+ } else {
+ Log.w(TAG, "Ignoring intent's EXTRA_TITLE, deprecated in P. You may wish to set a"
+ + " preview title by using EXTRA_TITLE property of the wrapped"
+ + " EXTRA_INTENT.");
+ }
+ }
+
int defaultTitleRes = 0;
if (title == null) {
defaultTitleRes = com.android.internal.R.string.chooseActivity;
}
+
Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
Intent[] initialIntents = null;
if (pa != null) {
initialIntents = new Intent[pa.length];
- for (int i=0; i<pa.length; i++) {
+ for (int i = 0; i < pa.length; i++) {
if (!(pa[i] instanceof Intent)) {
Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
finish();
@@ -364,6 +389,68 @@
}
}
+ /**
+ * Override method to add content preview area, specific to the chooser activity.
+ */
+ @Override
+ public void setHeader() {
+ super.setHeader();
+
+ Intent targetIntent = getTargetIntent();
+ if (targetIntent == null) {
+ return;
+ }
+
+ ViewGroup contentPreviewLayout = findViewById(R.id.content_preview);
+ String action = targetIntent.getAction();
+ if (!(Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action))) {
+ contentPreviewLayout.setVisibility(View.GONE);
+ return;
+ }
+
+ showDefaultContentPreview(contentPreviewLayout, targetIntent);
+ }
+
+ private void showDefaultContentPreview(final ViewGroup parentLayout,
+ final Intent targetIntent) {
+ CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
+ TextView previewTextView = findViewById(R.id.content_preview_text);
+ if (sharingText == null) {
+ previewTextView.setVisibility(View.GONE);
+ } else {
+ previewTextView.setText(sharingText);
+ }
+
+ String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
+ TextView previewTitleView = findViewById(R.id.content_preview_title);
+ if (previewTitle == null) {
+ previewTitleView.setVisibility(View.GONE);
+ } else {
+ previewTitleView.setText(previewTitle);
+ }
+
+ ClipData previewData = targetIntent.getClipData();
+ Uri previewThumbnail = null;
+ if (previewData != null) {
+ if (previewData.getItemCount() > 0) {
+ ClipData.Item previewDataItem = previewData.getItemAt(0);
+ previewThumbnail = previewDataItem.getUri();
+ }
+ }
+
+ ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
+ if (previewThumbnail == null) {
+ previewThumbnailView.setVisibility(View.GONE);
+ } else {
+ Bitmap bmp = loadThumbnail(previewThumbnail, new Size(200, 200));
+ if (bmp == null) {
+ previewThumbnailView.setVisibility(View.GONE);
+ } else {
+ previewThumbnailView.setImageBitmap(bmp);
+ }
+ }
+ }
+
static SharedPreferences getPinnedSharedPrefs(Context context) {
// The code below is because in the android:ui process, no one can hear you scream.
// The package info in the context isn't initialized in the way it is for normal apps,
@@ -630,15 +717,19 @@
}
}
if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
- if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
- + QUERY_TARGET_SERVICE_LIMIT);
+ if (DEBUG) {
+ Log.d(TAG, "queryTargets hit query target limit "
+ + QUERY_TARGET_SERVICE_LIMIT);
+ }
break;
}
}
if (!mServiceConnections.isEmpty()) {
- if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for "
- + WATCHDOG_TIMEOUT_MILLIS + "ms");
+ if (DEBUG) {
+ Log.d(TAG, "queryTargets setting watchdog timer for "
+ + WATCHDOG_TIMEOUT_MILLIS + "ms");
+ }
mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
WATCHDOG_TIMEOUT_MILLIS);
} else {
@@ -969,6 +1060,20 @@
mLaunchedFromUid);
}
+ @VisibleForTesting
+ protected Bitmap loadThumbnail(Uri uri, Size size) {
+ if (uri == null || size == null) {
+ return null;
+ }
+
+ try {
+ return getContentResolver().loadThumbnail(uri, size, null);
+ } catch (IOException | NullPointerException ex) {
+ Log.w(TAG, "Error loading preview thumbnail for uri: " + uri.toString(), ex);
+ }
+ return null;
+ }
+
final class ChooserTargetInfo implements TargetInfo {
private final DisplayResolveInfo mSourceInfo;
private final ResolveInfo mBackupResolveInfo;
@@ -1247,7 +1352,7 @@
UserManager userManager =
(UserManager) getSystemService(Context.USER_SERVICE);
if (ii instanceof LabeledIntent) {
- LabeledIntent li = (LabeledIntent)ii;
+ LabeledIntent li = (LabeledIntent) ii;
ri.resolvePackageName = li.getSourcePackage();
ri.labelRes = li.getLabelResource();
ri.nonLocalizedLabel = li.getNonLocalizedLabel();
@@ -1391,8 +1496,10 @@
}
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) {
- if (DEBUG) Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
- + " targets");
+ if (DEBUG) {
+ Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
+ + " targets");
+ }
if (mTargetsNeedPruning && targets.size() > 0) {
// First proper update since we got an onListRebuilt() with (transient) 0 items.
@@ -1492,8 +1599,9 @@
public int getCount() {
return (int) (
getCallerTargetRowCount()
- + getServiceTargetRowCount()
- + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
+ + getServiceTargetRowCount()
+ + Math.ceil(
+ (float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
);
}
@@ -1860,7 +1968,7 @@
final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount();
int offset = 0;
- for (int i = 0; i < chooserTargetRows; i++) {
+ for (int i = 0; i < chooserTargetRows; i++) {
final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i;
final int vt = mChooserRowAdapter.getItemViewType(pos);
if (vt != mCachedViewType) {
@@ -1882,4 +1990,65 @@
mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
}
}
+
+
+ /**
+ * Used internally to round image corners while obeying view padding.
+ */
+ public static class RoundedRectImageView extends ImageView {
+ private int mRadius = 0;
+ private Path mPath = new Path();
+
+ public RoundedRectImageView(Context context) {
+ super(context);
+ }
+
+ public RoundedRectImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mRadius = context.getResources().getDimensionPixelSize(R.dimen.chooser_corner_radius);
+ }
+
+ private void updatePath(int width, int height) {
+ mPath.reset();
+
+ int imageWidth = width - getPaddingLeft() - getPaddingRight();
+ int imageHeight = height - getPaddingTop() - getPaddingBottom();
+ mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
+ mRadius, Path.Direction.CW);
+ }
+
+ /**
+ * Sets the corner radius on all corners
+ *
+ * param radius 0 for no radius, > 0 for a visible corner radius
+ */
+ public void setRadius(int radius) {
+ mRadius = radius;
+ updatePath(getWidth(), getHeight());
+ }
+
+ @Override
+ protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
+ super.onSizeChanged(width, height, oldWidth, oldHeight);
+ updatePath(width, height);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mRadius != 0) {
+ canvas.clipPath(mPath);
+ }
+
+ super.onDraw(canvas);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a50c736..0919911 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1044,7 +1044,10 @@
}
}
- public void setTitleAndIcon() {
+ /**
+ * Configure the area above the app selection list (title, content preview, etc).
+ */
+ public void setHeader() {
if (mAdapter.getCount() == 0 && mAdapter.mPlaceholderCount == 0) {
final TextView titleView = findViewById(R.id.title);
if (titleView != null) {
@@ -1062,23 +1065,6 @@
titleView.setText(title);
}
setTitle(title);
-
- // Try to initialize the title icon if we have a view for it and a title to match
- final ImageView titleIcon = findViewById(R.id.title_icon);
- if (titleIcon != null) {
- ApplicationInfo ai = null;
- try {
- if (!TextUtils.isEmpty(mReferrerPackage)) {
- ai = mPm.getApplicationInfo(mReferrerPackage, 0);
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not find referrer package " + mReferrerPackage);
- }
-
- if (ai != null) {
- titleIcon.setImageDrawable(ai.loadIcon(mPm));
- }
- }
}
final ImageView iconView = findViewById(R.id.icon);
@@ -1692,7 +1678,7 @@
mPostListReadyRunnable = new Runnable() {
@Override
public void run() {
- setTitleAndIcon();
+ setHeader();
resetButtonBar();
onListRebuilt();
mPostListReadyRunnable = null;
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
index afe6dad..6ba584d 100644
--- a/core/java/com/android/internal/os/AppZygoteInit.java
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -73,6 +73,9 @@
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 c8d30b2..9ed7384 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -78,18 +78,7 @@
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 bc7cf87..474d4d7 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 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 @@
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 c0e45b1..67a7441 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -55,6 +55,7 @@
jfieldID ownerUid;
jfieldID inputFeatures;
jfieldID displayId;
+ jfieldID portalToDisplayId;
} gInputWindowHandleClassInfo;
static Mutex gHandleMutex;
@@ -154,6 +155,8 @@
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 @@
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 e0eaf14..a160451 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -230,8 +230,6 @@
}
optional Connectivity connectivity = 32;
- reserved 145; // Used to be ContentCapture, which moved to DeviceConfig
-
optional SettingProto contact_metadata_sync_enabled = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto contacts_database_wal_enabled = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -999,7 +997,7 @@
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
@@ -1039,5 +1037,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 149;
+ // Next tag = 145 then 149; // (145 was removed)
}
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index ded2b35..c42f43a 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -29,8 +29,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:elevation="8dp"
- android:paddingStart="16dp"
android:background="?attr/colorBackgroundFloating" >
<TextView android:id="@+id/profile_button"
android:layout_width="wrap_content"
@@ -46,25 +44,66 @@
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:singleLine="true"/>
- <ImageView android:id="@+id/title_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="16dp"
- android:visibility="gone"
- android:scaleType="fitCenter"
- android:layout_below="@id/profile_button"
- android:layout_alignParentLeft="true"/>
+
<TextView android:id="@+id/title"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textAppearance="?attr/textAppearanceMedium"
- android:textSize="14sp"
- android:gravity="start|center_vertical"
+ android:textSize="20sp"
+ android:gravity="center"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="12dp"
- android:paddingBottom="12dp"
+ android:paddingBottom="6dp"
android:layout_below="@id/profile_button"
- android:layout_toRightOf="@id/title_icon"/>
+ android:layout_centerHorizontal="true"/>
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:id="@+id/content_preview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/colorBackgroundFloating">
+
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_thumbnail"
+ android:layout_alignParentTop="true"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:gravity="center"
+ android:adjustViewBounds="true"
+ android:maxWidth="90dp"
+ android:maxHeight="90dp"
+ android:scaleType="fitCenter"
+ android:padding="5dp"/>
+
+ <TextView
+ android:id="@+id/content_preview_title"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@id/content_preview_thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:paddingStart="15dp"
+ android:paddingEnd="15dp"
+ android:paddingTop="10dp" />
+
+ <TextView
+ android:id="@+id/content_preview_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/content_preview_title"
+ android:layout_toEndOf="@id/content_preview_thumbnail"
+ android:gravity="start|top"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:paddingStart="15dp"
+ android:paddingEnd="15dp"
+ android:paddingTop="10dp"
+ android:paddingBottom="5dp"/>
</RelativeLayout>
<ListView
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c870683..ef3834c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -714,4 +714,7 @@
<dimen name="harmful_app_message_padding_bottom">24dp</dimen>
<!-- Line spacing modifier for the message field of the harmful app dialog -->
<item name="harmful_app_message_line_spacing_modifier" type="dimen">1.22</item>
+
+ <!-- chooser corner radius -->
+ <dimen name="chooser_corner_radius">4dp</dimen>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a761baf..6adb4a7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3082,7 +3082,7 @@
<!-- Label for a link to a intent resolver dialog when selecting an editor application -->
<string name="whichEditApplicationLabel">Edit</string>
<!-- Title of intent resolver dialog when selecting a sharing application to run. -->
- <string name="whichSendApplication">Share with</string>
+ <string name="whichSendApplication">Share</string>
<!-- Title of intent resolver dialog when selecting a sharing application to run
and a previously used application is known. -->
<string name="whichSendApplicationNamed">Share with %1$s</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ca8e226..54a3243 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -50,6 +50,10 @@
<java-symbol type="id" name="characterPicker" />
<java-symbol type="id" name="clearDefaultHint" />
<java-symbol type="id" name="contentPanel" />
+ <java-symbol type="id" name="content_preview" />
+ <java-symbol type="id" name="content_preview_thumbnail" />
+ <java-symbol type="id" name="content_preview_text" />
+ <java-symbol type="id" name="content_preview_title" />
<java-symbol type="id" name="current_scene" />
<java-symbol type="id" name="scene_layoutid_cache" />
<java-symbol type="id" name="customPanel" />
@@ -2703,9 +2707,9 @@
<java-symbol type="layout" name="date_picker_month_item_material" />
<java-symbol type="id" name="month_view" />
<java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
+ <java-symbol type="dimen" name="chooser_corner_radius" />
<java-symbol type="layout" name="chooser_grid" />
<java-symbol type="layout" name="resolve_grid_item" />
- <java-symbol type="id" name="title_icon" />
<java-symbol type="id" name="day_picker_view_pager" />
<java-symbol type="layout" name="day_picker_content_material" />
<java-symbol type="drawable" name="scroll_indicator_material" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 9d04e63..6a81050 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -37,7 +37,9 @@
mockwebserver \
guava \
androidx.test.runner \
+ androidx.test.ext.junit \
androidx.test.rules \
+ androidx.test.espresso.core \
mockito-target-minus-junit4 \
espresso-core \
ub-uiautomator \
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 86818c6..268bb81 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -62,6 +62,7 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -1144,6 +1145,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 0000000..1bdd21b
--- /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 0b5bde7..e32b412 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -509,7 +509,6 @@
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 0000000..d0719cb
--- /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 0000000..a17fa73
--- /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/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index aaa624e..21fcae7 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -16,12 +16,12 @@
package com.android.internal.app;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
@@ -33,12 +33,18 @@
import static org.mockito.Mockito.when;
import android.app.usage.UsageStatsManager;
+import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.net.Uri;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
@@ -69,7 +75,21 @@
@Test
public void customTitle() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
+ Intent viewIntent = createViewTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(viewIntent, "chooser test"));
+
+ waitForIdle();
+ onView(withId(R.id.title)).check(matches(withText("chooser test")));
+ }
+
+ @Test
+ public void customTitleIgnoredForSendIntents() throws InterruptedException {
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
@@ -77,12 +97,12 @@
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
waitForIdle();
- onView(withId(R.id.title)).check(matches(withText("chooser test")));
+ onView(withId(R.id.title)).check(matches(withText(R.string.whichSendApplication)));
}
@Test
public void emptyTitle() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
@@ -95,8 +115,72 @@
}
@Test
+ public void emptyPreviewTitleAndThumbnail() throws InterruptedException {
+ Intent sendIntent = createSendTextIntentWithPreview(null, null);
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_title)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void visiblePreviewTitleWithoutThumbnail() throws InterruptedException {
+ String previewTitle = "My Content Preview Title";
+ Intent sendIntent = createSendTextIntentWithPreview(previewTitle, null);
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_title)).check(matches(withText(previewTitle)));
+ onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void visiblePreviewTitleWithInvalidThumbnail() throws InterruptedException {
+ String previewTitle = "My Content Preview Title";
+ Intent sendIntent = createSendTextIntentWithPreview(previewTitle,
+ Uri.parse("tel:(+49)12345789"));
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_thumbnail)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void visiblePreviewTitleAndThumbnail() throws InterruptedException {
+ String previewTitle = "My Content Preview Title";
+ Intent sendIntent = createSendTextIntentWithPreview(previewTitle,
+ Uri.parse("android.resource://com.android.frameworks.coretests/"
+ + com.android.frameworks.coretests.R.drawable.test320x240));
+ sOverrides.previewThumbnail = createBitmap();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.content_preview_title)).check(matches(isDisplayed()));
+ onView(withId(R.id.content_preview_thumbnail)).check(matches(isDisplayed()));
+ }
+
+ @Test
public void twoOptionsAndUserSelectsOne() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
@@ -125,7 +209,7 @@
@Test
public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
@@ -158,7 +242,7 @@
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(null);
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -186,7 +270,7 @@
Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
final ChooserWrapperActivity activity = mActivityRule
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
@@ -197,7 +281,7 @@
@Test
public void hasOtherProfileOneOption() throws Exception {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(2);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
@@ -234,7 +318,7 @@
@Test
public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
@@ -273,7 +357,7 @@
@Test
public void hasLastChosenActivityAndOtherProfile() throws Exception {
- Intent sendIntent = createSendImageIntent();
+ Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
@@ -308,14 +392,33 @@
assertThat(chosen[0], is(toChoose));
}
- private Intent createSendImageIntent() {
+ private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
- sendIntent.setType("image/jpeg");
return sendIntent;
}
+ private Intent createSendTextIntentWithPreview(String title, Uri imageThumbnail) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ sendIntent.putExtra(Intent.EXTRA_TITLE, title);
+ if (imageThumbnail != null) {
+ ClipData.Item clipItem = new ClipData.Item(imageThumbnail);
+ sendIntent.setClipData(new ClipData("Clip Label", new String[]{"image/png"}, clipItem));
+ }
+
+ return sendIntent;
+ }
+
+ private Intent createViewTextIntent() {
+ Intent viewIntent = new Intent();
+ viewIntent.setAction(Intent.ACTION_VIEW);
+ viewIntent.putExtra(Intent.EXTRA_TEXT, "testing intent viewing");
+ return viewIntent;
+ }
+
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
for (int i = 0; i < numberOfResults; i++) {
@@ -340,4 +443,24 @@
private void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
-}
\ No newline at end of file
+
+ private Bitmap createBitmap() {
+ int width = 200;
+ int height = 200;
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.RED);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawPaint(paint);
+
+ paint.setColor(Color.WHITE);
+ paint.setAntiAlias(true);
+ paint.setTextSize(14.f);
+ paint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText("Hi!", (width / 2.f), (height / 2.f), paint);
+
+ return bitmap;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 60529f6..637d2ea 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -21,6 +21,9 @@
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.Size;
import java.util.function.Function;
@@ -74,6 +77,14 @@
return super.getPackageManager();
}
+ @Override
+ protected Bitmap loadThumbnail(Uri uri, Size size) {
+ if (sOverrides.previewThumbnail != null) {
+ return sOverrides.previewThumbnail;
+ }
+ return super.loadThumbnail(uri, size);
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -85,11 +96,13 @@
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public ResolverListController resolverListController;
public Boolean isVoiceInteraction;
+ public Bitmap previewThumbnail;
public void reset() {
onSafelyStartCallback = null;
isVoiceInteraction = null;
createPackageManager = null;
+ previewThumbnail = null;
resolverListController = mock(ResolverListController.class);
}
}
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index abc4dbf..fb55f5c 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -29,7 +29,7 @@
VkDevice device;
VkQueue queue;
uint32_t graphics_queue_index;
- uint32_t instance_version;
+ uint32_t api_version;
const char* const* enabled_instance_extension_names;
uint32_t enabled_instance_extension_names_length;
const char* const* enabled_device_extension_names;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 1e75202..582d51e 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -83,7 +83,6 @@
mDevice = VK_NULL_HANDLE;
mPhysicalDevice = VK_NULL_HANDLE;
mInstance = VK_NULL_HANDLE;
- mInstanceVersion = 0u;
mInstanceExtensions.clear();
mDeviceExtensions.clear();
free_features_extensions_structs(mPhysicalDeviceFeatures2);
@@ -100,7 +99,7 @@
0, // applicationVersion
"android framework", // pEngineName
0, // engineVerison
- VK_MAKE_VERSION(1, 1, 0), // apiVersion
+ mAPIVersion, // apiVersion
};
{
@@ -377,8 +376,9 @@
}
GET_PROC(EnumerateInstanceVersion);
- LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion));
- LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0));
+ uint32_t instanceVersion;
+ LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
+ LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
GrVkExtensions extensions;
LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2));
@@ -398,7 +398,7 @@
backendContext.fDevice = mDevice;
backendContext.fQueue = mGraphicsQueue;
backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
- backendContext.fInstanceVersion = mInstanceVersion;
+ backendContext.fMaxAPIVersion = mAPIVersion;
backendContext.fVkExtensions = &extensions;
backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
backendContext.fGetProc = std::move(getProc);
@@ -446,7 +446,7 @@
.device = mDevice,
.queue = mGraphicsQueue,
.graphics_queue_index = mGraphicsQueueIndex,
- .instance_version = mInstanceVersion,
+ .api_version = mAPIVersion,
.enabled_instance_extension_names = mInstanceExtensions.data(),
.enabled_instance_extension_names_length =
static_cast<uint32_t>(mInstanceExtensions.size()),
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index abe78ef..6426fe2 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -246,7 +246,7 @@
VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
// Variables saved to populate VkFunctorInitParams.
- uint32_t mInstanceVersion = 0u;
+ static const uint32_t mAPIVersion = VK_MAKE_VERSION(1, 1, 0);
std::vector<const char*> mInstanceExtensions;
std::vector<const char*> mDeviceExtensions;
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{};
diff --git a/media/Android.bp b/media/Android.bp
index 3181a29..e7d5faf 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -66,7 +66,9 @@
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
similarity index 100%
rename from media/java/android/media/MediaDescription.aidl
rename to media/apex/java/android/media/MediaDescription.aidl
diff --git a/media/java/android/media/MediaDescription.java b/media/apex/java/android/media/MediaDescription.java
similarity index 93%
rename from media/java/android/media/MediaDescription.java
rename to media/apex/java/android/media/MediaDescription.java
index 31079e5..39eeb3e 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 @@
return false;
}
- if (!(o instanceof MediaDescription)){
+ if (!(o instanceof MediaDescription)) {
return false;
}
@@ -375,6 +391,11 @@
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
similarity index 100%
rename from media/java/android/media/Rating.aidl
rename to media/apex/java/android/media/Rating.aidl
diff --git a/media/java/android/media/Rating.java b/media/apex/java/android/media/Rating.java
similarity index 93%
rename from media/java/android/media/Rating.java
rename to media/apex/java/android/media/Rating.java
index 04d5364f..ffe7e48 100644
--- a/media/java/android/media/Rating.java
+++ b/media/apex/java/android/media/Rating.java
@@ -33,7 +33,7 @@
* 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 @@
* 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 @@
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 @@
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 @@
* 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/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index e31ce19..5b3e496 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -84,7 +84,7 @@
VkDevice device;
VkQueue queue;
uint32_t graphics_queue_index;
- uint32_t instance_version;
+ uint32_t api_version;
const char* const* enabled_instance_extension_names;
uint32_t enabled_instance_extension_names_length;
const char* const* enabled_device_extension_names;
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index afe103a..6deb47f 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -102,7 +102,7 @@
.device = init_vk_params.device,
.queue = init_vk_params.queue,
.graphics_queue_index = init_vk_params.graphics_queue_index,
- .instance_version = init_vk_params.instance_version,
+ .api_version = init_vk_params.api_version,
.enabled_instance_extension_names =
init_vk_params.enabled_instance_extension_names,
.enabled_instance_extension_names_length =
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 7eaf04b..0a571c5 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -60,7 +60,6 @@
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.io.IOException;
@@ -107,11 +106,11 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
mCm = ConnectivityManager.from(this);
mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
- mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
mUserAgent =
getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
mUrl = getUrl();
@@ -637,7 +636,7 @@
}
private void logMetricsEvent(int event) {
- MetricsLogger.action(this, event, getPackageName());
+ mCaptivePortal.logEvent(event, getPackageName());
}
private static final SparseArray<String> SSL_ERRORS = new SparseArray<>();
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index 010a810..7fb51b9 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/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index f21561f..b34efc4 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -687,6 +687,15 @@
}
sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
}
+
+ @Override
+ public void logEvent(int eventId, String packageName)
+ throws RemoteException {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ "CaptivePortal");
+ mCallback.logCaptivePortalLoginEvent(eventId, packageName);
+ }
}));
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f523481..ec70f8c 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 3877c90..6ca8261 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 @@
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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3453e79..a18eb76 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -593,11 +593,6 @@
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:process=":ui"
android:visibleToInstantApps="true">
- <intent-filter>
- <action android:name="android.intent.action.CHOOSER_UI" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.VOICE" />
- </intent-filter>
</activity>
<!-- Doze with notifications, run in main sysui process for every user -->
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index f66a57b..9b3d7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -137,8 +137,10 @@
}
public NotificationIconAreaController createNotificationIconAreaController(Context context,
- StatusBar statusBar, StatusBarStateController statusBarStateController) {
- return new NotificationIconAreaController(context, statusBar, statusBarStateController);
+ StatusBar statusBar, StatusBarStateController statusBarStateController,
+ NotificationListener listener) {
+ return new NotificationIconAreaController(context, statusBar, statusBarStateController,
+ listener);
}
public KeyguardIndicationController createKeyguardIndicationController(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 83fd970..8731bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -238,14 +238,14 @@
*/
public void setExpandedBubble(BubbleView bubbleToExpand) {
mExpandedBubble = bubbleToExpand;
- boolean prevExpanded = mIsExpanded;
- mIsExpanded = true;
- if (!prevExpanded) {
+ if (!mIsExpanded) {
// If we weren't previously expanded we should animate open.
animateExpansion(true /* expand */);
} else {
- // If we were expanded just update the views
+ // Otherwise just update the views
+ // TODO: probably animate / page to expanded one
updateExpandedBubble();
+ updatePointerPosition();
requestUpdate();
}
mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
@@ -387,7 +387,6 @@
mIsExpanded = shouldExpand;
updateExpandedBubble();
applyCurrentState();
- //requestUpdate();
mIsAnimating = true;
@@ -400,7 +399,10 @@
if (shouldExpand) {
mBubbleContainer.setController(mExpandedAnimationController);
mExpandedAnimationController.expandFromStack(
- mStackAnimationController.getStackPosition(), updateAfter);
+ mStackAnimationController.getStackPosition(), () -> {
+ updatePointerPosition();
+ updateAfter.run();
+ });
} else {
mBubbleContainer.cancelAllAnimations();
mExpandedAnimationController.collapseBackToStack(
@@ -649,10 +651,7 @@
}
// Bubble with notification as expanded state doesn't need a header / title
mExpandedViewContainer.setHeaderText(null);
-
}
- float pointerPosition = mExpandedBubble.getPosition().x + (mExpandedBubble.getWidth() / 2);
- mExpandedViewContainer.setPointerPosition((int) pointerPosition);
}
private void applyCurrentState() {
@@ -690,6 +689,14 @@
}
}
+ private void updatePointerPosition() {
+ if (mExpandedBubble != null) {
+ float pointerPosition = mExpandedBubble.getPosition().x
+ + (mExpandedBubble.getWidth() / 2f);
+ mExpandedViewContainer.setPointerPosition((int) pointerPosition);
+ }
+ }
+
private void applyRowState(ExpandableNotificationRow view) {
view.reset();
view.setHeadsUp(false);
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 968bd28..60dceef 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 @@
}
}
+ @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/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index db5c244..e2688f1 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.os.UserHandle
import android.util.IconDrawableFactory
@@ -157,16 +158,21 @@
} else {
icons.visibility = View.GONE
}
- item.setOnClickListener(object : View.OnClickListener {
- val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE)
- .putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName)
- .putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid))
- override fun onClick(v: View?) {
- Dependency.get(ActivityStarter::class.java)
- .postStartActivityDismissingKeyguard(intent, 0)
- dismissDialog?.invoke()
- }
- })
+ try {
+ // Check if package exists
+ context.packageManager.getPackageInfo(app.packageName, 0)
+ item.setOnClickListener(object : View.OnClickListener {
+ val intent = Intent(Intent.ACTION_REVIEW_APP_PERMISSION_USAGE)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName)
+ .putExtra(Intent.EXTRA_USER, UserHandle.getUserHandleForUid(app.uid))
+ override fun onClick(v: View?) {
+ Dependency.get(ActivityStarter::class.java)
+ .postStartActivityDismissingKeyguard(intent, 0)
+ dismissDialog?.invoke()
+ }
+ })
+ } catch (e: PackageManager.NameNotFoundException) {}
+
itemList.addView(item)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index f3a46ce..0583843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -20,6 +20,8 @@
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_CHILD_NOTIFICATIONS;
+import android.annotation.SuppressLint;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
@@ -32,10 +34,13 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+import java.util.ArrayList;
+
/**
* This class handles listening to notification updates and passing them along to
* NotificationPresenter to be displayed to the user.
*/
+@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
@@ -47,6 +52,7 @@
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
+ private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
private final Context mContext;
protected NotificationPresenter mPresenter;
@@ -55,6 +61,10 @@
mContext = context;
}
+ public void addNotificationSettingsListener(NotificationSettingsListener listener) {
+ mSettingsListeners.add(listener);
+ }
+
@Override
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
@@ -70,6 +80,8 @@
mEntryManager.addNotification(sbn, currentRanking);
}
});
+ NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
+ onStatusBarIconsBehaviorChanged(noMan.shouldHideSilentStatusBarIcons());
}
@Override
@@ -133,6 +145,13 @@
}
}
+ @Override
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ for (NotificationSettingsListener listener : mSettingsListeners) {
+ listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
+ }
+ }
+
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
@@ -144,4 +163,10 @@
Log.e(TAG, "Unable to register notification listener", e);
}
}
+
+ public interface NotificationSettingsListener {
+
+ default void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { }
+
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e86996a..9e99fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -11,9 +11,7 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import androidx.annotation.NonNull;
-import androidx.collection.ArrayMap;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.ViewClippingUtil;
@@ -21,6 +19,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarStateController;
@@ -28,11 +27,13 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
import java.util.function.Function;
+import androidx.annotation.NonNull;
+import androidx.collection.ArrayMap;
+
/**
* A controller for the space in the status bar to the left of the system icons. This area is
* normally reserved for notifications.
@@ -46,18 +47,19 @@
private final NotificationEntryManager mEntryManager;
private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
private final StatusBarStateController mStatusBarStateController;
- private final TunerService.Tunable mTunable = new TunerService.Tunable() {
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (key.equals(LOW_PRIORITY)) {
- mShowLowPriority = "1".equals(newValue)
- || !NotificationUtils.useNewInterruptionModel(mContext);
- if (mNotificationScrollLayout != null) {
- updateStatusBarIcons();
+ @VisibleForTesting
+ final NotificationListener.NotificationSettingsListener mSettingsListener =
+ new NotificationListener.NotificationSettingsListener() {
+ @Override
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ if (NotificationUtils.useNewInterruptionModel(mContext)) {
+ mShowLowPriority = !hideSilentStatusIcons;
+ if (mNotificationScrollLayout != null) {
+ updateStatusBarIcons();
+ }
+ }
}
- }
- }
- };
+ };
private int mIconSize;
private int mIconHPadding;
@@ -71,7 +73,7 @@
private ViewGroup mNotificationScrollLayout;
private Context mContext;
private boolean mFullyDark;
- private boolean mShowLowPriority;
+ private boolean mShowLowPriority = true;
/**
* Ratio representing being awake or in ambient mode, where 1 is dark and 0 awake.
@@ -90,15 +92,15 @@
view -> view instanceof StatusBarWindowView;
public NotificationIconAreaController(Context context, StatusBar statusBar,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ NotificationListener notificationListener) {
mStatusBar = statusBar;
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mEntryManager = Dependency.get(NotificationEntryManager.class);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
-
- Dependency.get(TunerService.class).addTunable(mTunable, LOW_PRIORITY);
+ notificationListener.addNotificationSettingsListener(mSettingsListener);
initializeNotificationAreaViews(context);
}
@@ -243,6 +245,11 @@
true /* hideRepliedMessages */);
}
+ @VisibleForTesting
+ boolean shouldShouldLowPriorityIcons() {
+ return mShowLowPriority;
+ }
+
/**
* Updates the notification icons for a host layout. This will ensure that the notification
* host layout will have the same icons like the ones in here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1470d0f..ceadd1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -801,7 +801,8 @@
mNotificationLogger.setUpWithContainer(notifListContainer);
mNotificationIconAreaController = SystemUIFactory.getInstance()
- .createNotificationIconAreaController(context, this, mStatusBarStateController);
+ .createNotificationIconAreaController(
+ context, this, mStatusBarStateController, mNotificationListener);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index c880172..bcf5964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -17,10 +17,12 @@
package com.android.systemui.statusbar;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.NotificationManager;
import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
@@ -55,6 +57,7 @@
// Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private NotificationManager mNotificationManager;
private NotificationListener mListener;
private StatusBarNotification mSbn;
@@ -67,6 +70,7 @@
mRemoteInputManager);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(TestableLooper.get(this).getLooper()));
+ mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
@@ -106,4 +110,28 @@
// RankingMap may be modified by plugins.
verify(mEntryManager).updateNotificationRanking(any());
}
+
+ @Test
+ public void testOnConnectReadStatusBarSetting() {
+ NotificationListener.NotificationSettingsListener settingsListener =
+ mock(NotificationListener.NotificationSettingsListener.class);
+ mListener.addNotificationSettingsListener(settingsListener);
+
+ when(mNotificationManager.shouldHideSilentStatusBarIcons()).thenReturn(true);
+
+ mListener.onListenerConnected();
+
+ verify(settingsListener).onStatusBarIconsBehaviorChanged(true);
+ }
+
+ @Test
+ public void testOnStatusBarIconsBehaviorChanged() {
+ NotificationListener.NotificationSettingsListener settingsListener =
+ mock(NotificationListener.NotificationSettingsListener.class);
+ mListener.addNotificationSettingsListener(settingsListener);
+
+ mListener.onStatusBarIconsBehaviorChanged(true);
+
+ verify(settingsListener).onStatusBarIconsBehaviorChanged(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
new file mode 100644
index 0000000..13145b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationIconAreaControllerTest extends SysuiTestCase {
+
+ @Mock
+ private NotificationListener mListener;
+ @Mock
+ StatusBar mStatusBar;
+ @Mock
+ StatusBarStateController mStatusBarStateController;
+ private NotificationIconAreaController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mController = new NotificationIconAreaController(mContext, mStatusBar,
+ mStatusBarStateController, mListener);
+ }
+
+ @Test
+ public void testNotificationIcons_featureOff() {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(), NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
+ assertTrue(mController.shouldShouldLowPriorityIcons());
+ }
+
+ @Test
+ public void testNotificationIcons_featureOn_settingHideIcons() {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(), NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ mController.mSettingsListener.onStatusBarIconsBehaviorChanged(true);
+
+ assertFalse(mController.shouldShouldLowPriorityIcons());
+ }
+
+ @Test
+ public void testNotificationIcons_featureOn_settingShowIcons() {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(), NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ mController.mSettingsListener.onStatusBarIconsBehaviorChanged(false);
+
+ assertTrue(mController.shouldShouldLowPriorityIcons());
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f88d521..47cd917 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 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/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 7d228f1..8b2c1b9 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1863,7 +1863,7 @@
// The agent was running with a stub Application object, so shut it down.
// !!! We hardcode the confirmation UI's package name here rather than use a
// manifest flag! TODO something less direct.
- if (app.uid >= Process.FIRST_APPLICATION_UID
+ if (!UserHandle.isCore(app.uid)
&& !app.packageName.equals("com.android.backupconfirm")) {
if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
mActivityManager.killApplicationProcess(app.processName, app.uid);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 7763d7b..324c2d9 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -45,9 +45,9 @@
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
@@ -1174,7 +1174,7 @@
// b. the app does not state android:killAfterRestore="false" in its manifest
final int appFlags = mCurrentPackage.applicationInfo.flags;
final boolean killAfterRestore =
- (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
+ !UserHandle.isCore(mCurrentPackage.applicationInfo.uid)
&& ((mRestoreDescription.getDataType()
== RestoreDescription.TYPE_FULL_STREAM)
|| ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 2db8928..7ee3047 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -72,7 +71,7 @@
}
// 2. they run as a system-level uid
- if ((app.uid < Process.FIRST_APPLICATION_UID)) {
+ if (UserHandle.isCore(app.uid)) {
// and the backup is happening for non-system user
if (userId != UserHandle.USER_SYSTEM && !app.packageName.equals(
PACKAGE_MANAGER_SENTINEL)) {
diff --git a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
index cce5b3b..97bde9c 100644
--- a/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/RestoreUtils.java
@@ -34,7 +34,7 @@
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Process;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -162,7 +162,7 @@
if (AppBackupUtils.signaturesMatch(sigs, pkg, pmi)) {
// If this is a system-uid app without a declared backup agent,
// don't restore any of the file data.
- if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+ if (UserHandle.isCore(pkg.applicationInfo.uid)
&& (pkg.applicationInfo.backupAgentName == null)) {
Slog.w(TAG, "Installed app " + info.packageName
+ " has restricted uid and no agent");
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index f4b235a..f3b8098 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -53,7 +53,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Bundle;
-import android.os.Process;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.server.backup.FileMetadata;
@@ -404,8 +404,7 @@
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
// Restore system-uid-space packages only if they have
// defined a custom backup agent
- if ((pkgInfo.applicationInfo.uid
- >= Process.FIRST_APPLICATION_UID)
+ if (!UserHandle.isCore(pkgInfo.applicationInfo.uid)
|| (pkgInfo.applicationInfo.backupAgentName != null)) {
// Verify signatures against any installed version; if they
// don't match, then we fall though and ignore the data. The
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index de7c8cc..58263fc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -146,6 +146,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
@@ -2685,6 +2686,11 @@
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
+
+ @Override
+ public void logCaptivePortalLoginEvent(int eventId, String packageName) {
+ new MetricsLogger().action(eventId, packageName);
+ }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 84577f1..4507193 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -21,6 +21,7 @@
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 @@
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 @@
*
* <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 @@
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 @@
// Execute action with least user impact
if (currentObserverToNotify != null) {
- currentObserverToNotify.execute(packageToReport);
+ currentObserverToNotify.execute(packageToReport, packageVersionCode);
}
}
}
@@ -313,14 +315,14 @@
* @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 a634b57..f153ab9 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.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 java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.List;
/**
* Controls error conditions in applications.
@@ -411,7 +413,7 @@
} 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 @@
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 @@
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 580d688..7ae77d5 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.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.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Full information about a particular process that
@@ -974,6 +976,18 @@
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/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b89768a..c9df86e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1241,27 +1241,59 @@
}
}
+ @Nullable
+ private IBinder getDisplayToken(int displayId) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device != null) {
+ return device.getDisplayTokenLocked();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private boolean screenshotInternal(int displayId, Surface outSurface) {
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return false;
+ }
+ SurfaceControl.screenshot(token, outSurface);
+ return true;
+ }
+
@VisibleForTesting
DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
int displayId) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
- return SurfaceControl.getDisplayedContentSamplingAttributes(displayToken);
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return null;
+ }
+ return SurfaceControl.getDisplayedContentSamplingAttributes(token);
}
@VisibleForTesting
boolean setDisplayedContentSamplingEnabledInternal(
int displayId, boolean enable, int componentMask, int maxFrames) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return false;
+ }
return SurfaceControl.setDisplayedContentSamplingEnabled(
- displayToken, enable, componentMask, maxFrames);
+ token, enable, componentMask, maxFrames);
}
@VisibleForTesting
DisplayedContentSample getDisplayedContentSampleInternal(int displayId,
long maxFrames, long timestamp) {
- IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
- return SurfaceControl.getDisplayedContentSample(
- displayToken, maxFrames, timestamp);
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return null;
+ }
+ return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
}
private void clearViewportsLocked() {
@@ -2257,20 +2289,7 @@
@Override
public boolean screenshot(int displayId, Surface outSurface) {
- synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device != null) {
- final IBinder token = device.getDisplayTokenLocked();
- if (token != null) {
- SurfaceControl.screenshot(token, outSurface);
- return true;
- }
- }
- }
- }
- return false;
+ return screenshotInternal(displayId, outSurface);
}
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 669ff2b..5e7ea05 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -234,6 +234,7 @@
private static native void nativeReloadPointerIcons(long ptr);
private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
private static native void nativeSetPointerCapture(long ptr, boolean detached);
+ private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId);
// Input event injection constants defined in InputDispatcher.h.
private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -1890,6 +1891,16 @@
return new String[0];
}
+ /**
+ * Gets if an input device could dispatch to the given display".
+ * @param deviceId The input device id.
+ * @param displayId The specific display id.
+ * @return True if the device could dispatch to the given display, false otherwise.
+ */
+ public boolean canDispatchToDisplay(int deviceId, int displayId) {
+ return nativeCanDispatchToDisplay(mPtr, deviceId, displayId);
+ }
+
// Native callback.
private int getKeyRepeatTimeout() {
return ViewConfiguration.getKeyRepeatTimeout();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 467b192..ba187c0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,6 +198,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -2366,6 +2367,28 @@
}
@Override
+ public boolean shouldHideSilentStatusIcons(String callingPkg) {
+ checkCallerIsSameApp(callingPkg);
+
+ if (isCallerSystemOrPhone()
+ || mListeners.isListenerPackage(callingPkg)) {
+ return mPreferencesHelper.shouldHideSilentStatusIcons();
+ } else {
+ throw new SecurityException("Only available for notification listeners");
+ }
+ }
+
+ @Override
+ public void setHideSilentStatusIcons(boolean hide) {
+ checkCallerIsSystem();
+
+ mPreferencesHelper.setHideSilentStatusIcons(hide);
+ handleSavePolicyFile();
+
+ mListeners.onStatusBarIconsBehaviorChanged(hide);
+ }
+
+ @Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -3633,6 +3656,22 @@
}
@Override
+ public ComponentName getAllowedNotificationAssistantForUser(int userId) {
+ checkCallerIsSystem();
+ List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
+ if (allowedComponents.size() > 1) {
+ throw new IllegalStateException(
+ "At most one NotificationAssistant: " + allowedComponents.size());
+ }
+ return CollectionUtils.firstOrNull(allowedComponents);
+ }
+
+ @Override
+ public ComponentName getAllowedNotificationAssistant() {
+ return getAllowedNotificationAssistantForUser(getCallingUserHandle().getIdentifier());
+ }
+
+ @Override
public boolean isNotificationListenerAccessGranted(ComponentName listener) {
Preconditions.checkNotNull(listener);
checkCallerIsSystemOrSameApp(listener.getPackageName());
@@ -3700,8 +3739,15 @@
@Override
public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant,
int userId, boolean granted) throws RemoteException {
- Preconditions.checkNotNull(assistant);
checkCallerIsSystemOrShell();
+ if (assistant == null) {
+ ComponentName allowedAssistant = CollectionUtils.firstOrNull(
+ mAssistants.getAllowedComponents(userId));
+ if (allowedAssistant != null) {
+ setNotificationAssistantAccessGrantedForUser(allowedAssistant, userId, false);
+ }
+ return;
+ }
final long identity = Binder.clearCallingIdentity();
try {
if (mAllowedManagedServicePackages.test(assistant.getPackageName())) {
@@ -7242,6 +7288,26 @@
}
}
}
+
+ @Override
+ protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
+ boolean isPrimary, boolean enabled) {
+ // Ensures that only one component is enabled at a time
+ if (enabled) {
+ List<ComponentName> allowedComponents = getAllowedComponents(userId);
+ if (!allowedComponents.isEmpty()) {
+ ComponentName currentComponent = CollectionUtils.firstOrNull(allowedComponents);
+ if (currentComponent.flattenToString().equals(pkgOrComponent)) return;
+ try {
+ getBinderService().setNotificationAssistantAccessGrantedForUser(
+ currentComponent, userId, false);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+ super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled);
+ }
}
public class NotificationListeners extends ManagedServices {
@@ -7324,6 +7390,20 @@
return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
}
+ public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+ for (final ManagedServiceInfo info : getServices()) {
+ mHandler.post(() -> {
+ final INotificationListener listener = (INotificationListener) info.service;
+ try {
+ listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener "
+ + "(hideSilentStatusIcons): " + listener, ex);
+ }
+ });
+ }
+ }
+
/**
* asynchronously notify all listeners about a new notification
*
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3f0043c..6ed4f5c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -76,6 +76,7 @@
private static final String TAG_CHANNEL = "channel";
private static final String TAG_GROUP = "channelGroup";
private static final String TAG_DELEGATE = "delegate";
+ private static final String TAG_STATUS_ICONS = "status_icons";
private static final String ATT_VERSION = "version";
private static final String ATT_NAME = "name";
@@ -89,10 +90,13 @@
private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
private static final String ATT_ENABLED = "enabled";
private static final String ATT_USER_ALLOWED = "allowed";
+ private static final String ATT_HIDE_SILENT = "hide_silent";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
+ @VisibleForTesting
+ static final boolean DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS = false;
private static final boolean DEFAULT_SHOW_BADGE = true;
private static final boolean DEFAULT_ALLOW_BUBBLE = true;
private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false;
@@ -124,6 +128,7 @@
private SparseBooleanArray mBadgingEnabled;
private boolean mAreChannelsBypassingDnd;
+ private boolean mHideSilentStatusBarIcons;
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
ZenModeHelper zenHelper) {
@@ -143,9 +148,8 @@
String tag = parser.getName();
if (!TAG_RANKING.equals(tag)) return;
synchronized (mPackagePreferences) {
- // Clobber groups and channels with the xml, but don't delete other data that wasn't present
-
- // at the time of serialization.
+ // Clobber groups and channels with the xml, but don't delete other data that wasn't
+ // present at the time of serialization.
mRestoredWithoutUids.clear();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
@@ -153,7 +157,10 @@
return;
}
if (type == XmlPullParser.START_TAG) {
- if (TAG_PACKAGE.equals(tag)) {
+ if (TAG_STATUS_ICONS.equals(tag)) {
+ mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
+ parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
+ } else if (TAG_PACKAGE.equals(tag)) {
int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
String name = parser.getAttributeValue(null, ATT_NAME);
if (!TextUtils.isEmpty(name)) {
@@ -375,6 +382,11 @@
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
out.startTag(null, TAG_RANKING);
out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+ if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS) {
+ out.startTag(null, TAG_STATUS_ICONS);
+ out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
+ out.endTag(null, TAG_STATUS_ICONS);
+ }
synchronized (mPackagePreferences) {
final int N = mPackagePreferences.size();
@@ -781,6 +793,14 @@
}
}
+ public boolean shouldHideSilentStatusIcons() {
+ return mHideSilentStatusBarIcons;
+ }
+
+ public void setHideSilentStatusIcons(boolean hide) {
+ mHideSilentStatusBarIcons = hide;
+ }
+
public void lockChannelsForOEM(String[] appOrChannelList) {
if (appOrChannelList == null) {
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f1eeeb..dc18dfc 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.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 @@
return getStagedSessions();
case "uninstall-system-updates":
return uninstallSystemUpdates();
+ case "rollback-app":
+ return runRollbackApp();
default: {
String nextArg = getNextArg();
if (nextArg == null) {
@@ -348,6 +354,55 @@
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 0000000..8dd0760
--- /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 4b5e764..ba6cddd 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 @@
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 289618e..5eb137b 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.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.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 @@
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 @@
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 @@
}
}
+ 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 @@
// 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 @@
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);
- }
- }
+ final IntArray pendingBackups = mUserdataHelper.snapshotAppData(packageName,
+ installedUsers);
- PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion);
-
+ PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion,
+ pendingBackups, new ArrayList<>());
RollbackData data;
try {
synchronized (mLock) {
@@ -760,40 +790,24 @@
}
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;
- }
-
- try {
- mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode,
- seInfo, userId, storageFlags);
- } catch (InstallerException ie) {
- Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie);
- }
-
+ final boolean changedRollbackData = mUserdataHelper.restoreAppData(packageName,
+ rollbackData, userId, appId, ceDataInode, seInfo);
+ final PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
pmi.finishPackageInstall(token, false);
+
+ // 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);
+ }
+ }
});
}
@@ -900,10 +914,8 @@
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 @@
}
}
}
+
+ 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 2880103..b3cc6de 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
+import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -41,12 +42,10 @@
private static final String TAG = "RollbackPackageHealthObserver";
private static final String NAME = "rollback-observer";
private Context mContext;
- private RollbackManager mRollbackManager;
private Handler mHandler;
RollbackPackageHealthObserver(Context context) {
mContext = context;
- mRollbackManager = mContext.getSystemService(RollbackManager.class);
HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
handlerThread.start();
mHandler = handlerThread.getThreadHandler();
@@ -54,8 +53,10 @@
}
@Override
- public int onHealthCheckFailed(String packageName) {
- RollbackInfo rollback = getAvailableRollback(packageName);
+ public int onHealthCheckFailed(String packageName, long versionCode) {
+ RollbackInfo rollback =
+ getAvailableRollback(mContext.getSystemService(RollbackManager.class),
+ packageName, versionCode);
if (rollback == null) {
// Don't handle the notification, no rollbacks available for the package
return PackageHealthObserverImpact.USER_IMPACT_NONE;
@@ -65,8 +66,9 @@
}
@Override
- public boolean execute(String packageName) {
- RollbackInfo rollback = getAvailableRollback(packageName);
+ public boolean execute(String packageName, long versionCode) {
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ RollbackInfo rollback = getAvailableRollback(rollbackManager, packageName, versionCode);
if (rollback == null) {
// Expected a rollback to be available, what happened?
return false;
@@ -86,12 +88,9 @@
});
// TODO(zezeozue): Log initiated metrics
- // TODO: Pass the package as a cause package instead of using
- // Collections.emptyList once the version of the failing package is
- // easily available.
mHandler.post(() ->
- mRollbackManager.commitRollback(rollback.getRollbackId(),
- Collections.emptyList(),
+ rollbackManager.commitRollback(rollback.getRollbackId(),
+ Collections.singletonList(new VersionedPackage(packageName, versionCode)),
rollbackReceiver.getIntentSender()));
// Assume rollback executed successfully
return true;
@@ -110,11 +109,13 @@
PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
- private RollbackInfo getAvailableRollback(String packageName) {
- for (RollbackInfo rollback : mRollbackManager.getAvailableRollbacks()) {
+ private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
+ String packageName, long versionCode) {
+ for (RollbackInfo rollback : rollbackManager.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 98ebb09..c70f47d 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 @@
}
/**
+ * 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 @@
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 @@
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 3426ba6..080f965 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_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_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.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.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 @@
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.
@@ -1657,35 +1665,42 @@
final int len = devices != null ? devices.length : 0;
for (int i = 0; i < len; i++) {
InputDevice device = devices[i];
- if (!device.isVirtual()) {
- final int sources = device.getSources();
- final int presenceFlag = device.isExternal() ?
- WindowManagerPolicy.PRESENCE_EXTERNAL :
- WindowManagerPolicy.PRESENCE_INTERNAL;
+ // Ignore virtual input device.
+ if (device.isVirtual()) {
+ continue;
+ }
- // TODO(multi-display): Configure on per-display basis.
- if (mWmService.mIsTouchDevice) {
- if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
- InputDevice.SOURCE_TOUCHSCREEN) {
- config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
- }
- } else {
- config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
- }
+ // Check if input device can dispatch events to current display.
+ // If display type is virtual, will follow the default display.
+ if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(),
+ displayInfo.type == Display.TYPE_VIRTUAL ? DEFAULT_DISPLAY : mDisplayId)) {
+ continue;
+ }
- if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
- config.navigation = Configuration.NAVIGATION_TRACKBALL;
- navigationPresence |= presenceFlag;
- } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
- && config.navigation == Configuration.NAVIGATION_NONAV) {
- config.navigation = Configuration.NAVIGATION_DPAD;
- navigationPresence |= presenceFlag;
- }
+ final int sources = device.getSources();
+ final int presenceFlag = device.isExternal()
+ ? WindowManagerPolicy.PRESENCE_EXTERNAL : WindowManagerPolicy.PRESENCE_INTERNAL;
- if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
- config.keyboard = Configuration.KEYBOARD_QWERTY;
- keyboardPresence |= presenceFlag;
+ if (mWmService.mIsTouchDevice) {
+ if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
+ config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
}
+ } else {
+ config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+ }
+
+ if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
+ config.navigation = Configuration.NAVIGATION_TRACKBALL;
+ navigationPresence |= presenceFlag;
+ } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
+ && config.navigation == Configuration.NAVIGATION_NONAV) {
+ config.navigation = Configuration.NAVIGATION_DPAD;
+ navigationPresence |= presenceFlag;
+ }
+
+ if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
+ config.keyboard = Configuration.KEYBOARD_QWERTY;
+ keyboardPresence |= presenceFlag;
}
}
@@ -2410,10 +2425,7 @@
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 +2437,18 @@
}
}
+ /**
+ * 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 +3610,13 @@
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 +4861,43 @@
|| 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 cbc936f..0a4ab67 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 @@
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 2e5df45..dd94af6 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 @@
// 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 @@
@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 752c24e..975e62a 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 @@
/**
* 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 4f12010..62e7200 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 @@
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 @@
}
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 @@
}
}
cropRegionToStackBoundsIfNeeded(outRegion);
+ subtractTouchExcludeRegionIfNeeded(outRegion);
}
private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2855,6 +2874,22 @@
}
/**
+ * 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 @@
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/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ff0b0d6..33317b5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1652,6 +1652,13 @@
im->setCustomPointerIcon(spriteIcon);
}
+static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jint deviceId, jint displayId) {
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -1726,6 +1733,8 @@
(void*) nativeReloadPointerIcons },
{ "nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V",
(void*) nativeSetCustomPointerIcon },
+ { "nativeCanDispatchToDisplay", "(JII)Z",
+ (void*) nativeCanDispatchToDisplay },
};
#define FIND_CLASS(var, className) \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 51bdbb3..8f223b2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -74,20 +74,14 @@
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
-
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
- .PROVISIONING_ENTRY_POINT_ADB;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
- .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
-
-
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -240,11 +234,11 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.internal.util.StatLogger;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
@@ -14207,7 +14201,8 @@
+ "calendar APIs", packageName));
return false;
}
- final Intent intent = new Intent(CalendarContract.ACTION_VIEW_WORK_CALENDAR_EVENT);
+ final Intent intent = new Intent(
+ CalendarContract.ACTION_VIEW_MANAGED_PROFILE_CALENDAR_EVENT);
intent.setPackage(packageName);
intent.putExtra(CalendarContract.EXTRA_EVENT_ID, eventId);
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start);
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 0000000..33cbf7a
--- /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/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 1de1e4e..0b488c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.INotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
@@ -36,22 +37,17 @@
import android.util.IntArray;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -65,6 +61,8 @@
private UserManager mUm;
@Mock
NotificationManagerService mNm;
+ @Mock
+ private INotificationManager mINm;
NotificationAssistants mAssistants;
@@ -83,6 +81,7 @@
getContext().setMockPackageManager(mPm);
getContext().addMockSystemService(Context.USER_SERVICE, mUm);
mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));
+ when(mNm.getBinderService()).thenReturn(mINm);
List<ResolveInfo> approved = new ArrayList<>();
ResolveInfo resolve = new ResolveInfo();
@@ -136,4 +135,30 @@
verify(mAssistants, times(1)).addApprovedList(
new ComponentName("b", "b").flattenToString(),10, true);
}
+
+ @Test
+ public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
+ ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
+ ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
+ mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
+ true);
+ verify(mINm, never()).setNotificationAssistantAccessGrantedForUser(any(ComponentName.class),
+ eq(mZero.id), anyBoolean());
+
+ mAssistants.setPackageOrComponentEnabled(component2.flattenToString(), mZero.id, true,
+ true);
+ verify(mINm, times(1)).setNotificationAssistantAccessGrantedForUser(component1, mZero.id,
+ false);
+ }
+
+ @Test
+ public void testSetPackageOrComponentEnabled_samePackage() throws Exception {
+ ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
+ mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
+ true);
+ mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
+ true);
+ verify(mINm, never()).setNotificationAssistantAccessGrantedForUser(any(ComponentName.class),
+ eq(mZero.id), anyBoolean());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 5d46f0f..4cae3b3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2029,6 +2029,31 @@
}
@Test
+ public void testGetAssistantAllowedForUser() throws Exception {
+ UserHandle user = UserHandle.of(10);
+ try {
+ mBinderService.getAllowedNotificationAssistantForUser(user.getIdentifier());
+ } catch (IllegalStateException e) {
+ if (!e.getMessage().contains("At most one NotificationAssistant")) {
+ throw e;
+ }
+ }
+ verify(mAssistants, times(1)).getAllowedComponents(user.getIdentifier());
+ }
+
+ @Test
+ public void testGetAssistantAllowed() throws Exception {
+ try {
+ mBinderService.getAllowedNotificationAssistant();
+ } catch (IllegalStateException e) {
+ if (!e.getMessage().contains("At most one NotificationAssistant")) {
+ throw e;
+ }
+ }
+ verify(mAssistants, times(1)).getAllowedComponents(0);
+ }
+
+ @Test
public void testSetDndAccessForUser() throws Exception {
UserHandle user = UserHandle.of(10);
ComponentName c = ComponentName.unflattenFromString("package/Component");
@@ -2089,6 +2114,54 @@
}
@Test
+ public void testSetAssistantAccess_nullWithAllowedAssistant() throws Exception {
+ ArrayList<ComponentName> componentList = new ArrayList<>();
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ componentList.add(c);
+ when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+
+ try {
+ mBinderService.setNotificationAssistantAccessGranted(null, true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, true, false);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), 0, false, false);
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
+ public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception {
+ UserHandle user = UserHandle.of(10);
+ ArrayList<ComponentName> componentList = new ArrayList<>();
+ ComponentName c = ComponentName.unflattenFromString("package/Component");
+ componentList.add(c);
+ when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList);
+
+ try {
+ mBinderService.setNotificationAssistantAccessGrantedForUser(
+ null, user.getIdentifier(), true);
+ } catch (SecurityException e) {
+ if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
+ throw e;
+ }
+ }
+
+ verify(mAssistants, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, false);
+ verify(mConditionProviders, times(1)).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), false, false);
+ verify(mListeners, never()).setPackageOrComponentEnabled(
+ any(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testSetDndAccess() throws Exception {
ComponentName c = ComponentName.unflattenFromString("package/Component");
try {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index bde9dde..47ec390 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -2006,6 +2006,28 @@
}
@Test
+ public void testXml_statusBarIcons_default() throws Exception {
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
+ mHelper.shouldHideSilentStatusIcons());
+ }
+
+ @Test
+ public void testXml_statusBarIcons() throws Exception {
+ mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
+ mHelper.shouldHideSilentStatusIcons());
+ }
+
+ @Test
public void testSetNotificationDelegate() {
mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 86af642..c1c598d 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.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 @@
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 @@
// 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 @@
* 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 @@
// 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 @@
}
/**
+ * 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 @@
// 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 @@
* <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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
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;
}
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
index 02a439b..2310c82 100644
--- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/CrashingMainActivity.java
@@ -17,17 +17,30 @@
package com.android.tests.rollback.testapp;
import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
/**
* A crashing test app for testing apk rollback support.
*/
public class CrashingMainActivity extends Activity {
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ incrementCountAndBroadcast();
throw new RuntimeException("Intended force crash");
}
+
+ public void incrementCountAndBroadcast() {
+ SharedPreferences preferences = getSharedPreferences("prefs", Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ int count = preferences.getInt("crash_count", 0);
+ editor.putInt("crash_count", ++count).commit();
+
+ Intent intent = new Intent("com.android.tests.rollback.CRASH");
+ intent.putExtra("count", count);
+ sendBroadcast(intent);
+ }
}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index e128a6c..4b277ae 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -17,6 +17,7 @@
package com.android.tests.rollback;
import android.Manifest;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -36,7 +37,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -45,6 +45,7 @@
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
@@ -413,7 +414,6 @@
/**
* Test that app user data is rolled back.
- * TODO: Stop ignoring this test once user data rollback is supported.
*/
@Test
public void testUserDataRollback() throws Exception {
@@ -568,9 +568,7 @@
}
/**
- * Test rollback of multi-package installs.
- * TODO: Stop ignoring this test once support for multi-package rollback
- * is implemented.
+ * Test rollback of multi-package installs is implemented.
*/
@Test
public void testMultiPackage() throws Exception {
@@ -630,18 +628,20 @@
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
- // TODO(zezeozue): Stop ignoring after fixing race between rolling back and testing version
/**
* Test bad update automatic rollback.
*/
- @Ignore("Flaky")
@Test
public void testBadUpdateRollback() throws Exception {
+ BroadcastReceiver crashCountReceiver = null;
+ Context context = InstrumentationRegistry.getContext();
try {
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.KILL_BACKGROUND_PROCESSES,
+ Manifest.permission.RESTART_PACKAGES);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
// Prep installation of the test apps.
@@ -669,23 +669,52 @@
rm.getAvailableRollbacks(), TEST_APP_B);
assertRollbackInfoEquals(TEST_APP_B, 2, 1, rollbackB);
+ BlockingQueue<Integer> crashQueue = new SynchronousQueue<>();
+
+ IntentFilter crashCountFilter = new IntentFilter();
+ crashCountFilter.addAction("com.android.tests.rollback.CRASH");
+ crashCountFilter.addCategory(Intent.CATEGORY_DEFAULT);
+
+ crashCountReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ // Sleep long enough for packagewatchdog to be notified of crash
+ Thread.sleep(1000);
+ // Kill app and close AppErrorDialog
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ am.killBackgroundProcesses(TEST_APP_A);
+ // Allow another package launch
+ crashQueue.offer(intent.getIntExtra("count", 0), 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail("Failed to communicate with test app");
+ }
+ }
+ };
+ context.registerReceiver(crashCountReceiver, crashCountFilter);
+
// Start apps PackageWatchdog#TRIGGER_FAILURE_COUNT times so TEST_APP_A crashes
- for (int i = 0; i < 5; i++) {
+ Integer crashCount = null;
+ do {
RollbackTestUtils.launchPackage(TEST_APP_A);
- Thread.sleep(1000);
- }
- Thread.sleep(1000);
+ crashCount = crashQueue.poll(5, TimeUnit.SECONDS);
+ if (crashCount == null) {
+ fail("Timed out waiting for crash signal from test app");
+ }
+ } while(crashCount < 5);
// TEST_APP_A is automatically rolled back by the RollbackPackageHealthObserver
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
// Instrumented app is still the package installer
- Context context = InstrumentationRegistry.getContext();
String installer = context.getPackageManager().getInstallerPackageName(TEST_APP_A);
assertEquals(INSTRUMENTED_APP, installer);
// TEST_APP_B is untouched
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
} finally {
RollbackTestUtils.dropShellPermissionIdentity();
+ if (crashCountReceiver != null) {
+ context.unregisterReceiver(crashCountReceiver);
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 17847ea..145e269 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -23,17 +23,9 @@
import android.text.TextUtils;
import android.util.Log;
-import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -1126,6 +1118,12 @@
String value = PASSWORD_KEY.equals(key) ? "<removed>" : mFields.get(key);
sb.append(key).append(" ").append(value).append("\n");
}
+ if (mEapMethod >= 0 && mEapMethod < Eap.strings.length) {
+ sb.append("eap_method: ").append(Eap.strings[mEapMethod]).append("\n");
+ }
+ if (mPhase2Method > 0 && mPhase2Method < Phase2.strings.length) {
+ sb.append("phase2_method: ").append(Phase2.strings[mPhase2Method]).append("\n");
+ }
return sb.toString();
}