diff options
60 files changed, 1657 insertions, 463 deletions
diff --git a/api/Android.bp b/api/Android.bp new file mode 100644 index 000000000000..54ff82c97e17 --- /dev/null +++ b/api/Android.bp @@ -0,0 +1,7 @@ +genrule { + name: "current-api-xml", + tools: ["metalava"], + srcs: ["current.txt"], + out: ["current.api"], + cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)", +} diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index da4c0386a76f..8cae56d1acf4 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -240,7 +240,8 @@ public class AppOpsManager { public @interface UidState {} /** - * Uid state: The UID is a foreground persistent app. + * Uid state: The UID is a foreground persistent app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -248,7 +249,8 @@ public class AppOpsManager { public static final int UID_STATE_PERSISTENT = 100; /** - * Uid state: The UID is top foreground app. + * Uid state: The UID is top foreground app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -257,6 +259,7 @@ public class AppOpsManager { /** * Uid state: The UID is running a foreground service of location type. + * The lower the UID state the more important the UID is for the user. * @hide */ @TestApi @@ -264,7 +267,8 @@ public class AppOpsManager { public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; /** - * Uid state: The UID is running a foreground service. + * Uid state: The UID is running a foreground service. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -279,7 +283,8 @@ public class AppOpsManager { public static final int UID_STATE_MAX_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE; /** - * Uid state: The UID is a foreground app. + * Uid state: The UID is a foreground app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -287,7 +292,8 @@ public class AppOpsManager { public static final int UID_STATE_FOREGROUND = 500; /** - * Uid state: The UID is a background app. + * Uid state: The UID is a background app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi @@ -295,7 +301,8 @@ public class AppOpsManager { public static final int UID_STATE_BACKGROUND = 600; /** - * Uid state: The UID is a cached app. + * Uid state: The UID is a cached app. The lower the UID + * state the more important the UID is for the user. * @hide */ @TestApi diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 1166cb57cca7..77a777024a21 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -132,6 +132,9 @@ public class DownloadManager { */ public final static String COLUMN_STATUS = Downloads.Impl.COLUMN_STATUS; + /** {@hide} */ + public static final String COLUMN_FILE_NAME_HINT = Downloads.Impl.COLUMN_FILE_NAME_HINT; + /** * Provides more detail on the status of the download. Its meaning depends on the value of * {@link #COLUMN_STATUS}. @@ -173,6 +176,9 @@ public class DownloadManager { @TestApi public static final String COLUMN_MEDIASTORE_URI = Downloads.Impl.COLUMN_MEDIASTORE_URI; + /** {@hide} */ + public static final String COLUMN_DESTINATION = Downloads.Impl.COLUMN_DESTINATION; + /** * @hide */ @@ -340,26 +346,22 @@ public class DownloadManager { */ @UnsupportedAppUsage public static final String[] UNDERLYING_COLUMNS = new String[] { - Downloads.Impl._ID, - Downloads.Impl._DATA + " AS " + COLUMN_LOCAL_FILENAME, - Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, - Downloads.Impl.COLUMN_DESTINATION, - Downloads.Impl.COLUMN_TITLE, - Downloads.Impl.COLUMN_DESCRIPTION, - Downloads.Impl.COLUMN_URI, - Downloads.Impl.COLUMN_STATUS, - Downloads.Impl.COLUMN_FILE_NAME_HINT, - Downloads.Impl.COLUMN_MIME_TYPE + " AS " + COLUMN_MEDIA_TYPE, - Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + COLUMN_TOTAL_SIZE_BYTES, - Downloads.Impl.COLUMN_LAST_MODIFICATION + " AS " + COLUMN_LAST_MODIFIED_TIMESTAMP, - Downloads.Impl.COLUMN_CURRENT_BYTES + " AS " + COLUMN_BYTES_DOWNLOADED_SO_FAR, - Downloads.Impl.COLUMN_ALLOW_WRITE, - /* add the following 'computed' columns to the cursor. - * they are not 'returned' by the database, but their inclusion - * eliminates need to have lot of methods in CursorTranslator - */ - "'placeholder' AS " + COLUMN_LOCAL_URI, - "'placeholder' AS " + COLUMN_REASON + DownloadManager.COLUMN_ID, + DownloadManager.COLUMN_LOCAL_FILENAME, + DownloadManager.COLUMN_MEDIAPROVIDER_URI, + DownloadManager.COLUMN_DESTINATION, + DownloadManager.COLUMN_TITLE, + DownloadManager.COLUMN_DESCRIPTION, + DownloadManager.COLUMN_URI, + DownloadManager.COLUMN_STATUS, + DownloadManager.COLUMN_FILE_NAME_HINT, + DownloadManager.COLUMN_MEDIA_TYPE, + DownloadManager.COLUMN_TOTAL_SIZE_BYTES, + DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP, + DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR, + DownloadManager.COLUMN_ALLOW_WRITE, + DownloadManager.COLUMN_LOCAL_URI, + DownloadManager.COLUMN_REASON }; /** diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index 0ccd49f2e028..5e530eedd818 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -355,7 +355,8 @@ public abstract class SliceProvider extends ContentProvider { @Override public Bundle call(String method, String arg, Bundle extras) { if (method.equals(METHOD_SLICE)) { - Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + Uri uri = getUriWithoutUserId(validateIncomingUriOrNull( + extras.getParcelable(EXTRA_BIND_URI))); List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS); String callingPackage = getCallingPackage(); @@ -369,7 +370,7 @@ public abstract class SliceProvider extends ContentProvider { } else if (method.equals(METHOD_MAP_INTENT)) { Intent intent = extras.getParcelable(EXTRA_INTENT); if (intent == null) return null; - Uri uri = onMapIntentToUri(intent); + Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent)); List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS); Bundle b = new Bundle(); if (uri != null) { @@ -383,24 +384,27 @@ public abstract class SliceProvider extends ContentProvider { } else if (method.equals(METHOD_MAP_ONLY_INTENT)) { Intent intent = extras.getParcelable(EXTRA_INTENT); if (intent == null) return null; - Uri uri = onMapIntentToUri(intent); + Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent)); Bundle b = new Bundle(); b.putParcelable(EXTRA_SLICE, uri); return b; } else if (method.equals(METHOD_PIN)) { - Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + Uri uri = getUriWithoutUserId(validateIncomingUriOrNull( + extras.getParcelable(EXTRA_BIND_URI))); if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Only the system can pin/unpin slices"); } handlePinSlice(uri); } else if (method.equals(METHOD_UNPIN)) { - Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + Uri uri = getUriWithoutUserId(validateIncomingUriOrNull( + extras.getParcelable(EXTRA_BIND_URI))); if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Only the system can pin/unpin slices"); } handleUnpinSlice(uri); } else if (method.equals(METHOD_GET_DESCENDANTS)) { - Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + Uri uri = getUriWithoutUserId( + validateIncomingUriOrNull(extras.getParcelable(EXTRA_BIND_URI))); Bundle b = new Bundle(); b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS, new ArrayList<>(handleGetDescendants(uri))); @@ -416,6 +420,10 @@ public abstract class SliceProvider extends ContentProvider { return super.call(method, arg, extras); } + private Uri validateIncomingUriOrNull(Uri uri) { + return uri == null ? null : validateIncomingUri(uri); + } + private Collection<Uri> handleGetDescendants(Uri uri) { mCallback = "onGetSliceDescendants"; return onGetSliceDescendants(uri); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index a6b95a95dcd8..4a5ea03b4530 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -854,11 +854,16 @@ public abstract class Context { * to any callers for the same name, meaning they will see each other's * edits as soon as they are made. * - * This method is thead-safe. + * <p>This method is thread-safe. * - * @param name Desired preferences file. If a preferences file by this name - * does not exist, it will be created when you retrieve an - * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). + * <p>If the preferences directory does not already exist, it will be created when this method + * is called. + * + * <p>If a preferences file by this name does not exist, it will be created when you retrieve an + * editor ({@link SharedPreferences#edit()}) and then commit changes ({@link + * SharedPreferences.Editor#commit()} or {@link SharedPreferences.Editor#apply()}). + * + * @param name Desired preferences file. * @param mode Operating mode. * * @return The single {@link SharedPreferences} instance that can be used diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 037a149bfe37..c74daa8eadfc 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -503,9 +503,38 @@ public class LauncherApps { } /** - * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and - * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. Result may include - * synthesized activities like app details Activity injected by system. + * Retrieves a list of activities that specify {@link Intent#ACTION_MAIN} and + * {@link Intent#CATEGORY_LAUNCHER}, across all apps, for a specified user. If an app doesn't + * have any activities that specify <code>ACTION_MAIN</code> or <code>CATEGORY_LAUNCHER</code>, + * the system adds a synthesized activity to the list. This synthesized activity represents the + * app's details page within system settings. + * + * <p class="note"><b>Note: </b>It's possible for system apps, such as app stores, to prevent + * the system from adding synthesized activities to the returned list.</p> + * + * <p>As of <a href="/reference/android/os/Build.VERSION_CODES.html#Q">Android Q</a>, at least + * one of the app's activities or synthesized activities appears in the returned list unless the + * app satisfies at least one of the following conditions:</p> + * <ul> + * <li>The app is a system app.</li> + * <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>. + * </li> + * <li>The <code><application></code> tag in the app's manifest doesn't contain any child + * elements that represent + * <a href="/guide/components/fundamentals#DeclaringComponents">app components</a>.</li> + * </ul> + * + * <p>Additionally, the system hides synthesized activities for some or all apps in the + * following enterprise-related cases:</p> + * <ul> + * <li>If the device is a + * <a href="https://developers.google.com/android/work/overview#company-owned-devices-for-knowledge-workers">fully + * managed device</a>, no synthesized activities for any app appear in the returned list.</li> + * <li>If the current user has a + * <a href="https://developers.google.com/android/work/overview#employee-owned-devices-byod">work + * profile</a>, no synthesized activities for the user's work apps appear in the returned + * list.</li> + * </ul> * * @param packageName The specific package to query. If null, it checks all installed packages * in the profile. diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 861ae7ba122e..ac1cbd4619df 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -23,6 +23,7 @@ import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN; import static android.content.ConfigurationProto.KEYBOARD; import static android.content.ConfigurationProto.KEYBOARD_HIDDEN; import static android.content.ConfigurationProto.LOCALES; +import static android.content.ConfigurationProto.LOCALE_LIST; import static android.content.ConfigurationProto.MCC; import static android.content.ConfigurationProto.MNC; import static android.content.ConfigurationProto.NAVIGATION; @@ -1111,7 +1112,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration protoOutputStream.write(MCC, mcc); protoOutputStream.write(MNC, mnc); if (mLocaleList != null) { - mLocaleList.writeToProto(protoOutputStream, LOCALES); + protoOutputStream.write(LOCALE_LIST, mLocaleList.toLanguageTags()); } protoOutputStream.write(SCREEN_LAYOUT, screenLayout); protoOutputStream.write(COLOR_MODE, colorMode); @@ -1222,7 +1223,15 @@ public final class Configuration implements Parcelable, Comparable<Configuration .setVariant(variant) .setScript(script) .build(); - list.add(locale); + // Log a WTF here if a repeated locale is found to avoid throwing an + // exception in system server when LocaleList is created below + final int inListIndex = list.indexOf(locale); + if (inListIndex != -1) { + Slog.wtf(TAG, "Repeated locale (" + list.get(inListIndex) + ")" + + " found when trying to add: " + locale.toString()); + } else { + list.add(locale); + } } catch (IllformedLocaleException e) { Slog.e(TAG, "readFromProto error building locale with: " + "language-" + language + ";country-" + country @@ -1275,6 +1284,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration case (int) WINDOW_CONFIGURATION: windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION); break; + case (int) LOCALE_LIST: + try { + setLocales(LocaleList.forLanguageTags(protoInputStream.readString( + LOCALE_LIST))); + } catch (Exception e) { + Slog.e(TAG, "error parsing locale list in configuration.", e); + } + break; } } } finally { diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 3523e956656a..58901798b5f7 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -30,11 +30,14 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import com.android.internal.util.ArrayUtils; + import libcore.util.EmptyArray; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -49,14 +52,11 @@ import java.util.regex.Pattern; public class SQLiteQueryBuilder { private static final String TAG = "SQLiteQueryBuilder"; - private static final Pattern sLimitPattern = - Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private static final Pattern sAggregationPattern = Pattern.compile( "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)"); private Map<String, String> mProjectionMap = null; private List<Pattern> mProjectionGreylist = null; - private boolean mProjectionAggregationAllowed = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private String mTables = ""; @@ -65,7 +65,12 @@ public class SQLiteQueryBuilder { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mDistinct; private SQLiteDatabase.CursorFactory mFactory; - private boolean mStrict; + + private static final int STRICT_PARENTHESES = 1 << 0; + private static final int STRICT_COLUMNS = 1 << 1; + private static final int STRICT_GRAMMAR = 1 << 2; + + private int mStrictFlags; public SQLiteQueryBuilder() { mDistinct = false; @@ -208,14 +213,23 @@ public class SQLiteQueryBuilder { return mProjectionGreylist; } - /** {@hide} */ + /** + * @deprecated Projection aggregation is now always allowed + * + * @hide + */ + @Deprecated public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) { - mProjectionAggregationAllowed = projectionAggregationAllowed; } - /** {@hide} */ + /** + * @deprecated Projection aggregation is now always allowed + * + * @hide + */ + @Deprecated public boolean isProjectionAggregationAllowed() { - return mProjectionAggregationAllowed; + return true; } /** @@ -258,8 +272,12 @@ public class SQLiteQueryBuilder { * </ul> * By default, this value is false. */ - public void setStrict(boolean flag) { - mStrict = flag; + public void setStrict(boolean strict) { + if (strict) { + mStrictFlags |= STRICT_PARENTHESES; + } else { + mStrictFlags &= ~STRICT_PARENTHESES; + } } /** @@ -267,7 +285,75 @@ public class SQLiteQueryBuilder { * {@link #setStrict(boolean)}. */ public boolean isStrict() { - return mStrict; + return (mStrictFlags & STRICT_PARENTHESES) != 0; + } + + /** + * When enabled, verify that all projections and {@link ContentValues} only + * contain valid columns as defined by {@link #setProjectionMap(Map)}. + * <p> + * This enforcement applies to {@link #insert}, {@link #query}, and + * {@link #update} operations. Any enforcement failures will throw an + * {@link IllegalArgumentException}. + * + * {@hide} + */ + public void setStrictColumns(boolean strictColumns) { + if (strictColumns) { + mStrictFlags |= STRICT_COLUMNS; + } else { + mStrictFlags &= ~STRICT_COLUMNS; + } + } + + /** + * Get if the query is marked as strict, as last configured by + * {@link #setStrictColumns(boolean)}. + * + * {@hide} + */ + public boolean isStrictColumns() { + return (mStrictFlags & STRICT_COLUMNS) != 0; + } + + /** + * When enabled, verify that all untrusted SQL conforms to a restricted SQL + * grammar. Here are the restrictions applied: + * <ul> + * <li>In {@code WHERE} and {@code HAVING} clauses: subqueries, raising, and + * windowing terms are rejected. + * <li>In {@code GROUP BY} clauses: only valid columns are allowed. + * <li>In {@code ORDER BY} clauses: only valid columns, collation, and + * ordering terms are allowed. + * <li>In {@code LIMIT} clauses: only numerical values and offset terms are + * allowed. + * </ul> + * All column references must be valid as defined by + * {@link #setProjectionMap(Map)}. + * <p> + * This enforcement applies to {@link #query}, {@link #update} and + * {@link #delete} operations. This enforcement does not apply to trusted + * inputs, such as those provided by {@link #appendWhere}. Any enforcement + * failures will throw an {@link IllegalArgumentException}. + * + * {@hide} + */ + public void setStrictGrammar(boolean strictGrammar) { + if (strictGrammar) { + mStrictFlags |= STRICT_GRAMMAR; + } else { + mStrictFlags &= ~STRICT_GRAMMAR; + } + } + + /** + * Get if the query is marked as strict, as last configured by + * {@link #setStrictGrammar(boolean)}. + * + * {@hide} + */ + public boolean isStrictGrammar() { + return (mStrictFlags & STRICT_GRAMMAR) != 0; } /** @@ -303,9 +389,6 @@ public class SQLiteQueryBuilder { throw new IllegalArgumentException( "HAVING clauses are only permitted when using a groupBy clause"); } - if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) { - throw new IllegalArgumentException("invalid LIMIT clauses:" + limit); - } StringBuilder query = new StringBuilder(120); @@ -479,7 +562,13 @@ public class SQLiteQueryBuilder { projectionIn, selection, groupBy, having, sortOrder, limit); - if (mStrict && selection != null && selection.length() > 0) { + if (isStrictColumns()) { + enforceStrictColumns(projectionIn); + } + if (isStrictGrammar()) { + enforceStrictGrammar(selection, groupBy, having, sortOrder, limit); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -497,7 +586,7 @@ public class SQLiteQueryBuilder { // Execute wrapped query for extra protection final String wrappedSql = buildQuery(projectionIn, wrap(selection), groupBy, - having, sortOrder, limit); + wrap(having), sortOrder, limit); sql = wrappedSql; } else { // Execute unwrapped query @@ -519,6 +608,42 @@ public class SQLiteQueryBuilder { } /** + * Perform an insert by combining all current settings and the + * information passed into this method. + * + * @param db the database to insert on + * @return the row ID of the newly inserted row, or -1 if an error occurred + * + * {@hide} + */ + public long insert(@NonNull SQLiteDatabase db, @NonNull ContentValues values) { + Objects.requireNonNull(mTables, "No tables defined"); + Objects.requireNonNull(db, "No database defined"); + Objects.requireNonNull(values, "No values defined"); + + if (isStrictColumns()) { + enforceStrictColumns(values); + } + + final String sql = buildInsert(values); + + final ArrayMap<String, Object> rawValues = values.getValues(); + final int valuesLength = rawValues.size(); + final Object[] sqlArgs = new Object[valuesLength]; + for (int i = 0; i < sqlArgs.length; i++) { + sqlArgs[i] = rawValues.valueAt(i); + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + if (Build.IS_DEBUGGABLE) { + Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs)); + } else { + Log.d(TAG, sql); + } + } + return db.executeSql(sql, sqlArgs); + } + + /** * Perform an update by combining all current settings and the * information passed into this method. * @@ -541,7 +666,13 @@ public class SQLiteQueryBuilder { final String sql; final String unwrappedSql = buildUpdate(values, selection); - if (mStrict) { + if (isStrictColumns()) { + enforceStrictColumns(values); + } + if (isStrictGrammar()) { + enforceStrictGrammar(selection, null, null, null, null); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -610,7 +741,10 @@ public class SQLiteQueryBuilder { final String sql; final String unwrappedSql = buildDelete(selection); - if (mStrict) { + if (isStrictGrammar()) { + enforceStrictGrammar(selection, null, null, null, null); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -645,6 +779,81 @@ public class SQLiteQueryBuilder { return db.executeSql(sql, sqlArgs); } + private void enforceStrictColumns(@Nullable String[] projection) { + Objects.requireNonNull(mProjectionMap, "No projection map defined"); + + computeProjection(projection); + } + + private void enforceStrictColumns(@NonNull ContentValues values) { + Objects.requireNonNull(mProjectionMap, "No projection map defined"); + + final ArrayMap<String, Object> rawValues = values.getValues(); + for (int i = 0; i < rawValues.size(); i++) { + final String column = rawValues.keyAt(i); + if (!mProjectionMap.containsKey(column)) { + throw new IllegalArgumentException("Invalid column " + column); + } + } + } + + private void enforceStrictGrammar(@Nullable String selection, @Nullable String groupBy, + @Nullable String having, @Nullable String sortOrder, @Nullable String limit) { + SQLiteTokenizer.tokenize(selection, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarWhereHaving); + SQLiteTokenizer.tokenize(groupBy, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarGroupBy); + SQLiteTokenizer.tokenize(having, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarWhereHaving); + SQLiteTokenizer.tokenize(sortOrder, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarOrderBy); + SQLiteTokenizer.tokenize(limit, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarLimit); + } + + private void enforceStrictGrammarWhereHaving(@NonNull String token) { + if (isTableOrColumn(token)) return; + if (SQLiteTokenizer.isFunction(token)) return; + if (SQLiteTokenizer.isType(token)) return; + + // NOTE: we explicitly don't allow SELECT subqueries, since they could + // leak data that should have been filtered by the trusted where clause + switch (token.toUpperCase(Locale.US)) { + case "AND": case "AS": case "BETWEEN": case "BINARY": + case "CASE": case "CAST": case "COLLATE": case "DISTINCT": + case "ELSE": case "END": case "ESCAPE": case "EXISTS": + case "GLOB": case "IN": case "IS": case "ISNULL": + case "LIKE": case "MATCH": case "NOCASE": case "NOT": + case "NOTNULL": case "NULL": case "OR": case "REGEXP": + case "RTRIM": case "THEN": case "WHEN": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarGroupBy(@NonNull String token) { + if (isTableOrColumn(token)) return; + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarOrderBy(@NonNull String token) { + if (isTableOrColumn(token)) return; + switch (token.toUpperCase(Locale.US)) { + case "COLLATE": case "ASC": case "DESC": + case "BINARY": case "RTRIM": case "NOCASE": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarLimit(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "OFFSET": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + /** * Construct a {@code SELECT} statement suitable for use in a group of * {@code SELECT} statements that will be joined through {@code UNION} operators @@ -698,6 +907,35 @@ public class SQLiteQueryBuilder { } /** {@hide} */ + public String buildInsert(ContentValues values) { + if (values == null || values.isEmpty()) { + throw new IllegalArgumentException("Empty values"); + } + + StringBuilder sql = new StringBuilder(120); + sql.append("INSERT INTO "); + sql.append(SQLiteDatabase.findEditTable(mTables)); + sql.append(" ("); + + final ArrayMap<String, Object> rawValues = values.getValues(); + for (int i = 0; i < rawValues.size(); i++) { + if (i > 0) { + sql.append(','); + } + sql.append(rawValues.keyAt(i)); + } + sql.append(") VALUES ("); + for (int i = 0; i < rawValues.size(); i++) { + if (i > 0) { + sql.append(','); + } + sql.append('?'); + } + sql.append(")"); + return sql.toString(); + } + + /** {@hide} */ public String buildUpdate(ContentValues values, String selection) { if (values == null || values.isEmpty()) { throw new IllegalArgumentException("Empty values"); @@ -705,7 +943,7 @@ public class SQLiteQueryBuilder { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); - sql.append(mTables); + sql.append(SQLiteDatabase.findEditTable(mTables)); sql.append(" SET "); final ArrayMap<String, Object> rawValues = values.getValues(); @@ -726,7 +964,7 @@ public class SQLiteQueryBuilder { public String buildDelete(String selection) { StringBuilder sql = new StringBuilder(120); sql.append("DELETE FROM "); - sql.append(mTables); + sql.append(SQLiteDatabase.findEditTable(mTables)); final String where = computeWhere(selection); appendClause(sql, " WHERE ", where); @@ -868,65 +1106,13 @@ public class SQLiteQueryBuilder { /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public String[] computeProjection(String[] projectionIn) { - if (projectionIn != null && projectionIn.length > 0) { - if (mProjectionMap != null) { - String[] projection = new String[projectionIn.length]; - int length = projectionIn.length; - - for (int i = 0; i < length; i++) { - String operator = null; - String userColumn = projectionIn[i]; - String column = mProjectionMap.get(userColumn); - - // If aggregation is allowed, extract the underlying column - // that may be aggregated - if (mProjectionAggregationAllowed) { - final Matcher matcher = sAggregationPattern.matcher(userColumn); - if (matcher.matches()) { - operator = matcher.group(1); - userColumn = matcher.group(2); - column = mProjectionMap.get(userColumn); - } - } - - if (column != null) { - projection[i] = maybeWithOperator(operator, column); - continue; - } - - if (!mStrict && - ( userColumn.contains(" AS ") || userColumn.contains(" as "))) { - /* A column alias already exist */ - projection[i] = maybeWithOperator(operator, userColumn); - continue; - } - - // If greylist is configured, we might be willing to let - // this custom column bypass our strict checks. - if (mProjectionGreylist != null) { - boolean match = false; - for (Pattern p : mProjectionGreylist) { - if (p.matcher(userColumn).matches()) { - match = true; - break; - } - } - - if (match) { - Log.w(TAG, "Allowing abusive custom column: " + userColumn); - projection[i] = maybeWithOperator(operator, userColumn); - continue; - } - } - - throw new IllegalArgumentException("Invalid column " - + projectionIn[i]); - } - return projection; - } else { - return projectionIn; + public @Nullable String[] computeProjection(@Nullable String[] projectionIn) { + if (!ArrayUtils.isEmpty(projectionIn)) { + String[] projectionOut = new String[projectionIn.length]; + for (int i = 0; i < projectionIn.length; i++) { + projectionOut[i] = computeSingleProjectionOrThrow(projectionIn[i]); } + return projectionOut; } else if (mProjectionMap != null) { // Return all columns in projection map. Set<Entry<String, String>> entrySet = mProjectionMap.entrySet(); @@ -948,6 +1134,69 @@ public class SQLiteQueryBuilder { return null; } + private @NonNull String computeSingleProjectionOrThrow(@NonNull String userColumn) { + final String column = computeSingleProjection(userColumn); + if (column != null) { + return column; + } else { + throw new IllegalArgumentException("Invalid column " + userColumn); + } + } + + private @Nullable String computeSingleProjection(@NonNull String userColumn) { + // When no mapping provided, anything goes + if (mProjectionMap == null) { + return userColumn; + } + + String operator = null; + String column = mProjectionMap.get(userColumn); + + // When no direct match found, look for aggregation + if (column == null) { + final Matcher matcher = sAggregationPattern.matcher(userColumn); + if (matcher.matches()) { + operator = matcher.group(1); + userColumn = matcher.group(2); + column = mProjectionMap.get(userColumn); + } + } + + if (column != null) { + return maybeWithOperator(operator, column); + } + + if (mStrictFlags == 0 + && (userColumn.contains(" AS ") || userColumn.contains(" as "))) { + /* A column alias already exist */ + return maybeWithOperator(operator, userColumn); + } + + // If greylist is configured, we might be willing to let + // this custom column bypass our strict checks. + if (mProjectionGreylist != null) { + boolean match = false; + for (Pattern p : mProjectionGreylist) { + if (p.matcher(userColumn).matches()) { + match = true; + break; + } + } + + if (match) { + Log.w(TAG, "Allowing abusive custom column: " + userColumn); + return maybeWithOperator(operator, userColumn); + } + } + + return null; + } + + private boolean isTableOrColumn(String token) { + if (mTables.equals(token)) return true; + return computeSingleProjection(token) != null; + } + /** {@hide} */ public @Nullable String computeWhere(@Nullable String selection) { final boolean hasInternal = !TextUtils.isEmpty(mWhereClause); diff --git a/core/java/android/database/sqlite/SQLiteTokenizer.java b/core/java/android/database/sqlite/SQLiteTokenizer.java new file mode 100644 index 000000000000..7e7c3fb976c7 --- /dev/null +++ b/core/java/android/database/sqlite/SQLiteTokenizer.java @@ -0,0 +1,297 @@ +/* + * 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 android.database.sqlite; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; + +/** + * SQL Tokenizer specialized to extract tokens from SQL (snippets). + * <p> + * Based on sqlite3GetToken() in tokenzie.c in SQLite. + * <p> + * Source for v3.8.6 (which android uses): http://www.sqlite.org/src/artifact/ae45399d6252b4d7 + * (Latest source as of now: http://www.sqlite.org/src/artifact/78c8085bc7af1922) + * <p> + * Also draft spec: http://www.sqlite.org/draft/tokenreq.html + * + * @hide + */ +public class SQLiteTokenizer { + private static boolean isAlpha(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || (ch == '_'); + } + + private static boolean isNum(char ch) { + return ('0' <= ch && ch <= '9'); + } + + private static boolean isAlNum(char ch) { + return isAlpha(ch) || isNum(ch); + } + + private static boolean isAnyOf(char ch, String set) { + return set.indexOf(ch) >= 0; + } + + private static IllegalArgumentException genException(String message, String sql) { + throw new IllegalArgumentException(message + " in '" + sql + "'"); + } + + private static char peek(String s, int index) { + return index < s.length() ? s.charAt(index) : '\0'; + } + + public static final int OPTION_NONE = 0; + + /** + * Require that SQL contains only tokens; any comments or values will result + * in an exception. + */ + public static final int OPTION_TOKEN_ONLY = 1 << 0; + + /** + * Tokenize the given SQL, returning the list of each encountered token. + * + * @throws IllegalArgumentException if invalid SQL is encountered. + */ + public static List<String> tokenize(@Nullable String sql, int options) { + final ArrayList<String> res = new ArrayList<>(); + tokenize(sql, options, res::add); + return res; + } + + /** + * Tokenize the given SQL, sending each encountered token to the given + * {@link Consumer}. + * + * @throws IllegalArgumentException if invalid SQL is encountered. + */ + public static void tokenize(@Nullable String sql, int options, Consumer<String> checker) { + if (sql == null) { + return; + } + int pos = 0; + final int len = sql.length(); + while (pos < len) { + final char ch = peek(sql, pos); + + // Regular token. + if (isAlpha(ch)) { + final int start = pos; + pos++; + while (isAlNum(peek(sql, pos))) { + pos++; + } + final int end = pos; + + final String token = sql.substring(start, end); + checker.accept(token); + + continue; + } + + // Handle quoted tokens + if (isAnyOf(ch, "'\"`")) { + final int quoteStart = pos; + pos++; + + for (;;) { + pos = sql.indexOf(ch, pos); + if (pos < 0) { + throw genException("Unterminated quote", sql); + } + if (peek(sql, pos + 1) != ch) { + break; + } + // Quoted quote char -- e.g. "abc""def" is a single string. + pos += 2; + } + final int quoteEnd = pos; + pos++; + + if (ch != '\'') { + // Extract the token + final String tokenUnquoted = sql.substring(quoteStart + 1, quoteEnd); + + final String token; + + // Unquote if needed. i.e. "aa""bb" -> aa"bb + if (tokenUnquoted.indexOf(ch) >= 0) { + token = tokenUnquoted.replaceAll( + String.valueOf(ch) + ch, String.valueOf(ch)); + } else { + token = tokenUnquoted; + } + checker.accept(token); + } else { + if ((options &= OPTION_TOKEN_ONLY) != 0) { + throw genException("Non-token detected", sql); + } + } + continue; + } + // Handle tokens enclosed in [...] + if (ch == '[') { + final int quoteStart = pos; + pos++; + + pos = sql.indexOf(']', pos); + if (pos < 0) { + throw genException("Unterminated quote", sql); + } + final int quoteEnd = pos; + pos++; + + final String token = sql.substring(quoteStart + 1, quoteEnd); + + checker.accept(token); + continue; + } + if ((options &= OPTION_TOKEN_ONLY) != 0) { + throw genException("Non-token detected", sql); + } + + // Detect comments. + if (ch == '-' && peek(sql, pos + 1) == '-') { + pos += 2; + pos = sql.indexOf('\n', pos); + if (pos < 0) { + // We disallow strings ending in an inline comment. + throw genException("Unterminated comment", sql); + } + pos++; + + continue; + } + if (ch == '/' && peek(sql, pos + 1) == '*') { + pos += 2; + pos = sql.indexOf("*/", pos); + if (pos < 0) { + throw genException("Unterminated comment", sql); + } + pos += 2; + + continue; + } + + // Semicolon is never allowed. + if (ch == ';') { + throw genException("Semicolon is not allowed", sql); + } + + // For this purpose, we can simply ignore other characters. + // (Note it doesn't handle the X'' literal properly and reports this X as a token, + // but that should be fine...) + pos++; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/lang_keywords.html">SQLite reserved + * keyword</a>. + */ + public static boolean isKeyword(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "ABORT": case "ACTION": case "ADD": case "AFTER": + case "ALL": case "ALTER": case "ANALYZE": case "AND": + case "AS": case "ASC": case "ATTACH": case "AUTOINCREMENT": + case "BEFORE": case "BEGIN": case "BETWEEN": case "BINARY": + case "BY": case "CASCADE": case "CASE": case "CAST": + case "CHECK": case "COLLATE": case "COLUMN": case "COMMIT": + case "CONFLICT": case "CONSTRAINT": case "CREATE": case "CROSS": + case "CURRENT": case "CURRENT_DATE": case "CURRENT_TIME": case "CURRENT_TIMESTAMP": + case "DATABASE": case "DEFAULT": case "DEFERRABLE": case "DEFERRED": + case "DELETE": case "DESC": case "DETACH": case "DISTINCT": + case "DO": case "DROP": case "EACH": case "ELSE": + case "END": case "ESCAPE": case "EXCEPT": case "EXCLUDE": + case "EXCLUSIVE": case "EXISTS": case "EXPLAIN": case "FAIL": + case "FILTER": case "FOLLOWING": case "FOR": case "FOREIGN": + case "FROM": case "FULL": case "GLOB": case "GROUP": + case "GROUPS": case "HAVING": case "IF": case "IGNORE": + case "IMMEDIATE": case "IN": case "INDEX": case "INDEXED": + case "INITIALLY": case "INNER": case "INSERT": case "INSTEAD": + case "INTERSECT": case "INTO": case "IS": case "ISNULL": + case "JOIN": case "KEY": case "LEFT": case "LIKE": + case "LIMIT": case "MATCH": case "NATURAL": case "NO": + case "NOCASE": case "NOT": case "NOTHING": case "NOTNULL": + case "NULL": case "OF": case "OFFSET": case "ON": + case "OR": case "ORDER": case "OTHERS": case "OUTER": + case "OVER": case "PARTITION": case "PLAN": case "PRAGMA": + case "PRECEDING": case "PRIMARY": case "QUERY": case "RAISE": + case "RANGE": case "RECURSIVE": case "REFERENCES": case "REGEXP": + case "REINDEX": case "RELEASE": case "RENAME": case "REPLACE": + case "RESTRICT": case "RIGHT": case "ROLLBACK": case "ROW": + case "ROWS": case "RTRIM": case "SAVEPOINT": case "SELECT": + case "SET": case "TABLE": case "TEMP": case "TEMPORARY": + case "THEN": case "TIES": case "TO": case "TRANSACTION": + case "TRIGGER": case "UNBOUNDED": case "UNION": case "UNIQUE": + case "UPDATE": case "USING": case "VACUUM": case "VALUES": + case "VIEW": case "VIRTUAL": case "WHEN": case "WHERE": + case "WINDOW": case "WITH": case "WITHOUT": + return true; + default: + return false; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/lang_corefunc.html">SQLite reserved + * function</a>. + */ + public static boolean isFunction(@NonNull String token) { + switch (token.toLowerCase(Locale.US)) { + case "abs": case "avg": case "char": case "coalesce": + case "count": case "glob": case "group_concat": case "hex": + case "ifnull": case "instr": case "length": case "like": + case "likelihood": case "likely": case "lower": case "ltrim": + case "max": case "min": case "nullif": case "random": + case "randomblob": case "replace": case "round": case "rtrim": + case "substr": case "sum": case "total": case "trim": + case "typeof": case "unicode": case "unlikely": case "upper": + case "zeroblob": + return true; + default: + return false; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/datatype3.html">SQLite reserved type</a>. + */ + public static boolean isType(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "INT": case "INTEGER": case "TINYINT": case "SMALLINT": + case "MEDIUMINT": case "BIGINT": case "INT2": case "INT8": + case "CHARACTER": case "VARCHAR": case "NCHAR": case "NVARCHAR": + case "TEXT": case "CLOB": case "BLOB": case "REAL": + case "DOUBLE": case "FLOAT": case "NUMERIC": case "DECIMAL": + case "BOOLEAN": case "DATE": case "DATETIME": + return true; + default: + return false; + } + } +} diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 8a0a9c7c9d9d..0b25dbd78611 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -820,4 +820,64 @@ public final class DisplayManager { */ void onDisplayChanged(int displayId); } + + /** + * Interface for accessing keys belonging to {@link + * android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER}. + * @hide + */ + public interface DeviceConfig { + + /** + * Key for refresh rate in the zone defined by thresholds. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_defaultZoneBehavior + */ + String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone"; + + /** + * Key for accessing the display brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]). + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate + * @hide + */ + String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS = + "peak_refresh_rate_brightness_thresholds"; + + /** + * Key for accessing the ambient brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in lux. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate + * @hide + */ + String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS = + "peak_refresh_rate_ambient_thresholds"; + + /** + * Key for default peak refresh rate + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_defaultPeakRefreshRate + * @hide + */ + String KEY_PEAK_REFRESH_RATE_DEFAULT = "peak_refresh_rate_default"; + + /** + * Key for controlling which packages are explicitly blocked from running at refresh rates + * higher than 60hz. An app may be added to this list if they exhibit performance issues at + * higher refresh rates. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_highRefreshRateBlacklist + * @hide + */ + String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist"; + } } diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index 7782753e4abc..0de09efad8ea 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -21,9 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; -import android.content.LocaleProto; import android.icu.util.ULocale; -import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -143,26 +141,6 @@ public final class LocaleList implements Parcelable { } /** - * Helper to write LocaleList to a protocol buffer output stream. Assumes the parent - * protobuf has declared the locale as repeated. - * - * @param protoOutputStream Stream to write the locale to. - * @param fieldId Field Id of the Locale as defined in the parent message. - * @hide - */ - public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { - for (int i = 0; i < mList.length; i++) { - final Locale locale = mList[i]; - final long token = protoOutputStream.start(fieldId); - protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage()); - protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry()); - protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant()); - protoOutputStream.write(LocaleProto.SCRIPT, locale.getScript()); - protoOutputStream.end(token); - } - } - - /** * Retrieves a String representation of the language tags in this list. */ @NonNull diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 702b41beb071..26da0a0aee07 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -53,7 +53,7 @@ public abstract class VibrationEffect implements Parcelable { public static final int MAX_AMPLITUDE = 255; /** - * A click effect. + * A click effect. Use this effect as a baseline, as it's the most common type of click effect. * * @see #get(int) */ @@ -67,7 +67,7 @@ public abstract class VibrationEffect implements Parcelable { public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK; /** - * A tick effect. + * A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}. * @see #get(int) */ public static final int EFFECT_TICK = Effect.TICK; @@ -89,7 +89,7 @@ public abstract class VibrationEffect implements Parcelable { public static final int EFFECT_POP = Effect.POP; /** - * A heavy click effect. + * A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}. * @see #get(int) */ public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index ea50ae810535..c837b9346173 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -136,6 +136,14 @@ public final class DeviceConfig { public static final String NAMESPACE_DEX_BOOT = "dex_boot"; /** + * Namespace for display manager related features. The names to access the properties in this + * namespace should be defined in {@link android.hardware.display.DisplayManager}. + * + * @hide + */ + public static final String NAMESPACE_DISPLAY_MANAGER = "display_manager"; + + /** * Namespace for all Game Driver features. * * @hide @@ -350,15 +358,6 @@ public final class DeviceConfig { */ String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS = "system_gesture_exclusion_log_debounce_millis"; - - /** - * Key for controlling which packages are explicitly blocked from running at refresh rates - * higher than 60hz. - * - * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER - * @hide - */ - String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist"; } private static final Object sLock = new Object(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f92be195229e..209a3648cb82 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3477,6 +3477,14 @@ public final class Settings { }; /** + * The user selected min refresh rate in frames per second. + * + * If this isn't set, 0 will be used. + * @hide + */ + public static final String MIN_REFRESH_RATE = "min_refresh_rate"; + + /** * The user selected peak refresh rate in frames per second. * * If this isn't set, the system falls back to a device specific default. @@ -5949,6 +5957,18 @@ public final class Settings { public static final String DEVICE_PROVISIONED = Global.DEVICE_PROVISIONED; /** + * Indicates whether a DPC has been downloaded during provisioning. + * + * <p>Type: int (0 for false, 1 for true) + * + * <p>If this is true, then any attempts to begin setup again should result in factory reset + * + * @hide + */ + public static final String MANAGED_PROVISIONING_DPC_DOWNLOADED = + "managed_provisioning_dpc_downloaded"; + + /** * Indicates whether the current user has completed setup via the setup wizard. * <p> * Type: int (0 for false, 1 for true) diff --git a/core/java/android/view/accessibility/TEST_MAPPING b/core/java/android/view/accessibility/TEST_MAPPING index 535a31a908a0..d2bd6ea2b702 100644 --- a/core/java/android/view/accessibility/TEST_MAPPING +++ b/core/java/android/view/accessibility/TEST_MAPPING @@ -30,9 +30,6 @@ }, { "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-filter": "android.view.textclassifier.TextClassifierTest#testSuggetsConversationActions_deduplicate" } ] } diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING index 31e240bf55bb..01a6edecf21e 100644 --- a/core/java/android/view/textclassifier/TEST_MAPPING +++ b/core/java/android/view/textclassifier/TEST_MAPPING @@ -8,9 +8,6 @@ }, { "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-filter": "android.view.textclassifier.TextClassifierTest#testSuggetsConversationActions_deduplicate" } ] } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 95fca00f2346..721ac2d9a6dc 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1843,8 +1843,8 @@ public class WebView extends AbsoluteLayout /** * Injects the supplied Java object into this WebView. The object is - * injected into the JavaScript context of the main frame, using the - * supplied name. This allows the Java object's methods to be + * injected into all frames of the web page, including all the iframes, + * using the supplied name. This allows the Java object's methods to be * accessed from JavaScript. For applications targeted to API * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} * and above, only public methods that are annotated with @@ -1883,6 +1883,11 @@ public class WebView extends AbsoluteLayout * thread of this WebView. Care is therefore required to maintain thread * safety. * </li> + * <li> Because the object is exposed to all the frames, any frame could + * obtain the object name and call methods on it. There is no way to tell the + * calling frame's origin from the app side, so the app must not assume that + * the caller is trustworthy unless the app can guarantee that no third party + * content is ever loaded into the WebView even inside an iframe.</li> * <li> The Java object's fields are not accessible.</li> * <li> For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP} * and above, methods of injected Java objects are enumerable from diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index be5d2211c670..d62b979a2ed3 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -62,8 +62,9 @@ import java.io.IOException; /** * Displays image resources, for example {@link android.graphics.Bitmap} * or {@link android.graphics.drawable.Drawable} resources. - * ImageView is also commonly used to {@link #setImageTintMode(PorterDuff.Mode) - * apply tints to an image} and handle {@link #setScaleType(ScaleType) image scaling}. + * ImageView is also commonly used to + * <a href="#setImageTintMode(android.graphics.PorterDuff.Mode)">apply tints to an image</a> and + * handle <a href="#setScaleType(android.widget.ImageView.ScaleType)">image scaling</a>. * * <p> * The following XML snippet is a common example of using an ImageView to display an image resource: @@ -76,7 +77,8 @@ import java.io.IOException; * <ImageView * android:layout_width="wrap_content" * android:layout_height="wrap_content" - * android:src="@mipmap/ic_launcher" + * android:src="@drawable/my_image" + * android:contentDescription="@string/my_image_description" * /> * </LinearLayout> * </pre> diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index c24ffb0c329b..ad53eb9feeae 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -479,10 +479,6 @@ class ZygoteConnection { closeSocket(); - if (parsedArgs.mNiceName != null) { - Process.setArgV0(parsedArgs.mNiceName); - } - // End of the postFork event. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); if (parsedArgs.mInvokeWith != null) { diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto index 57ced09240f2..7fa0ff64f8bf 100644 --- a/core/proto/android/content/configuration.proto +++ b/core/proto/android/content/configuration.proto @@ -32,7 +32,7 @@ message ConfigurationProto { optional float font_scale = 1; optional uint32 mcc = 2; optional uint32 mnc = 3 [ (.android.privacy).dest = DEST_EXPLICIT ]; - repeated LocaleProto locales = 4; + repeated LocaleProto locales = 4 [deprecated = true]; optional uint32 screen_layout = 5; optional uint32 color_mode = 6; optional uint32 touchscreen = 7; @@ -48,6 +48,7 @@ message ConfigurationProto { optional uint32 smallest_screen_width_dp = 17; optional uint32 density_dpi = 18; optional .android.app.WindowConfigurationProto window_configuration = 19; + optional string locale_list = 20; } /** diff --git a/core/proto/android/content/locale.proto b/core/proto/android/content/locale.proto index bae6ec141981..a8f2a1334038 100644 --- a/core/proto/android/content/locale.proto +++ b/core/proto/android/content/locale.proto @@ -22,6 +22,7 @@ import "frameworks/base/core/proto/android/privacy.proto"; package android.content; message LocaleProto { + option deprecated = true; option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional string language = 1; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e4a5074ddbbf..efc943e3d44a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4196,6 +4196,10 @@ --> </integer-array> + <!-- Default refresh rate in the zone defined by brightness and ambient thresholds. + If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> + <integer name="config_defaultRefreshRateInZone">0</integer> + <!-- The type of the light sensor to be used by the display framework for things like auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. --> <string name="config_displayLightSensorType" translatable="false" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 261e76e35a4a..e5805fd91fbb 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3798,6 +3798,7 @@ <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> + <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java index 2fc3e36e7948..ad97ff101cb0 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java @@ -16,16 +16,29 @@ package android.content.res; +import android.content.Context; +import android.os.LocaleList; import android.platform.test.annotations.Presubmit; +import android.util.AtomicFile; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.usage.IntervalStatsProto; + import junit.framework.TestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Locale; + /** * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest */ @@ -54,4 +67,70 @@ public class ConfigurationTest extends TestCase { config2.updateFrom(config); assertEquals(config2.screenLayout, Configuration.SCREENLAYOUT_COMPAT_NEEDED); } + + @Test + public void testReadWriteProto() throws Exception { + final Context context = InstrumentationRegistry.getTargetContext(); + final File testDir = new File(context.getFilesDir(), "ConfigurationTest"); + testDir.mkdirs(); + final File proto = new File(testDir, "configs"); + if (proto.exists()) { + proto.delete(); + } + + final Locale arabic = new Locale.Builder().setLocale(new Locale("ar", "AE")).build(); + final Locale urdu = new Locale.Builder().setLocale(new Locale("ur", "IN")).build(); + final Locale urduExtension = new Locale.Builder().setLocale(new Locale("ur", "IN")) + .setExtension('u', "nu-latn").build(); + Configuration write = new Configuration(); + write.setLocales(new LocaleList(arabic, urdu, urduExtension)); + writeToProto(proto, write); + assertTrue("Failed to write configs to proto.", proto.exists()); + + final Configuration read = new Configuration(); + try { + readFromProto(proto, read); + } finally { + proto.delete(); + } + + assertEquals("Missing locales in proto file written to disk.", + read.getLocales().size(), write.getLocales().size()); + assertTrue("Arabic locale not found in Configuration locale list.", + read.getLocales().indexOf(arabic) != -1); + assertTrue("Urdu locale not found in Configuration locale list.", + read.getLocales().indexOf(urdu) != -1); + assertTrue("Urdu locale with extensions not found in Configuration locale list.", + read.getLocales().indexOf(urduExtension) != -1); + } + + private void writeToProto(File f, Configuration config) throws Exception { + final AtomicFile af = new AtomicFile(f); + FileOutputStream fos = af.startWrite(); + try { + final ProtoOutputStream protoOut = new ProtoOutputStream(fos); + final long token = protoOut.start(IntervalStatsProto.CONFIGURATIONS); + config.writeToProto(protoOut, IntervalStatsProto.Configuration.CONFIG, false, false); + protoOut.end(token); + protoOut.flush(); + af.finishWrite(fos); + fos = null; + } finally { + af.failWrite(fos); + } + } + + private void readFromProto(File f, Configuration config) throws Exception { + final AtomicFile afRead = new AtomicFile(f); + try (FileInputStream in = afRead.openRead()) { + final ProtoInputStream protoIn = new ProtoInputStream(in); + if (protoIn.isNextField(IntervalStatsProto.CONFIGURATIONS)) { + final long token = protoIn.start(IntervalStatsProto.CONFIGURATIONS); + if (protoIn.isNextField(IntervalStatsProto.Configuration.CONFIG)) { + config.readFromProto(protoIn, IntervalStatsProto.Configuration.CONFIG); + protoIn.end(token); + } + } + } + } } diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java new file mode 100644 index 000000000000..a9d148289262 --- /dev/null +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java @@ -0,0 +1,173 @@ +/* + * 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 android.database.sqlite; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class SQLiteTokenizerTest { + private List<String> getTokens(String sql) { + return SQLiteTokenizer.tokenize(sql, SQLiteTokenizer.OPTION_NONE); + } + + private void checkTokens(String sql, String spaceSeparatedExpectedTokens) { + final List<String> expected = spaceSeparatedExpectedTokens == null + ? new ArrayList<>() + : Arrays.asList(spaceSeparatedExpectedTokens.split(" +")); + + assertEquals(expected, getTokens(sql)); + } + + private void assertInvalidSql(String sql, String message) { + try { + getTokens(sql); + fail("Didn't throw InvalidSqlException"); + } catch (IllegalArgumentException e) { + assertTrue("Expected " + e.getMessage() + " to contain " + message, + e.getMessage().contains(message)); + } + } + + @Test + public void testWhitespaces() { + checkTokens(" select \t\r\n a\n\n ", "select a"); + checkTokens("a b", "a b"); + } + + @Test + public void testComment() { + checkTokens("--\n", null); + checkTokens("a--\n", "a"); + checkTokens("a--abcdef\n", "a"); + checkTokens("a--abcdef\nx", "a x"); + checkTokens("a--\nx", "a x"); + assertInvalidSql("a--abcdef", "Unterminated comment"); + assertInvalidSql("a--abcdef\ndef--", "Unterminated comment"); + + checkTokens("/**/", null); + assertInvalidSql("/*", "Unterminated comment"); + assertInvalidSql("/*/", "Unterminated comment"); + assertInvalidSql("/*\n* /*a", "Unterminated comment"); + checkTokens("a/**/", "a"); + checkTokens("/**/b", "b"); + checkTokens("a/**/b", "a b"); + checkTokens("a/* -- \n* /* **/b", "a b"); + } + + @Test + public void testStrings() { + assertInvalidSql("'", "Unterminated quote"); + assertInvalidSql("a'", "Unterminated quote"); + assertInvalidSql("a'''", "Unterminated quote"); + assertInvalidSql("a''' ", "Unterminated quote"); + checkTokens("''", null); + checkTokens("''''", null); + checkTokens("a''''b", "a b"); + checkTokens("a' '' 'b", "a b"); + checkTokens("'abc'", null); + checkTokens("'abc\ndef'", null); + checkTokens("a'abc\ndef'", "a"); + checkTokens("'abc\ndef'b", "b"); + checkTokens("a'abc\ndef'b", "a b"); + checkTokens("a'''abc\nd''ef'''b", "a b"); + } + + @Test + public void testDoubleQuotes() { + assertInvalidSql("\"", "Unterminated quote"); + assertInvalidSql("a\"", "Unterminated quote"); + assertInvalidSql("a\"\"\"", "Unterminated quote"); + assertInvalidSql("a\"\"\" ", "Unterminated quote"); + checkTokens("\"\"", ""); + checkTokens("\"\"\"\"", "\""); + checkTokens("a\"\"\"\"b", "a \" b"); + checkTokens("a\"\t\"\"\t\"b", "a \t\"\t b"); + checkTokens("\"abc\"", "abc"); + checkTokens("\"abc\ndef\"", "abc\ndef"); + checkTokens("a\"abc\ndef\"", "a abc\ndef"); + checkTokens("\"abc\ndef\"b", "abc\ndef b"); + checkTokens("a\"abc\ndef\"b", "a abc\ndef b"); + checkTokens("a\"\"\"abc\nd\"\"ef\"\"\"b", "a \"abc\nd\"ef\" b"); + } + + @Test + public void testBackQuotes() { + assertInvalidSql("`", "Unterminated quote"); + assertInvalidSql("a`", "Unterminated quote"); + assertInvalidSql("a```", "Unterminated quote"); + assertInvalidSql("a``` ", "Unterminated quote"); + checkTokens("``", ""); + checkTokens("````", "`"); + checkTokens("a````b", "a ` b"); + checkTokens("a`\t``\t`b", "a \t`\t b"); + checkTokens("`abc`", "abc"); + checkTokens("`abc\ndef`", "abc\ndef"); + checkTokens("a`abc\ndef`", "a abc\ndef"); + checkTokens("`abc\ndef`b", "abc\ndef b"); + checkTokens("a`abc\ndef`b", "a abc\ndef b"); + checkTokens("a```abc\nd``ef```b", "a `abc\nd`ef` b"); + } + + @Test + public void testBrackets() { + assertInvalidSql("[", "Unterminated quote"); + assertInvalidSql("a[", "Unterminated quote"); + assertInvalidSql("a[ ", "Unterminated quote"); + assertInvalidSql("a[[ ", "Unterminated quote"); + checkTokens("[]", ""); + checkTokens("[[]", "["); + checkTokens("a[[]b", "a [ b"); + checkTokens("a[\t[\t]b", "a \t[\t b"); + checkTokens("[abc]", "abc"); + checkTokens("[abc\ndef]", "abc\ndef"); + checkTokens("a[abc\ndef]", "a abc\ndef"); + checkTokens("[abc\ndef]b", "abc\ndef b"); + checkTokens("a[abc\ndef]b", "a abc\ndef b"); + checkTokens("a[[abc\nd[ef[]b", "a [abc\nd[ef[ b"); + } + + @Test + public void testSemicolons() { + assertInvalidSql(";", "Semicolon is not allowed"); + assertInvalidSql(" ;", "Semicolon is not allowed"); + assertInvalidSql("; ", "Semicolon is not allowed"); + assertInvalidSql("-;-", "Semicolon is not allowed"); + checkTokens("--;\n", null); + checkTokens("/*;*/", null); + checkTokens("';'", null); + checkTokens("[;]", ";"); + checkTokens("`;`", ";"); + } + + @Test + public void testTokens() { + checkTokens("a,abc,a00b,_1,_123,abcdef", "a abc a00b _1 _123 abcdef"); + checkTokens("a--\nabc/**/a00b''_1'''ABC'''`_123`abc[d]\"e\"f", + "a abc a00b _1 _123 abc d e f"); + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index aee178a9a01e..79c93ce773a4 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -93,6 +93,7 @@ public class SettingsBackupTest { Settings.System.VOLUME_VOICE, // deprecated since API 2? Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only + Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities Settings.System.PEAK_REFRESH_RATE // depends on hardware capabilities ); @@ -715,6 +716,7 @@ public class SettingsBackupTest { Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, Settings.Secure.FLASHLIGHT_AVAILABLE, Settings.Secure.FLASHLIGHT_ENABLED, + Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED, Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS, diff --git a/data/keyboards/Vendor_045e_Product_02dd.kl b/data/keyboards/Vendor_045e_Product_02dd.kl new file mode 100644 index 000000000000..3975cec24fcb --- /dev/null +++ b/data/keyboards/Vendor_045e_Product_02dd.kl @@ -0,0 +1,57 @@ +# 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. + +# +# XBox One Controller - Model 1697 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9a95288a69ae..8f164f1592d3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -27,6 +27,7 @@ import android.content.SharedPreferences; import android.os.ParcelUuid; import android.os.SystemClock; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -804,10 +805,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE || mDevice.getBluetoothClass().getDeviceClass() == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); - } else { - mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); + EventLog.writeEvent(0x534e4554, "138529441", -1, ""); } + mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 7f906f6c5b06..6d874ab2be9b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -117,7 +117,7 @@ public class HidProfile implements LocalBluetoothProfile { public boolean isPreferred(BluetoothDevice device) { if (mService == null) return false; - return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; + return mService.getPriority(device) != BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { diff --git a/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml index fe1bb265880c..7c7c8c1e4729 100644 --- a/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml +++ b/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml @@ -16,16 +16,10 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:viewportWidth="22" android:viewportHeight="17" - android:width="22dp" - android:height="17dp"> + android:width="19.41dp" + android:height="15dp"> <path android:fillColor="#FFFFFFFF" - android:pathData="M1.22,8.49l0.43-4.96h4.33v1.17H2.67L2.44,7.41c0.41-0.29,0.85-0.43,1.33-0.43c0.77,0,1.38,0.3,1.83,0.9 s0.66,1.41,0.66,2.43c0,1.03-0.24,1.84-0.72,2.43s-1.14,0.88-1.98,0.88c-0.75,0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07,0.57,0.23,1,0.49,1.29c0.26,0.29,0.59,0.43,1.01,0.43c0.47,0,0.84-0.2,1.1-0.61c0.26-0.41,0.4-0.96,0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.96,8.11,3.47,8.11c-0.4,0-0.72,0.1-0.96,0.31L2.19,8.75L1.22,8.49z" /> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M14.14,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13c0.56-0.7,1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79c0.54,0.53,0.83,1.28,0.89,2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45c-0.29-0.35-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7C9.14,5.63,8.96,6.37,8.95,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" /> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M20.96,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" /> - + android:pathData="M 6.72 3.52 L 6.54 4.69 L 3.34 4.69 L 2.63 7.41 C 3.011 7.143 3.465 7 3.93 7 C 4.5 6.986 5.041 7.251 5.38 7.71 C 5.756 8.249 5.952 8.893 5.94 9.55 C 5.98 10.276 5.864 11.002 5.6 11.68 C 5.385 12.267 5.007 12.78 4.51 13.16 C 4.043 13.499 3.476 13.671 2.9 13.65 C 2.271 13.653 1.675 13.369 1.28 12.88 C 0.854 12.302 0.636 11.597 0.66 10.88 L 1.76 10.88 C 1.81 12 2.21 12.57 3 12.58 C 3.592 12.589 4.132 12.243 4.37 11.7 C 4.708 11.044 4.85 10.305 4.78 9.57 C 4.767 9.209 4.645 8.86 4.43 8.57 C 4.239 8.309 3.934 8.156 3.61 8.16 C 3.404 8.138 3.196 8.162 3 8.23 C 2.748 8.358 2.518 8.527 2.32 8.73 L 1.31 8.46 L 2.51 3.52 L 6.72 3.52 Z M 11.7 3.39 C 12.459 3.353 13.195 3.662 13.7 4.23 C 14.185 4.864 14.42 5.654 14.36 6.45 L 13.1 6.45 C 13.131 5.938 12.998 5.43 12.72 5 C 12.455 4.679 12.056 4.498 11.64 4.51 C 11.025 4.456 10.42 4.688 10 5.14 C 9.491 5.811 9.179 6.611 9.1 7.45 L 8.75 9.54 L 8.75 10.57 C 8.82 11.86 9.36 12.52 10.36 12.57 C 10.701 12.593 11.043 12.538 11.36 12.41 C 11.661 12.281 11.943 12.113 12.2 11.91 L 12.62 9.62 L 10.77 9.62 L 11 8.52 L 14 8.52 L 13.36 12.23 C 13.176 12.483 12.953 12.706 12.7 12.89 C 11.995 13.398 11.138 13.652 10.27 13.61 C 9.507 13.634 8.773 13.315 8.27 12.74 C 7.748 12.085 7.475 11.267 7.5 10.43 C 7.47 10.097 7.47 9.763 7.5 9.43 L 7.8 7.43 C 7.927 6.332 8.36 5.293 9.05 4.43 C 9.725 3.692 10.703 3.308 11.7 3.39 Z M 20.81 7.21 L 20.62 8.29 L 18.32 8.29 L 18.06 9.8 L 20.06 9.8 L 19.83 10.84 L 17.88 10.84 L 17.59 12.54 L 19.9 12.54 L 19.71 13.61 L 16.14 13.61 L 17.25 7.21 L 20.81 7.21 Z" /> + <path android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z"/> </vector> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 340cb3ad7358..6e8e8236a99a 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -196,9 +196,6 @@ <!-- Doze: duration to avoid false pickup gestures triggered by notification vibrations --> <integer name="doze_pickup_vibration_threshold">2000</integer> - <!-- Doze: can we assume the pickup sensor includes a proximity check? --> - <bool name="doze_pickup_performs_proximity_check">false</bool> - <!-- Type of a sensor that provides a low-power estimate of the desired display brightness, suitable to listen to while the device is asleep (e.g. during always-on display) --> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 264a1020ddc9..0d85a3f77270 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -134,11 +134,13 @@ public class SystemUIFactory { public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, - KeyguardBouncer.BouncerExpansionCallback expansionCallback) { + KeyguardBouncer.BouncerExpansionCallback expansionCallback, + KeyguardBypassController bypassController) { return new KeyguardBouncer(context, callback, lockPatternUtils, container, dismissCallbackRegistry, FalsingManagerFactory.getInstance(context), expansionCallback, UnlockMethodCache.getInstance(context), - KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper())); + KeyguardUpdateMonitor.getInstance(context), bypassController, + new Handler(Looper.getMainLooper())); } public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 092eb46cad5e..026a62528c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -107,8 +107,7 @@ public class DozeSensors { config.dozePickupSensorAvailable(), DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */, false /* touchscreen */, - false /* ignoresSetting */, - mDozeParameters.getPickupPerformsProxCheck()), + false /* ignoresSetting */), new TriggerSensor( findSensorWithType(config.doubleTapSensorType()), Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, @@ -205,11 +204,8 @@ public class DozeSensors { public void updateListening() { boolean anyListening = false; for (TriggerSensor s : mSensors) { - // We don't want to be listening while we're PAUSED (prox sensor is covered) - // except when the sensor is already gated by prox. - boolean listen = mListening && (!mPaused || s.performsProxCheck()); - s.setListening(listen); - if (listen) { + s.setListening(mListening); + if (mListening) { anyListening = true; } } @@ -391,7 +387,6 @@ public class DozeSensors { private final boolean mReportsTouchCoordinates; private final boolean mSettingDefault; private final boolean mRequiresTouchscreen; - private final boolean mSensorPerformsProxCheck; protected boolean mRequested; protected boolean mRegistered; @@ -408,14 +403,12 @@ public class DozeSensors { boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) { this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates, - requiresTouchscreen, false /* ignoresSetting */, - false /* sensorPerformsProxCheck */); + requiresTouchscreen, false /* ignoresSetting */); } private TriggerSensor(Sensor sensor, String setting, boolean settingDef, boolean configured, int pulseReason, boolean reportsTouchCoordinates, - boolean requiresTouchscreen, boolean ignoresSetting, - boolean sensorPerformsProxCheck) { + boolean requiresTouchscreen, boolean ignoresSetting) { mSensor = sensor; mSetting = setting; mSettingDefault = settingDef; @@ -424,7 +417,6 @@ public class DozeSensors { mReportsTouchCoordinates = reportsTouchCoordinates; mRequiresTouchscreen = requiresTouchscreen; mIgnoresSetting = ignoresSetting; - mSensorPerformsProxCheck = sensorPerformsProxCheck; } public void setListening(boolean listen) { @@ -498,23 +490,13 @@ public class DozeSensors { screenX = event.values[0]; screenY = event.values[1]; } - mCallback.onSensorPulse(mPulseReason, mSensorPerformsProxCheck, screenX, screenY, - event.values); + mCallback.onSensorPulse(mPulseReason, screenX, screenY, event.values); if (!mRegistered) { updateListening(); // reregister, this sensor only fires once } })); } - /** - * If the sensor itself performs proximity checks, to avoid pocket dialing. - * Gated sensors don't need to be stopped when the {@link DozeMachine} is - * {@link DozeMachine.State#DOZE_AOD_PAUSED}. - */ - public boolean performsProxCheck() { - return mSensorPerformsProxCheck; - } - public void registerSettingsObserver(ContentObserver settingsObserver) { if (mConfigured && !TextUtils.isEmpty(mSetting)) { mResolver.registerContentObserver( @@ -610,8 +592,7 @@ public class DozeSensors { return; } if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event)); - mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1, - event.getValues()); + mCallback.onSensorPulse(mPulseReason, -1, -1, event.getValues()); })); } } @@ -621,13 +602,11 @@ public class DozeSensors { /** * Called when a sensor requests a pulse * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP} - * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity. * @param screenX the location on the screen where the sensor fired or -1 - * if the sensor doesn't support reporting screen locations. + * if the sensor doesn't support reporting screen locations. * @param screenY the location on the screen where the sensor fired or -1 * @param rawValues raw values array from the event. */ - void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck, - float screenX, float screenY, float[] rawValues); + void onSensorPulse(int pulseReason, float screenX, float screenY, float[] rawValues); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 310f04abc36c..bab64db4519c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -41,6 +41,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.Preconditions; import com.android.systemui.Dependency; +import com.android.systemui.R; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; @@ -156,8 +157,7 @@ public class DozeTriggers implements DozeMachine.Part { } @VisibleForTesting - void onSensor(int pulseReason, boolean sensorPerformedProxCheck, - float screenX, float screenY, float[] rawValues) { + void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) { boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP; boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP; boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP; @@ -169,10 +169,11 @@ public class DozeTriggers implements DozeMachine.Part { if (isWakeDisplay) { onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState()); } else if (isLongPress) { - requestPulse(pulseReason, sensorPerformedProxCheck, null /* onPulseSupressedListener */); + requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, + null /* onPulseSupressedListener */); } else if (isWakeLockScreen) { if (wakeEvent) { - requestPulse(pulseReason, sensorPerformedProxCheck, + requestPulse(pulseReason, true /* alreadyPerformedProxCheck */, null /* onPulseSupressedListener */); } } else { @@ -191,8 +192,7 @@ public class DozeTriggers implements DozeMachine.Part { } else { mDozeHost.extendPulse(pulseReason); } - }, sensorPerformedProxCheck - || (mDockManager != null && mDockManager.isDocked()), pulseReason); + }, true /* alreadyPerformedProxCheck */, pulseReason); } if (isPickup) { @@ -278,7 +278,7 @@ public class DozeTriggers implements DozeMachine.Part { .setType(MetricsEvent.TYPE_OPEN) .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP)); } - }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP); + }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP); } else { boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); @@ -417,6 +417,9 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.dump(pw); } + /** + * @see DozeSensors.ProxSensor + */ private abstract class ProximityCheck implements SensorEventListener, Runnable { private static final int TIMEOUT_DELAY_MS = 500; @@ -428,12 +431,18 @@ public class DozeTriggers implements DozeMachine.Part { private boolean mRegistered; private boolean mFinished; private float mMaxRange; + private boolean mUsingBrightnessSensor; protected abstract void onProximityResult(int result); public void check() { Preconditions.checkState(!mFinished && !mRegistered); - final Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + Sensor sensor = DozeSensors.findSensorWithType(mSensorManager, + mContext.getString(R.string.doze_brightness_sensor_type)); + mUsingBrightnessSensor = sensor != null; + if (sensor == null) { + sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + } if (sensor == null) { if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found"); finishWithResult(RESULT_UNKNOWN); @@ -449,6 +458,9 @@ public class DozeTriggers implements DozeMachine.Part { mRegistered = true; } + /** + * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent) + */ @Override public void onSensorChanged(SensorEvent event) { if (event.values.length == 0) { @@ -458,7 +470,14 @@ public class DozeTriggers implements DozeMachine.Part { if (DozeMachine.DEBUG) { Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange); } - final boolean isNear = event.values[0] < mMaxRange; + final boolean isNear; + if (mUsingBrightnessSensor) { + // The custom brightness sensor is gated by the proximity sensor and will + // return 0 whenever prox is covered. + isNear = event.values[0] == 0; + } else { + isNear = event.values[0] < mMaxRange; + } finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 10b48e71005d..bb6a38e1dcf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -207,10 +207,6 @@ public class DozeParameters implements TunerService.Tunable, return SystemProperties.get(propName, mContext.getString(resId)); } - public boolean getPickupPerformsProxCheck() { - return mContext.getResources().getBoolean(R.bool.doze_pickup_performs_proximity_check); - } - public int getPulseVisibleDurationExtended() { return 2 * getPulseVisibleDuration(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index c4d346ccaefb..dc9b373de688 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -77,6 +77,7 @@ public class KeyguardBouncer { } }; private final Runnable mRemoveViewRunnable = this::removeView; + private final KeyguardBypassController mKeyguardBypassController; protected KeyguardHostView mKeyguardView; private final Runnable mResetRunnable = ()-> { if (mKeyguardView != null) { @@ -97,7 +98,8 @@ public class KeyguardBouncer { LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache, - KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardBypassController keyguardBypassController, Handler handler) { mContext = context; mCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -109,6 +111,7 @@ public class KeyguardBouncer { mHandler = handler; mUnlockMethodCache = unlockMethodCache; mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + mKeyguardBypassController = keyguardBypassController; } public void show(boolean resetSecuritySelection) { @@ -171,7 +174,8 @@ public class KeyguardBouncer { // Split up the work over multiple frames. DejankUtils.removeCallbacks(mResetRunnable); if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer() - && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) { + && !mKeyguardUpdateMonitor.userNeedsStrongAuth() + && !mKeyguardBypassController.getBypassEnabled()) { mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); } else { DejankUtils.postAfterTraversal(mShowRunnable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 4d85a422d9b9..7e86651ffef6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -220,7 +220,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBiometricUnlockController = biometricUnlockController; mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, - mExpansionCallback); + mExpansionCallback, bypassController); mNotificationPanelView = notificationPanelView; notificationPanelView.addExpansionListener(this); mBypassController = bypassController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java index e1ef809a883c..6dc90b9028fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java @@ -19,7 +19,6 @@ import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; public interface KeyguardMonitor extends CallbackController<Callback> { boolean isSecure(); - boolean canSkipBouncer(); boolean isShowing(); boolean isOccluded(); boolean isKeyguardFadingAway(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java index 87ed14a9eeec..26d615c86c29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java @@ -17,13 +17,11 @@ package com.android.systemui.statusbar.policy; import android.annotation.NonNull; -import android.app.ActivityManager; import android.content.Context; import com.android.internal.util.Preconditions; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; @@ -39,14 +37,11 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback private final ArrayList<Callback> mCallbacks = new ArrayList<>(); private final Context mContext; - private final CurrentUserTracker mUserTracker; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private int mCurrentUser; private boolean mShowing; private boolean mSecure; private boolean mOccluded; - private boolean mCanSkipBouncer; private boolean mListening; private boolean mKeyguardFadingAway; @@ -62,13 +57,6 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback public KeyguardMonitorImpl(Context context) { mContext = context; mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - mUserTracker = new CurrentUserTracker(mContext) { - @Override - public void onUserSwitched(int newUserId) { - mCurrentUser = newUserId; - updateCanSkipBouncerState(); - } - }; } @Override @@ -77,10 +65,7 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback mCallbacks.add(callback); if (mCallbacks.size() != 0 && !mListening) { mListening = true; - mCurrentUser = ActivityManager.getCurrentUser(); - updateCanSkipBouncerState(); mKeyguardUpdateMonitor.registerCallback(this); - mUserTracker.startTracking(); } } @@ -90,7 +75,6 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) { mListening = false; mKeyguardUpdateMonitor.removeCallback(this); - mUserTracker.stopTracking(); } } @@ -109,11 +93,6 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback return mOccluded; } - @Override - public boolean canSkipBouncer() { - return mCanSkipBouncer; - } - public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) { if (mShowing == showing && mSecure == secure && mOccluded == occluded) return; mShowing = showing; @@ -124,7 +103,6 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback @Override public void onTrustChanged(int userId) { - updateCanSkipBouncerState(); notifyKeyguardChanged(); } @@ -132,10 +110,6 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback return mKeyguardUpdateMonitor.isDeviceInteractive(); } - private void updateCanSkipBouncerState() { - mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser); - } - private void notifyKeyguardChanged() { // Copy the list to allow removal during callback. new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 395add76dda4..35e3923f285b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -61,6 +61,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.tiles.UserDetailView; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.phone.UnlockMethodCache; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -595,17 +596,19 @@ public class UserSwitcherController implements Dumpable { final UserSwitcherController mController; private final KeyguardMonitor mKeyguardMonitor; + private final UnlockMethodCache mUnlockMethodCache; protected BaseUserAdapter(UserSwitcherController controller) { mController = controller; mKeyguardMonitor = controller.mKeyguardMonitor; + mUnlockMethodCache = UnlockMethodCache.getInstance(controller.mContext); controller.addAdapter(new WeakReference<>(this)); } public int getUserCount() { boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() && mKeyguardMonitor.isSecure() - && !mKeyguardMonitor.canSkipBouncer(); + && !mUnlockMethodCache.canSkipBouncer(); if (!secureKeyguardShowing) { return mController.getUsers().size(); } @@ -627,7 +630,7 @@ public class UserSwitcherController implements Dumpable { public int getCount() { boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() && mKeyguardMonitor.isSecure() - && !mKeyguardMonitor.canSkipBouncer(); + && !mUnlockMethodCache.canSkipBouncer(); if (!secureKeyguardShowing) { return mController.getUsers().size(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index 2ed0970ce44b..0c124fff53a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -37,7 +37,6 @@ public class DozeConfigurationUtil { when(params.getPulseOnSigMotion()).thenReturn(false); when(params.getPickupVibrationThreshold()).thenReturn(0); when(params.getProxCheckBeforePulse()).thenReturn(true); - when(params.getPickupPerformsProxCheck()).thenReturn(true); when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class)); when(params.doubleTapReportsTouchCoordinates()).thenReturn(false); when(params.getDisplayNeedsBlanking()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index 7df45a3d8949..cd6d1e069566 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -19,7 +19,6 @@ package com.android.systemui.doze; import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -79,8 +78,6 @@ public class DozeSensorsTest extends SysuiTestCase { private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; @Mock private TriggerSensor mTriggerSensor; - @Mock - private TriggerSensor mProxGatedTriggerSensor; private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; private TestableLooper mTestableLooper; private DozeSensors mDozeSensors; @@ -88,7 +85,6 @@ public class DozeSensorsTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mProxGatedTriggerSensor.performsProxCheck()).thenReturn(true); mTestableLooper = TestableLooper.get(this); when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L); when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); @@ -106,14 +102,14 @@ public class DozeSensorsTest extends SysuiTestCase { mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); mTestableLooper.processAllMessages(); verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN), - anyBoolean(), anyFloat(), anyFloat(), eq(null)); + anyFloat(), anyFloat(), eq(null)); mDozeSensors.requestTemporaryDisable(); reset(mCallback); mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); mTestableLooper.processAllMessages(); verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN), - anyBoolean(), anyFloat(), anyFloat(), eq(null)); + anyFloat(), anyFloat(), eq(null)); } @Test @@ -132,20 +128,17 @@ public class DozeSensorsTest extends SysuiTestCase { } @Test - public void testSetPaused_onlyPausesNonGatedSensors() { + public void testSetPaused_doesntPause_sensors() { mDozeSensors.setListening(true); verify(mTriggerSensor).setListening(eq(true)); - verify(mProxGatedTriggerSensor).setListening(eq(true)); - clearInvocations(mTriggerSensor, mProxGatedTriggerSensor); + clearInvocations(mTriggerSensor); mDozeSensors.setPaused(true); - verify(mTriggerSensor).setListening(eq(false)); - verify(mProxGatedTriggerSensor).setListening(eq(true)); - - clearInvocations(mTriggerSensor, mProxGatedTriggerSensor); - mDozeSensors.setPaused(false); verify(mTriggerSensor).setListening(eq(true)); - verify(mProxGatedTriggerSensor).setListening(eq(true)); + + clearInvocations(mTriggerSensor); + mDozeSensors.setListening(false); + verify(mTriggerSensor).setListening(eq(false)); } private class TestableDozeSensors extends DozeSensors { @@ -161,7 +154,7 @@ public class DozeSensorsTest extends SysuiTestCase { mWakeLockScreenListener = (PluginSensor) sensor; } } - mSensors = new TriggerSensor[] {mTriggerSensor, mProxGatedTriggerSensor}; + mSensors = new TriggerSensor[] {mTriggerSensor}; } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index d4642238d8fd..e190f9923da8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -20,7 +20,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -134,28 +133,4 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH); verify(mDockManagerFake).removeListener(any()); } - - @Test - public void testOnSensor_whenUndockedWithNearAndDoubleTapScreen_shouldNotWakeUp() { - mSensors.getMockProximitySensor().sendProximityResult(false /* far */); - - mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP, - false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */, - null /* rawValues */); - verify(mMachine, never()).wakeUp(); - } - - @Test - public void testOnSensor_whenDockedWithNearAndDoubleTapScreen_shouldWakeUp() { - doReturn(true).when(mDockManagerFake).isDocked(); - doReturn(true).when(mParameters).getDisplayNeedsBlanking(); - mSensors.getMockProximitySensor().sendProximityResult(false /* far */); - - mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP, - false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */, - null /* rawValues */); - - verify(mHost).setAodDimmingScrim(eq(1f)); - verify(mMachine).wakeUp(); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 907e695f2513..cd60e47eef50 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; @@ -84,6 +85,8 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Mock private UnlockMethodCache mUnlockMethodCache; @Mock + private KeyguardBypassController mKeyguardBypassController; + @Mock private Handler mHandler; private KeyguardBouncer mBouncer; @@ -98,7 +101,8 @@ public class KeyguardBouncerTest extends SysuiTestCase { when(mKeyguardHostView.getHeight()).thenReturn(500); mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback, mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager, - mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) { + mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, + mKeyguardBypassController, mHandler) { @Override protected void inflateView() { super.inflateView(); @@ -391,6 +395,15 @@ public class KeyguardBouncerTest extends SysuiTestCase { } @Test + public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() { + when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true); + when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); + mBouncer.show(true /* reset */); + + verify(mHandler, never()).postDelayed(any(), anyLong()); + } + + @Test public void testRegisterUpdateMonitorCallback() { verify(mKeyguardUpdateMonitor).registerCallback(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java index 95c7a4d09f92..2fb0e0e7caf8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java @@ -80,9 +80,4 @@ public class FakeKeyguardMonitor implements KeyguardMonitor { public long calculateGoingToFullShadeDelay() { return 0; } - - @Override - public boolean canSkipBouncer() { - return false; - } } diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING index 45c52452afb4..d90c3bd9b4c2 100644 --- a/services/accessibility/TEST_MAPPING +++ b/services/accessibility/TEST_MAPPING @@ -52,9 +52,6 @@ }, { "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-filter": "android.view.textclassifier.TextClassifierTest#testSuggetsConversationActions_deduplicate" } ] }, @@ -66,9 +63,6 @@ }, { "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-filter": "android.view.textclassifier.TextClassifierTest#testSuggetsConversationActions_deduplicate" } ] } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 07482796b027..9936d73fb800 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -1188,6 +1188,7 @@ public class VibratorService extends IVibratorService.Stub private static boolean isNotification(int usageHint) { switch (usageHint) { case AudioAttributes.USAGE_NOTIFICATION: + case AudioAttributes.USAGE_NOTIFICATION_EVENT: case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0a774f613670..cc5932e9b38c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5267,7 +5267,7 @@ public class ActivityManagerService extends IActivityManager.Stub storageManager.commitChanges(); } catch (Exception e) { PowerManager pm = (PowerManager) - mInjector.getContext().getSystemService(Context.POWER_SERVICE); + mContext.getSystemService(Context.POWER_SERVICE); pm.reboot("Checkpoint commit failed"); } diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 884e7a564e56..21d4925722d0 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -14,15 +14,6 @@ }, { "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-filter": "android.app.cts.AlarmManagerTest#testSetRepeating" - }, - { - "exclude-filter": "android.app.cts.SystemFeaturesTest#testLocationFeatures" - }, - { - "exclude-filter": "android.app.cts.SystemFeaturesTest#testSensorFeatures" } ] }, diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index a7065216f6a3..ee49f5885e4a 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -223,7 +223,8 @@ public class FaceService extends BiometricServiceBase { @Override public boolean wasUserDetected() { - return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED; + return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED + && mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY; } @Override diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index c45a314e39cc..500a24282191 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -38,13 +38,16 @@ import android.os.Message; import android.os.UserHandle; import android.os.PowerManager; import android.os.SystemClock; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; +import com.android.internal.os.BackgroundThread; import com.android.internal.R; import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import com.android.server.display.whitebalance.AmbientFilter; @@ -64,6 +67,9 @@ public class DisplayModeDirector { private static final boolean DEBUG = false; private static final int MSG_ALLOWED_MODES_CHANGED = 1; + private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2; + private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; + private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4; // Special ID used to indicate that given vote is to be applied globally, rather than to a // specific display. @@ -91,6 +97,7 @@ public class DisplayModeDirector { private final DisplayObserver mDisplayObserver; private final BrightnessObserver mBrightnessObserver; + private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private Listener mListener; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { @@ -103,7 +110,7 @@ public class DisplayModeDirector { mSettingsObserver = new SettingsObserver(context, handler); mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); - + mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); } /** @@ -405,7 +412,7 @@ public class DisplayModeDirector { void onAllowedDisplayModesChanged(); } - private static final class DisplayModeDirectorHandler extends Handler { + private final class DisplayModeDirectorHandler extends Handler { DisplayModeDirectorHandler(Looper looper) { super(looper, null, true /*async*/); } @@ -417,23 +424,65 @@ public class DisplayModeDirector { Listener listener = (Listener) msg.obj; listener.onAllowedDisplayModesChanged(); break; + + case MSG_BRIGHTNESS_THRESHOLDS_CHANGED: + Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; + + if (thresholds != null) { + mBrightnessObserver.onDeviceConfigThresholdsChanged( + thresholds.first, thresholds.second); + } else { + mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null); + } + break; + + case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED: + Float defaultPeakRefreshRate = (Float) msg.obj; + mSettingsObserver.onDeviceConfigDefaultPeakRefreshRateChanged( + defaultPeakRefreshRate); + break; + + case MSG_REFRESH_RATE_IN_ZONE_CHANGED: + int refreshRateInZone = msg.arg1; + mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged( + refreshRateInZone); + break; } } } private static final class Vote { - // We split the app request into two priorities in case we can satisfy one desire without - // the other. - public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 0; - public static final int PRIORITY_APP_REQUEST_SIZE = 1; - public static final int PRIORITY_USER_SETTING_REFRESH_RATE = 2; - public static final int PRIORITY_LOW_BRIGHTNESS = 3; - public static final int PRIORITY_LOW_POWER_MODE = 4; + // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. + // If the higher voters result is a range, it will fix the rate to a single choice. + // It's used to avoid rate switch in certain conditions. + public static final int PRIORITY_LOW_BRIGHTNESS = 0; + + // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. + // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] + public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1; + + // We split the app request into different priorities in case we can satisfy one desire + // without the other. + + // Application can specify preferred refresh rate with below attrs. + // @see android.view.WindowManager.LayoutParams#preferredRefreshRate + // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId + // System also forces some apps like blacklisted app to run at a lower refresh rate. + // @see android.R.array#config_highRefreshRateBlacklist + public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2; + public static final int PRIORITY_APP_REQUEST_SIZE = 3; + + // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest + // of low priority voters. It votes [0, max(PEAK, MIN)] + public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4; + + // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. + public static final int PRIORITY_LOW_POWER_MODE = 5; // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as // appropriate, as well as priorityToString. - public static final int MIN_PRIORITY = PRIORITY_APP_REQUEST_REFRESH_RATE; + public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS; public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE; /** @@ -477,12 +526,16 @@ public class DisplayModeDirector { public static String priorityToString(int priority) { switch (priority) { + case PRIORITY_LOW_BRIGHTNESS: + return "PRIORITY_LOW_BRIGHTNESS"; + case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: + return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; case PRIORITY_APP_REQUEST_REFRESH_RATE: return "PRIORITY_APP_REQUEST_REFRESH_RATE"; case PRIORITY_APP_REQUEST_SIZE: return "PRIORITY_APP_REQUEST_SIZE"; - case PRIORITY_USER_SETTING_REFRESH_RATE: - return "PRIORITY_USER_SETTING_REFRESH_RATE"; + case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE: + return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE"; case PRIORITY_LOW_POWER_MODE: return "PRIORITY_LOW_POWER_MODE"; default: @@ -502,13 +555,15 @@ public class DisplayModeDirector { } private final class SettingsObserver extends ContentObserver { - private final Uri mRefreshRateSetting = + private final Uri mPeakRefreshRateSetting = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + private final Uri mMinRefreshRateSetting = + Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); private final Context mContext; - private final float mDefaultPeakRefreshRate; + private float mDefaultPeakRefreshRate; SettingsObserver(@NonNull Context context, @NonNull Handler handler) { super(handler); @@ -519,20 +574,44 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(mRefreshRateSetting, false /*notifyDescendants*/, this, + cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this, + UserHandle.USER_SYSTEM); + cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); + + Float deviceConfigDefaultPeakRefresh = + mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate(); + if (deviceConfigDefaultPeakRefresh != null) { + mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh; + } + synchronized (mLock) { updateRefreshRateSettingLocked(); updateLowPowerModeSettingLocked(); } } + public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) { + if (defaultPeakRefreshRate == null) { + defaultPeakRefreshRate = (float) mContext.getResources().getInteger( + R.integer.config_defaultPeakRefreshRate); + } + + if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) { + synchronized (mLock) { + mDefaultPeakRefreshRate = defaultPeakRefreshRate; + updateRefreshRateSettingLocked(); + } + } + } + @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - if (mRefreshRateSetting.equals(uri)) { + if (mPeakRefreshRateSetting.equals(uri) + || mMinRefreshRateSetting.equals(uri)) { updateRefreshRateSettingLocked(); } else if (mLowPowerModeSetting.equals(uri)) { updateLowPowerModeSettingLocked(); @@ -550,15 +629,21 @@ public class DisplayModeDirector { vote = null; } updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote); - mBrightnessObserver.onLowPowerModeEnabled(inLowPowerMode); + mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode); } private void updateRefreshRateSettingLocked() { + float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), + Settings.System.MIN_REFRESH_RATE, 0f); float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); - Vote vote = Vote.forRefreshRates(0f, peakRefreshRate); - updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote); - mBrightnessObserver.onPeakRefreshRateEnabled(peakRefreshRate > 60f); + + updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, + Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate))); + updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY)); + + mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate); } public void dumpLocked(PrintWriter pw) { @@ -599,6 +684,7 @@ public class DisplayModeDirector { refreshRateVote = null; sizeVote = null; } + updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote); updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); return; @@ -720,8 +806,8 @@ public class DisplayModeDirector { Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); private final static int LIGHT_SENSOR_RATE_MS = 250; - private final int[] mDisplayBrightnessThresholds; - private final int[] mAmbientBrightnessThresholds; + private int[] mDisplayBrightnessThresholds; + private int[] mAmbientBrightnessThresholds; // valid threshold if any item from the array >= 0 private boolean mShouldObserveDisplayChange; private boolean mShouldObserveAmbientChange; @@ -734,35 +820,127 @@ public class DisplayModeDirector { private AmbientFilter mAmbientFilter; private final Context mContext; - private ScreenStateReceiver mScreenStateReceiver; + private final ScreenStateReceiver mScreenStateReceiver; - // Enable light sensor only when screen is on, peak refresh rate enabled and low power mode - // off. After initialization, these states will be updated from the same handler thread. + // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak + // refresh rate changeable and low power mode off. After initialization, these states will + // be updated from the same handler thread. private boolean mScreenOn = false; - private boolean mPeakRefreshRateEnabled = false; + private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; + private int mRefreshRateInZone; + BrightnessObserver(Context context, Handler handler) { super(handler); mContext = context; + mScreenStateReceiver = new ScreenStateReceiver(mContext); mDisplayBrightnessThresholds = context.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); mAmbientBrightnessThresholds = context.getResources().getIntArray( R.array.config_ambientThresholdsOfPeakRefreshRate); + if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) { throw new RuntimeException("display brightness threshold array and ambient " + "brightness threshold array have different length"); } + } + + public void observe(SensorManager sensorManager) { + mSensorManager = sensorManager; + + // DeviceConfig is accessible after system ready. + int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds(); + int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds(); + + if (brightnessThresholds != null && ambientThresholds != null + && brightnessThresholds.length == ambientThresholds.length) { + mDisplayBrightnessThresholds = brightnessThresholds; + mAmbientBrightnessThresholds = ambientThresholds; + } + + mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone(); + restartObserver(); + mDeviceConfigDisplaySettings.startListening(); + } + + public void onRefreshRateSettingChangedLocked(float min, float max) { + boolean changeable = (max - min > 1f && max > 60f); + if (mRefreshRateChangeable != changeable) { + mRefreshRateChangeable = changeable; + updateSensorStatus(); + if (!changeable) { + // Revoke previous vote from BrightnessObserver + updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null); + } + } + } + + public void onLowPowerModeEnabledLocked(boolean b) { + if (mLowPowerModeEnabled != b) { + mLowPowerModeEnabled = b; + updateSensorStatus(); + } + } + + public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds, + int[] ambientThresholds) { + if (brightnessThresholds != null && ambientThresholds != null + && brightnessThresholds.length == ambientThresholds.length) { + mDisplayBrightnessThresholds = brightnessThresholds; + mAmbientBrightnessThresholds = ambientThresholds; + } else { + // Invalid or empty. Use device default. + mDisplayBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_brightnessThresholdsOfPeakRefreshRate); + mAmbientBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_ambientThresholdsOfPeakRefreshRate); + } + restartObserver(); + } + + public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) { + if (refreshRate != mRefreshRateInZone) { + mRefreshRateInZone = refreshRate; + restartObserver(); + } + } + + public void dumpLocked(PrintWriter pw) { + pw.println(" BrightnessObserver"); + pw.println(" mRefreshRateInZone: " + mRefreshRateInZone); + + for (int d: mDisplayBrightnessThresholds) { + pw.println(" mDisplayBrightnessThreshold: " + d); + } + + for (int d: mAmbientBrightnessThresholds) { + pw.println(" mAmbientBrightnessThreshold: " + d); + } + } + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + synchronized (mLock) { + if (mRefreshRateChangeable) { + onBrightnessChangedLocked(); + } + } + } + + private void restartObserver() { mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds); mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds); - } - public void observe(SensorManager sensorManager) { + final ContentResolver cr = mContext.getContentResolver(); if (mShouldObserveDisplayChange) { - final ContentResolver cr = mContext.getContentResolver(); + // Content Service does not check if an listener has already been registered. + // To ensure only one listener is registered, force an unregistration first. + cr.unregisterContentObserver(this); cr.registerContentObserver(mDisplayBrightnessSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); + } else { + cr.unregisterContentObserver(this); } if (mShouldObserveAmbientChange) { @@ -772,7 +950,7 @@ public class DisplayModeDirector { Sensor lightSensor = null; if (!TextUtils.isEmpty(lightSensorType)) { - List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); for (int i = 0; i < sensors.size(); i++) { Sensor sensor = sensors.get(i); if (lightSensorType.equals(sensor.getStringType())) { @@ -783,69 +961,44 @@ public class DisplayModeDirector { } if (lightSensor == null) { - lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); } if (lightSensor != null) { final Resources res = mContext.getResources(); mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res); - mSensorManager = sensorManager; mLightSensor = lightSensor; // Intent.ACTION_SCREEN_ON is not sticky. Check current screen status. if (mContext.getSystemService(PowerManager.class).isInteractive()) { onScreenOn(true); } - mScreenStateReceiver = new ScreenStateReceiver(mContext); + mScreenStateReceiver.register(); } + } else { + mAmbientFilter = null; + mLightSensor = null; + mScreenStateReceiver.unregister(); } - if (mShouldObserveDisplayChange || mShouldObserveAmbientChange) { + if (mRefreshRateChangeable) { + updateSensorStatus(); synchronized (mLock) { onBrightnessChangedLocked(); } } } - public void onPeakRefreshRateEnabled(boolean b) { - if (mShouldObserveAmbientChange && mPeakRefreshRateEnabled != b) { - mPeakRefreshRateEnabled = b; - updateSensorStatus(); - } - } - - public void onLowPowerModeEnabled(boolean b) { - if (mShouldObserveAmbientChange && mLowPowerModeEnabled != b) { - mLowPowerModeEnabled = b; - updateSensorStatus(); - } - } - - public void dumpLocked(PrintWriter pw) { - pw.println(" BrightnessObserver"); - - for (int d: mDisplayBrightnessThresholds) { - pw.println(" mDisplayBrightnessThreshold: " + d); - } - - for (int d: mAmbientBrightnessThresholds) { - pw.println(" mAmbientBrightnessThreshold: " + d); - } - } - - @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - synchronized (mLock) { - onBrightnessChangedLocked(); - } - } - /** * Checks to see if at least one value is positive, in which case it is necessary to listen * to value changes. */ private boolean checkShouldObserve(int[] a) { + if (mRefreshRateInZone <= 0) { + return false; + } + for (int d: a) { if (d >= 0) { return true; @@ -855,37 +1008,42 @@ public class DisplayModeDirector { return false; } - private void onBrightnessChangedLocked() { - int brightness = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, -1); - - Vote vote = null; + private boolean isInsideZone(int brightness, float lux) { for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) { int disp = mDisplayBrightnessThresholds[i]; int ambi = mAmbientBrightnessThresholds[i]; if (disp >= 0 && ambi >= 0) { if (brightness <= disp && mAmbientLux <= ambi) { - vote = Vote.forRefreshRates(0f, 60f); + return true; } } else if (disp >= 0) { if (brightness <= disp) { - vote = Vote.forRefreshRates(0f, 60f); + return true; } } else if (ambi >= 0) { if (mAmbientLux <= ambi) { - vote = Vote.forRefreshRates(0f, 60f); + return true; } } + } - if (vote != null) { - break; - } + return false; + } + + private void onBrightnessChangedLocked() { + int brightness = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS, -1); + + Vote vote = null; + boolean insideZone = isInsideZone(brightness, mAmbientLux); + if (insideZone) { + vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone); } if (DEBUG) { Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux + - (vote != null ? " 60hz only" : " no refresh rate limit")); + ", Vote " + vote); } updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); } @@ -904,7 +1062,8 @@ public class DisplayModeDirector { return; } - if (mScreenOn && !mLowPowerModeEnabled && mPeakRefreshRateEnabled) { + if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled + && mRefreshRateChangeable) { mSensorManager.registerListener(mLightSensorListener, mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); } else { @@ -993,18 +1152,133 @@ public class DisplayModeDirector { }; private final class ScreenStateReceiver extends BroadcastReceiver { + final Context mContext; + boolean mRegistered; + public ScreenStateReceiver(Context context) { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - context.registerReceiver(this, filter, null, mHandler); + mContext = context; } @Override public void onReceive(Context context, Intent intent) { onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction())); } + + public void register() { + if (!mRegistered) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(this, filter, null, mHandler); + mRegistered = true; + } + } + + public void unregister() { + if (mRegistered) { + mContext.unregisterReceiver(this); + mRegistered = false; + } + } } } + + private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { + public DeviceConfigDisplaySettings() { + } + + public void startListening() { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + BackgroundThread.getExecutor(), this); + } + + /* + * Return null if no such property or wrong format (not comma separated integers). + */ + public int[] getBrightnessThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig. + KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS); + } + + /* + * Return null if no such property or wrong format (not comma separated integers). + */ + public int[] getAmbientThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig. + KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS); + } + + /* + * Return null if no such property + */ + public Float getDefaultPeakRefreshRate() { + float defaultPeakRefreshRate = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); + + if (defaultPeakRefreshRate == -1) { + return null; + } + return defaultPeakRefreshRate; + } + + public int getRefreshRateInZone() { + int defaultRefreshRateInZone = mContext.getResources().getInteger( + R.integer.config_defaultRefreshRateInZone); + + int refreshRate = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE, + defaultRefreshRateInZone); + + return refreshRate; + } + + @Override + public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { + int[] brightnessThresholds = getBrightnessThresholds(); + int[] ambientThresholds = getAmbientThresholds(); + Float defaultPeakRefreshRate = getDefaultPeakRefreshRate(); + int refreshRateInZone = getRefreshRateInZone(); + + mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED, + new Pair<int[], int[]>(brightnessThresholds, ambientThresholds)) + .sendToTarget(); + mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED, + defaultPeakRefreshRate).sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone, + 0).sendToTarget(); + } + + private int[] getIntArrayProperty(String prop) { + String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, + null); + + if (strArray != null) { + return parseIntArray(strArray); + } + + return null; + } + + private int[] parseIntArray(@NonNull String strArray) { + String[] items = strArray.split(","); + int[] array = new int[items.length]; + + try { + for (int i = 0; i < array.length; i++) { + array[i] = Integer.parseInt(items[i]); + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e); + array = null; + } + + return array; + } + } + } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0032e9a8ea51..e75f545eafaa 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -505,6 +505,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE; } + if (callingUid != Process.SYSTEM_UID) { + // Only system_server can use INSTALL_DISABLE_VERIFICATION. + params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION; + } + boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0; if (params.isStaged || isApex) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index e107c9aedf38..08c1bb536211 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -86,9 +86,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private static final String TAG = "RollbackManager"; - // Rollbacks expire after 48 hours. + // Rollbacks expire after 14 days. private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS = - TimeUnit.HOURS.toMillis(48); + TimeUnit.DAYS.toMillis(14); // Lock used to synchronize accesses to in-memory rollback data // structures. By convention, methods with the suffix "Locked" require @@ -1289,7 +1289,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) { - return a.getPackageName().equals(b.getPackageName()) + return a != null && b != null + && a.getPackageName().equals(b.getPackageName()) && a.getLongVersionCode() == b.getLongVersionCode(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 9908b3657121..b0f1e5d69be4 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1172,8 +1172,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return false; } final int displayId = display.getDisplayId(); - return displayId == DEFAULT_DISPLAY - || mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); + if (displayId == DEFAULT_DISPLAY) { + return true; + } + + final long ident = Binder.clearCallingIdentity(); + try { + return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId); + } finally { + Binder.restoreCallingIdentity(ident); + } } void forEachDisplayConnector(Consumer<DisplayConnector> action) { diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java index 5726cb2d87d4..271db2895eac 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java @@ -16,7 +16,7 @@ package com.android.server.wm; -import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_HIGH_REFRESH_RATE_BLACKLIST; import android.annotation.NonNull; import android.annotation.Nullable; @@ -58,9 +58,9 @@ class HighRefreshRateBlacklist { @VisibleForTesting HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) { mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist); - deviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + deviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), new OnPropertyChangedListener()); - final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + final String property = deviceConfig.getProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST); updateBlacklist(property); } @@ -110,7 +110,9 @@ class HighRefreshRateBlacklist { private class OnPropertyChangedListener implements DeviceConfig.OnPropertyChangedListener { public void onPropertyChanged(@NonNull String namespace, @NonNull String name, @Nullable String value) { - updateBlacklist(value); + if (KEY_HIGH_REFRESH_RATE_BLACKLIST.equals(name)) { + updateBlacklist(value); + } } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 5c1dfef925cd..e6ed2edba807 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -38,6 +38,7 @@ import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityModuleConnector; +import android.net.Network; import android.net.NetworkStackClient; import android.os.BaseBundle; import android.os.Binder; diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java index 19ade3394de7..62f2c35f339b 100644 --- a/services/net/java/android/net/ConnectivityModuleConnector.java +++ b/services/net/java/android/net/ConnectivityModuleConnector.java @@ -32,7 +32,7 @@ import android.os.IBinder; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.Slog; @@ -296,8 +296,6 @@ public class ConnectivityModuleConnector { private synchronized void maybeCrashWithTerribleFailure(@NonNull String message, @Nullable String packageName) { logWtf(message, null); - // Called DeviceConfig to minimize merge conflicts - final DeviceConfigStub DeviceConfig = new DeviceConfigStub(mContext); // uptime is monotonic even after a framework restart final long uptime = SystemClock.elapsedRealtime(); final long now = System.currentTimeMillis(); @@ -417,36 +415,4 @@ public class ConnectivityModuleConnector { // dump is thread-safe on SharedLog mLog.dump(null, pw, null); } - - /** - * Stub class to replicate DeviceConfig behavior with minimal merge conflicts. - */ - private class DeviceConfigStub { - private final Context mContext; - - // Namespace is actually unused, but is here to replicate the final API. - private static final String NAMESPACE_CONNECTIVITY = "connectivity"; - - private DeviceConfigStub(Context context) { - mContext = context; - } - - private long getLong( - @NonNull String namespace, @NonNull String key, long defaultVal) { - // Temporary solution until DeviceConfig is available - try { - return Settings.Global.getLong( - mContext.getContentResolver(), TAG + "_" + key, defaultVal); - } catch (Throwable e) { - logWtf("Could not obtain setting " + key, e); - return defaultVal; - } - } - - private boolean getBoolean( - @NonNull String namespace, @NonNull String key, boolean defaultVal) { - // Temporary solution until DeviceConfig is available - return getLong(namespace, key, defaultVal ? 1 : 0) != 0; - } - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java index e02b69c4b058..cd90462fffe4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java @@ -16,7 +16,7 @@ package com.android.server.wm; -import static android.provider.DeviceConfig.WindowManager.KEY_HIGH_REFRESH_RATE_BLACKLIST; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_HIGH_REFRESH_RATE_BLACKLIST; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -126,9 +126,9 @@ public class HighRefreshRateBlacklistTest { @Override public String getProperty(String namespace, String name) { - if (!DeviceConfig.NAMESPACE_WINDOW_MANAGER.equals(namespace) + if (!DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace) || !KEY_HIGH_REFRESH_RATE_BLACKLIST.equals(name)) { - throw new IllegalArgumentException("Only things in NAMESPACE_WINDOW_MANAGER " + throw new IllegalArgumentException("Only things in NAMESPACE_DISPLAY_MANAGER " + "supported."); } return mBlacklist; @@ -138,8 +138,8 @@ public class HighRefreshRateBlacklistTest { public void addOnPropertyChangedListener(String namespace, Executor executor, DeviceConfig.OnPropertyChangedListener listener) { - if (!DeviceConfig.NAMESPACE_WINDOW_MANAGER.equals(namespace)) { - throw new IllegalArgumentException("Only things in NAMESPACE_WINDOW_MANAGER " + if (!DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)) { + throw new IllegalArgumentException("Only things in NAMESPACE_DISPLAY_MANAGER " + "supported."); } mListeners.add(new Pair<>(listener, executor)); @@ -153,7 +153,7 @@ public class HighRefreshRateBlacklistTest { final Executor executor = listenerInfo.second; final DeviceConfig.OnPropertyChangedListener listener = listenerInfo.first; executor.execute(() -> { - listener.onPropertyChanged(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + listener.onPropertyChanged(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_HIGH_REFRESH_RATE_BLACKLIST, blacklist); latch.countDown(); }); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index fccb012311c8..d675a21ede01 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1039,6 +1039,18 @@ public class CarrierConfigManager { "call_forwarding_map_non_number_to_voicemail_bool"; /** + * When {@code true}, the phone will always tell the IMS stack to keep RTT enabled and + * determine on a per-call basis (based on extras from the dialer app) whether a call should be + * an RTT call or not. + * + * When {@code false}, the old behavior is used, where the toggle in accessibility settings is + * used to set the IMS stack's RTT enabled state. + * @hide + */ + public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = + "ignore_rtt_mode_setting_bool"; + + /** * Determines whether conference calls are supported by a carrier. When {@code true}, * conference calling is supported, {@code false otherwise}. */ @@ -3281,6 +3293,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100); sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false); + sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index bad294794337..4315514cb7a5 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -25,6 +25,7 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,6 +64,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { * Tests watchdog triggered staged rollbacks involving only apks. */ @Test + @Ignore("b/139175593 flaky test") public void testBadApkOnly() throws Exception { runPhase("testBadApkOnlyEnableRollback"); getDevice().reboot(); |