Merge "Marked some WM flaky tests as such" into qt-dev
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index c149195..bd3b673 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -52,7 +52,7 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidate();
+            textClassificationManager.invalidateForTesting();
         }
     }
 
@@ -68,7 +68,7 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidate();
+            textClassificationManager.invalidateForTesting();
         }
     }
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 82a34ce..1ad0cfd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2764,6 +2764,9 @@
         if (itemInfo.packageName != null) {
             dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo);
         }
+        if (dr == null && itemInfo != appInfo) {
+            dr = loadUnbadgedItemIcon(appInfo, appInfo);
+        }
         if (dr == null) {
             dr = itemInfo.loadDefaultIcon(this);
         }
diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java
index 9fabfde..0287564 100644
--- a/core/java/android/app/SharedElementCallback.java
+++ b/core/java/android/app/SharedElementCallback.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.RectF;
@@ -49,6 +50,7 @@
     private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
     private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER =
             "sharedElement:snapshot:graphicBuffer";
+    private static final String BUNDLE_SNAPSHOT_COLOR_SPACE = "sharedElement:snapshot:colorSpace";
     private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
     private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
 
@@ -186,6 +188,10 @@
                     } else {
                         GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle();
                         bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer);
+                        ColorSpace cs = bitmap.getColorSpace();
+                        if (cs != null) {
+                            bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId());
+                        }
                     }
                     bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
                             imageView.getScaleType().toString());
@@ -235,8 +241,13 @@
                 return null;
             }
             if (bitmap == null) {
+                ColorSpace colorSpace = null;
+                int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0);
+                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+                }
                 bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer),
-                                                   null);
+                                                   colorSpace);
             }
             ImageView imageView = new ImageView(context);
             view = imageView;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d2f0fb3..338eb2d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1509,11 +1509,6 @@
          * state of the permission can be determined only at install time and cannot be
          * changed on updated or at a later point via the package manager APIs.
          *
-         * <p>The whitelisted non-immutably restricted permissions would be added to
-         * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}
-         * while the immutably restricted permissions would be added to the {@link
-         * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist}
-         *
          * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int)
          * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int)
          */
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 6478de2..8536158 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -881,6 +881,7 @@
 
             maybeSetApiBlacklistExemptions(primaryZygoteState, false);
             maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
         }
     }
 
@@ -896,6 +897,7 @@
 
             maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
             maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+            maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
         }
     }
 
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 774d4ae..9a11104 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -263,6 +263,7 @@
      * Namespace for TextClassifier related features.
      *
      * @hide
+     * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
      */
     @SystemApi
     public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8231a92..75b40fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12039,26 +12039,27 @@
          * entity_list_default use ":" as delimiter for values. Ex:
          *
          * <pre>
-         * smart_linkify_enabled                            (boolean)
-         * system_textclassifier_enabled                    (boolean)
+         * classify_text_max_range_length                   (int)
+         * detect_language_from_text_enabled                (boolean)
+         * entity_list_default                              (String[])
+         * entity_list_editable                             (String[])
+         * entity_list_not_editable                         (String[])
+         * generate_links_log_sample_rate                   (int)
+         * generate_links_max_text_length                   (int)
+         * in_app_conversation_action_types_default         (String[])
+         * lang_id_context_settings                         (float[])
+         * lang_id_threshold_override                       (float)
+         * local_textclassifier_enabled                     (boolean)
          * model_dark_launch_enabled                        (boolean)
-         * smart_selection_enabled                          (boolean)
-         * smart_text_share_enabled                         (boolean)
+         * notification_conversation_action_types_default   (String[])
          * smart_linkify_enabled                            (boolean)
          * smart_select_animation_enabled                   (boolean)
+         * smart_selection_enabled                          (boolean)
+         * smart_text_share_enabled                         (boolean)
          * suggest_selection_max_range_length               (int)
-         * classify_text_max_range_length                   (int)
-         * generate_links_max_text_length                   (int)
-         * generate_links_log_sample_rate                   (int)
-         * entity_list_default                              (String[])
-         * entity_list_not_editable                         (String[])
-         * entity_list_editable                             (String[])
-         * in_app_conversation_action_types_default         (String[])
-         * notification_conversation_action_types_default   (String[])
-         * lang_id_threshold_override                       (float)
+         * system_textclassifier_enabled                    (boolean)
          * template_intent_factory_enabled                  (boolean)
          * translate_in_classification_enabled              (boolean)
-         * detect_language_from_text_enabled                (boolean)
          * </pre>
          *
          * <p>
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index 55e6141..516d336 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -30,6 +30,7 @@
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.Handler;
@@ -62,11 +63,15 @@
     private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
         @Override
         public void provideContextImage(int taskId, GraphicBuffer contextImage,
-                Bundle imageContextRequestExtras) {
+                int colorSpaceId, Bundle imageContextRequestExtras) {
 
             Bitmap wrappedBuffer = null;
             if (contextImage != null) {
-                wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, null);
+                ColorSpace colorSpace = null;
+                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+                }
+                wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
             }
 
             mHandler.sendMessage(
diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
index 1926478..6240e00 100644
--- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
+++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl
@@ -32,6 +32,7 @@
     void provideContextImage(
             int taskId,
             in GraphicBuffer contextImage,
+            int colorSpaceId,
             in Bundle imageContextRequestExtras);
     void suggestContentSelections(
             in SelectionsRequest request,
diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java
index b475412..63de059 100644
--- a/core/java/android/view/textclassifier/ConfigParser.java
+++ b/core/java/android/view/textclassifier/ConfigParser.java
@@ -17,9 +17,19 @@
 
 import android.annotation.Nullable;
 import android.provider.DeviceConfig;
+import android.util.ArrayMap;
 import android.util.KeyValueListParser;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 /**
  * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}.
@@ -27,80 +37,228 @@
  *
  * @hide
  */
-@VisibleForTesting
+@VisibleForTesting(visibility = Visibility.PACKAGE)
 public final class ConfigParser {
     private static final String TAG = "ConfigParser";
 
-    private final KeyValueListParser mParser;
+    static final boolean ENABLE_DEVICE_CONFIG = true;
 
-    // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the
-    // option of using DeviceConfig entirely.
-    static final boolean ENABLE_DEVICE_CONFIG = false;
+    private static final String STRING_LIST_DELIMITER = ":";
 
-    public ConfigParser(@Nullable String textClassifierConstants) {
-        final KeyValueListParser parser = new KeyValueListParser(',');
-        try {
-            parser.setString(textClassifierConstants);
-        } catch (IllegalArgumentException e) {
-            // Failed to parse the settings string, log this and move on with defaults.
-            Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants);
+    private final Supplier<String> mLegacySettingsSupplier;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private final Map<String, Object> mCache = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private @Nullable KeyValueListParser mSettingsParser;  // Call getLegacySettings() instead.
+
+    public ConfigParser(Supplier<String> legacySettingsSupplier) {
+        mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
+    }
+
+    private KeyValueListParser getLegacySettings() {
+        synchronized (mLock) {
+            if (mSettingsParser == null) {
+                final String legacySettings = mLegacySettingsSupplier.get();
+                try {
+                    mSettingsParser = new KeyValueListParser(',');
+                    mSettingsParser.setString(legacySettings);
+                } catch (IllegalArgumentException e) {
+                    // Failed to parse the settings string, log this and move on with defaults.
+                    Log.w(TAG, "Bad text_classifier_constants: " + legacySettings);
+                }
+            }
+            return mSettingsParser;
         }
-        mParser = parser;
     }
 
     /**
-     * Reads a boolean flag.
+     * Reads a boolean setting through the cache.
      */
     public boolean getBoolean(String key, boolean defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getBoolean(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getBoolean(key, defaultValue));
-        } else {
-            return mParser.getBoolean(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Boolean) {
+                return (boolean) cached;
+            }
+            final boolean value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getBoolean(key, defaultValue));
+            } else {
+                value = getLegacySettings().getBoolean(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads an integer flag.
+     * Reads an integer setting through the cache.
      */
     public int getInt(String key, int defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getInt(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getInt(key, defaultValue));
-        } else {
-            return mParser.getInt(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Integer) {
+                return (int) cached;
+            }
+            final int value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getInt(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getInt(key, defaultValue));
+            } else {
+                value = getLegacySettings().getInt(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads a float flag.
+     * Reads a float setting through the cache.
      */
     public float getFloat(String key, float defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getFloat(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getFloat(key, defaultValue));
-        } else {
-            return mParser.getFloat(key, defaultValue);
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof Float) {
+                return (float) cached;
+            }
+            final float value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getFloat(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getFloat(key, defaultValue));
+            } else {
+                value = getLegacySettings().getFloat(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
         }
     }
 
     /**
-     * Reads a string flag.
+     * Reads a string setting through the cache.
      */
     public String getString(String key, String defaultValue) {
-        if (ENABLE_DEVICE_CONFIG) {
-            return DeviceConfig.getString(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    key,
-                    mParser.getString(key, defaultValue));
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof String) {
+                return (String) cached;
+            }
+            final String value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = DeviceConfig.getString(
+                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                        key,
+                        getLegacySettings().getString(key, defaultValue));
+            } else {
+                value = getLegacySettings().getString(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    /**
+     * Reads a string list setting through the cache.
+     */
+    public List<String> getStringList(String key, List<String> defaultValue) {
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof List) {
+                final List asList = (List) cached;
+                if (asList.isEmpty()) {
+                    return Collections.emptyList();
+                } else if (asList.get(0) instanceof String) {
+                    return (List<String>) cached;
+                }
+            }
+            final List<String> value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = getDeviceConfigStringList(
+                        key,
+                        getSettingsStringList(key, defaultValue));
+            } else {
+                value = getSettingsStringList(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    /**
+     * Reads a float array through the cache. The returned array should be expected to be of the
+     * same length as that of the defaultValue.
+     */
+    public float[] getFloatArray(String key, float[] defaultValue) {
+        synchronized (mLock) {
+            final Object cached = mCache.get(key);
+            if (cached instanceof float[]) {
+                return (float[]) cached;
+            }
+            final float[] value;
+            if (ENABLE_DEVICE_CONFIG) {
+                value = getDeviceConfigFloatArray(
+                        key,
+                        getSettingsFloatArray(key, defaultValue));
+            } else {
+                value = getSettingsFloatArray(key, defaultValue);
+            }
+            mCache.put(key, value);
+            return value;
+        }
+    }
+
+    private List<String> getSettingsStringList(String key, List<String> defaultValue) {
+        return parse(mSettingsParser.getString(key, null), defaultValue);
+    }
+
+    private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private float[] getSettingsFloatArray(String key, float[] defaultValue) {
+        return parse(mSettingsParser.getString(key, null), defaultValue);
+    }
+
+    private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
+        if (listStr != null) {
+            return Collections.unmodifiableList(
+                    Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
+        }
+        return defaultValue;
+    }
+
+    private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
+        if (arrayStr != null) {
+            final String[] split = arrayStr.split(STRING_LIST_DELIMITER);
+            if (split.length != defaultValue.length) {
+                return defaultValue;
+            }
+            final float[] result = new float[split.length];
+            for (int i = 0; i < split.length; i++) {
+                try {
+                    result[i] = Float.parseFloat(split[i]);
+                } catch (NumberFormatException e) {
+                    return defaultValue;
+                }
+            }
+            return result;
         } else {
-            return mParser.getString(key, defaultValue);
+            return defaultValue;
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 876e5cc..2964f51 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -16,58 +16,39 @@
 
 package android.view.textclassifier;
 
-import android.annotation.Nullable;
-
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.StringJoiner;
+import java.util.function.Supplier;
 
 /**
  * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_linkify_enabled                            (boolean)
- * system_textclassifier_enabled                    (boolean)
- * model_dark_launch_enabled                        (boolean)
- * smart_selection_enabled                          (boolean)
- * smart_text_share_enabled                         (boolean)
- * smart_linkify_enabled                            (boolean)
- * smart_select_animation_enabled                   (boolean)
- * suggest_selection_max_range_length               (int)
- * classify_text_max_range_length                   (int)
- * generate_links_max_text_length                   (int)
- * generate_links_log_sample_rate                   (int)
- * entity_list_default                              (String[])
- * entity_list_not_editable                         (String[])
- * entity_list_editable                             (String[])
- * in_app_conversation_action_types_default         (String[])
- * notification_conversation_action_types_default   (String[])
- * lang_id_threshold_override                       (float)
- * template_intent_factory_enabled                  (boolean)
- * translate_in_classification_enabled              (boolean)
- * detect_languages_from_text_enabled               (boolean)
- * lang_id_context_settings                         (float[])
- * </pre>
- *
+ * This is encoded as a key=value list, separated by commas.
  * <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
  * Example of setting the values for testing.
+ * <p>
+ * <pre>
  * adb shell settings put global text_classifier_constants \
  *      model_dark_launch_enabled=true,smart_selection_enabled=true, \
  *      entity_list_default=phone:address, \
  *      lang_id_context_settings=20:1.0:0.4
+ * </pre>
+ * <p>
+ * Settings are also available in device config. These take precedence over those in settings
+ * global.
+ * <p>
+ * <pre>
+ * adb shell cmd device_config put textclassifier system_textclassifier_enabled true
+ * </pre>
+ *
+ * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
+ * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER
  * @hide
  */
+// TODO: Rename to TextClassifierSettings.
 public final class TextClassificationConstants {
 
-    private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG;
-
     /**
      * Whether the smart linkify feature is enabled.
      */
@@ -188,29 +169,26 @@
     private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
     private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
-    private static final String STRING_LIST_DELIMITER = ":";
-    private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER)
-            .add(TextClassifier.TYPE_ADDRESS)
-            .add(TextClassifier.TYPE_EMAIL)
-            .add(TextClassifier.TYPE_PHONE)
-            .add(TextClassifier.TYPE_URL)
-            .add(TextClassifier.TYPE_DATE)
-            .add(TextClassifier.TYPE_DATE_TIME)
-            .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
-    private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
-            new StringJoiner(STRING_LIST_DELIMITER)
-                    .add(ConversationAction.TYPE_TEXT_REPLY)
-                    .add(ConversationAction.TYPE_CREATE_REMINDER)
-                    .add(ConversationAction.TYPE_CALL_PHONE)
-                    .add(ConversationAction.TYPE_OPEN_URL)
-                    .add(ConversationAction.TYPE_SEND_EMAIL)
-                    .add(ConversationAction.TYPE_SEND_SMS)
-                    .add(ConversationAction.TYPE_TRACK_FLIGHT)
-                    .add(ConversationAction.TYPE_VIEW_CALENDAR)
-                    .add(ConversationAction.TYPE_VIEW_MAP)
-                    .add(ConversationAction.TYPE_ADD_CONTACT)
-                    .add(ConversationAction.TYPE_COPY)
-                    .toString();
+    private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
+            TextClassifier.TYPE_ADDRESS,
+            TextClassifier.TYPE_EMAIL,
+            TextClassifier.TYPE_PHONE,
+            TextClassifier.TYPE_URL,
+            TextClassifier.TYPE_DATE,
+            TextClassifier.TYPE_DATE_TIME,
+            TextClassifier.TYPE_FLIGHT_NUMBER);
+    private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
+            ConversationAction.TYPE_TEXT_REPLY,
+            ConversationAction.TYPE_CREATE_REMINDER,
+            ConversationAction.TYPE_CALL_PHONE,
+            ConversationAction.TYPE_OPEN_URL,
+            ConversationAction.TYPE_SEND_EMAIL,
+            ConversationAction.TYPE_SEND_SMS,
+            ConversationAction.TYPE_TRACK_FLIGHT,
+            ConversationAction.TYPE_VIEW_CALENDAR,
+            ConversationAction.TYPE_VIEW_MAP,
+            ConversationAction.TYPE_ADD_CONTACT,
+            ConversationAction.TYPE_COPY);
     /**
      * < 0  : Not set. Use value from LangId model.
      * 0 - 1: Override value in LangId model.
@@ -221,259 +199,185 @@
     private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
     private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
     private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
-    private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT =
-            new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString();
+    private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
 
-    private final boolean mSystemTextClassifierEnabled;
-    private final boolean mLocalTextClassifierEnabled;
-    private final boolean mModelDarkLaunchEnabled;
-    private final boolean mSmartSelectionEnabled;
-    private final boolean mSmartTextShareEnabled;
-    private final boolean mSmartLinkifyEnabled;
-    private final boolean mSmartSelectionAnimationEnabled;
-    private final int mSuggestSelectionMaxRangeLength;
-    private final int mClassifyTextMaxRangeLength;
-    private final int mGenerateLinksMaxTextLength;
-    private final int mGenerateLinksLogSampleRate;
-    private final List<String> mEntityListDefault;
-    private final List<String> mEntityListNotEditable;
-    private final List<String> mEntityListEditable;
-    private final List<String> mInAppConversationActionTypesDefault;
-    private final List<String> mNotificationConversationActionTypesDefault;
-    private final float mLangIdThresholdOverride;
-    private final boolean mTemplateIntentFactoryEnabled;
-    private final boolean mTranslateInClassificationEnabled;
-    private final boolean mDetectLanguagesFromTextEnabled;
-    private final float[] mLangIdContextSettings;
+    private final ConfigParser mConfigParser;
 
-    private TextClassificationConstants(@Nullable String settings) {
-        ConfigParser configParser = new ConfigParser(settings);
-        mSystemTextClassifierEnabled =
-                configParser.getBoolean(
-                        SYSTEM_TEXT_CLASSIFIER_ENABLED,
-                        SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
-        mLocalTextClassifierEnabled =
-                configParser.getBoolean(
-                        LOCAL_TEXT_CLASSIFIER_ENABLED,
-                        LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
-        mModelDarkLaunchEnabled =
-                configParser.getBoolean(
-                        MODEL_DARK_LAUNCH_ENABLED,
-                        MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
-        mSmartSelectionEnabled =
-                configParser.getBoolean(
-                        SMART_SELECTION_ENABLED,
-                        SMART_SELECTION_ENABLED_DEFAULT);
-        mSmartTextShareEnabled =
-                configParser.getBoolean(
-                        SMART_TEXT_SHARE_ENABLED,
-                        SMART_TEXT_SHARE_ENABLED_DEFAULT);
-        mSmartLinkifyEnabled =
-                configParser.getBoolean(
-                        SMART_LINKIFY_ENABLED,
-                        SMART_LINKIFY_ENABLED_DEFAULT);
-        mSmartSelectionAnimationEnabled =
-                configParser.getBoolean(
-                        SMART_SELECT_ANIMATION_ENABLED,
-                        SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
-        mSuggestSelectionMaxRangeLength =
-                configParser.getInt(
-                        SUGGEST_SELECTION_MAX_RANGE_LENGTH,
-                        SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
-        mClassifyTextMaxRangeLength =
-                configParser.getInt(
-                        CLASSIFY_TEXT_MAX_RANGE_LENGTH,
-                        CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
-        mGenerateLinksMaxTextLength =
-                configParser.getInt(
-                        GENERATE_LINKS_MAX_TEXT_LENGTH,
-                        GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
-        mGenerateLinksLogSampleRate =
-                configParser.getInt(
-                        GENERATE_LINKS_LOG_SAMPLE_RATE,
-                        GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
-        mEntityListDefault = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_DEFAULT,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mEntityListNotEditable = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_NOT_EDITABLE,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mEntityListEditable = parseStringList(
-                configParser.getString(
-                        ENTITY_LIST_EDITABLE,
-                        ENTITY_LIST_DEFAULT_VALUE));
-        mInAppConversationActionTypesDefault = parseStringList(
-                configParser.getString(
-                        IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
-                        CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
-        mNotificationConversationActionTypesDefault = parseStringList(
-                configParser.getString(
-                        NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
-                        CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
-        mLangIdThresholdOverride =
-                configParser.getFloat(
-                        LANG_ID_THRESHOLD_OVERRIDE,
-                        LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
-        mTemplateIntentFactoryEnabled =
-                configParser.getBoolean(
-                        TEMPLATE_INTENT_FACTORY_ENABLED,
-                        TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
-        mTranslateInClassificationEnabled =
-                configParser.getBoolean(
-                        TRANSLATE_IN_CLASSIFICATION_ENABLED,
-                        TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
-        mDetectLanguagesFromTextEnabled =
-                configParser.getBoolean(
-                        DETECT_LANGUAGES_FROM_TEXT_ENABLED,
-                        DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
-        mLangIdContextSettings = parseFloatArray(
-                configParser,
-                LANG_ID_CONTEXT_SETTINGS,
-                LANG_ID_CONTEXT_SETTINGS_DEFAULT);
-    }
-
-    /** Load from a settings string. */
-    public static TextClassificationConstants loadFromString(String settings) {
-        return new TextClassificationConstants(settings);
+    public TextClassificationConstants(Supplier<String> legacySettingsSupplier) {
+        mConfigParser = new ConfigParser(legacySettingsSupplier);
     }
 
     public boolean isLocalTextClassifierEnabled() {
-        return mLocalTextClassifierEnabled;
+        return mConfigParser.getBoolean(
+                LOCAL_TEXT_CLASSIFIER_ENABLED,
+                LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isSystemTextClassifierEnabled() {
-        return mSystemTextClassifierEnabled;
+        return mConfigParser.getBoolean(
+                SYSTEM_TEXT_CLASSIFIER_ENABLED,
+                SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isModelDarkLaunchEnabled() {
-        return mModelDarkLaunchEnabled;
+        return mConfigParser.getBoolean(
+                MODEL_DARK_LAUNCH_ENABLED,
+                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionEnabled() {
-        return mSmartSelectionEnabled;
+        return mConfigParser.getBoolean(
+                SMART_SELECTION_ENABLED,
+                SMART_SELECTION_ENABLED_DEFAULT);
     }
 
     public boolean isSmartTextShareEnabled() {
-        return mSmartTextShareEnabled;
+        return mConfigParser.getBoolean(
+                SMART_TEXT_SHARE_ENABLED,
+                SMART_TEXT_SHARE_ENABLED_DEFAULT);
     }
 
     public boolean isSmartLinkifyEnabled() {
-        return mSmartLinkifyEnabled;
+        return mConfigParser.getBoolean(
+                SMART_LINKIFY_ENABLED,
+                SMART_LINKIFY_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionAnimationEnabled() {
-        return mSmartSelectionAnimationEnabled;
+        return mConfigParser.getBoolean(
+                SMART_SELECT_ANIMATION_ENABLED,
+                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
     }
 
     public int getSuggestSelectionMaxRangeLength() {
-        return mSuggestSelectionMaxRangeLength;
+        return mConfigParser.getInt(
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getClassifyTextMaxRangeLength() {
-        return mClassifyTextMaxRangeLength;
+        return mConfigParser.getInt(
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH,
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksMaxTextLength() {
-        return mGenerateLinksMaxTextLength;
+        return mConfigParser.getInt(
+                GENERATE_LINKS_MAX_TEXT_LENGTH,
+                GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksLogSampleRate() {
-        return mGenerateLinksLogSampleRate;
+        return mConfigParser.getInt(
+                GENERATE_LINKS_LOG_SAMPLE_RATE,
+                GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
     }
 
     public List<String> getEntityListDefault() {
-        return mEntityListDefault;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_DEFAULT,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListNotEditable() {
-        return mEntityListNotEditable;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_NOT_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListEditable() {
-        return mEntityListEditable;
+        return mConfigParser.getStringList(
+                ENTITY_LIST_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getInAppConversationActionTypes() {
-        return mInAppConversationActionTypesDefault;
+        return mConfigParser.getStringList(
+                IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
+                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public List<String> getNotificationConversationActionTypes() {
-        return mNotificationConversationActionTypesDefault;
+        return mConfigParser.getStringList(
+                NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
+                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public float getLangIdThresholdOverride() {
-        return mLangIdThresholdOverride;
+        return mConfigParser.getFloat(
+                LANG_ID_THRESHOLD_OVERRIDE,
+                LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
     }
 
     public boolean isTemplateIntentFactoryEnabled() {
-        return mTemplateIntentFactoryEnabled;
+        return mConfigParser.getBoolean(
+                TEMPLATE_INTENT_FACTORY_ENABLED,
+                TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
     }
 
     public boolean isTranslateInClassificationEnabled() {
-        return mTranslateInClassificationEnabled;
+        return mConfigParser.getBoolean(
+                TRANSLATE_IN_CLASSIFICATION_ENABLED,
+                TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
     }
 
     public boolean isDetectLanguagesFromTextEnabled() {
-        return mDetectLanguagesFromTextEnabled;
+        return mConfigParser.getBoolean(
+                DETECT_LANGUAGES_FROM_TEXT_ENABLED,
+                DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
     }
 
     public float[] getLangIdContextSettings() {
-        return mLangIdContextSettings;
-    }
-
-    private static List<String> parseStringList(String listStr) {
-        return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
-    }
-
-    private static float[] parseFloatArray(
-            ConfigParser configParser, String key, String defaultStr) {
-        final String str = configParser.getString(key, defaultStr);
-        final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER);
-        String[] split = str.split(STRING_LIST_DELIMITER);
-        if (split.length != defaultSplit.length) {
-            Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults.");
-            split = defaultSplit;
-        }
-        final float[] result = new float[split.length];
-        for (int i = 0; i < split.length; i++) {
-            try {
-                result[i] = Float.parseFloat(split[i]);
-            } catch (NumberFormatException e) {
-                Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults.");
-                result[i] = Float.parseFloat(defaultSplit[i]);
-            }
-        }
-        return result;
+        return mConfigParser.getFloatArray(
+                LANG_ID_CONTEXT_SETTINGS,
+                LANG_ID_CONTEXT_SETTINGS_DEFAULT);
     }
 
     void dump(IndentingPrintWriter pw) {
         pw.println("TextClassificationConstants:");
         pw.increaseIndent();
-        pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled);
-        pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled);
-        pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled);
-        pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled);
-        pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled);
-        pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled);
-        pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled);
-        pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength);
-        pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength);
-        pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength);
-        pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate);
-        pw.printPair("getEntityListDefault", mEntityListDefault);
-        pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
-        pw.printPair("getEntityListEditable", mEntityListEditable);
-        pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault);
-        pw.printPair("getNotificationConversationActionTypes",
-                mNotificationConversationActionTypesDefault);
-        pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride);
-        pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled);
-        pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled);
-        pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled);
-        pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings));
+        pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
+                .println();
+        pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled())
+                .println();
+        pw.printPair("entity_list_default", getEntityListDefault())
+                .println();
+        pw.printPair("entity_list_editable", getEntityListEditable())
+                .println();
+        pw.printPair("entity_list_not_editable", getEntityListNotEditable())
+                .println();
+        pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
+                .println();
+        pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
+                .println();
+        pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
+                .println();
+        pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
+                .println();
+        pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
+                .println();
+        pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
+                .println();
+        pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
+                .println();
+        pw.printPair("notification_conversation_action_types_default",
+                getNotificationConversationActionTypes()).println();
+        pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
+                .println();
+        pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
+                .println();
+        pw.printPair("smart_selection_enabled", isSmartSelectionEnabled())
+                .println();
+        pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
+                .println();
+        pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
+                .println();
+        pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
+                .println();
+        pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
+                .println();
+        pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
+                .println();
         pw.decreaseIndent();
-        pw.println();
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 042b943..95ca9de 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -45,6 +45,9 @@
 
     private static final String LOG_TAG = "TextClassificationManager";
 
+    private static final TextClassificationConstants sDefaultSettings =
+            new TextClassificationConstants(() ->  null);
+
     private final Object mLock = new Object();
     private final TextClassificationSessionFactory mDefaultSessionFactory =
             classificationContext -> new TextClassificationSession(
@@ -129,9 +132,10 @@
     private TextClassificationConstants getSettings() {
         synchronized (mLock) {
             if (mSettings == null) {
-                mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
-                        getApplicationContext().getContentResolver(),
-                        Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+                mSettings = new TextClassificationConstants(
+                        () ->  Settings.Global.getString(
+                                getApplicationContext().getContentResolver(),
+                                Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
             }
             return mSettings;
         }
@@ -251,7 +255,11 @@
 
     /** @hide */
     @VisibleForTesting
-    public void invalidate() {
+    public void invalidateForTesting() {
+        invalidate();
+    }
+
+    private void invalidate() {
         synchronized (mLock) {
             mSettings = null;
             mLocalTextClassifier = null;
@@ -280,9 +288,8 @@
         if (tcm != null) {
             return tcm.getSettings();
         } else {
-            return TextClassificationConstants.loadFromString(Settings.Global.getString(
-                    context.getApplicationContext().getContentResolver(),
-                    Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+            // Use default settings if there is no tcm.
+            return sDefaultSettings;
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 3297523..3e95f1b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -301,7 +301,7 @@
             final ZonedDateTime refTime = ZonedDateTime.now();
             final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
                     ? request.getEntityConfig().resolveEntityListModifications(
-                            getEntitiesForHints(request.getEntityConfig().getHints()))
+                    getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
             final String localesString = concatenateLocales(request.getDefaultLocales());
             final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
@@ -779,8 +779,8 @@
         final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
         Log.v(LOG_TAG,
                 String.format(Locale.US, "LangIdContextSettings: "
-                        + "minimumTextSize=%d, penalizeRatio=%.2f, "
-                        + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
+                                + "minimumTextSize=%d, penalizeRatio=%.2f, "
+                                + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
                         minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
 
         if (end - start < minimumTextSize && penalizeRatio <= 0) {
@@ -903,4 +903,3 @@
         }
     }
 }
-
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 96331c2..659272c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2881,10 +2881,10 @@
 
             if (startType != lastStartType
                     || rowPosition == getContentPreviewRowCount() + getProfileRowCount()) {
-                row.setBackground(
+                row.setForeground(
                         getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
             } else {
-                row.setBackground(null);
+                row.setForeground(null);
             }
 
             int columnCount = holder.getColumnCount();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index dd6fb72..2987b4e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -406,8 +406,13 @@
         private int mHiddenApiAccessStatslogSampleRate = 0;
 
         public static void setHiddenApiAccessLogSampleRates(int sampleRate, int newSampleRate) {
-            sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
-            sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+            if (sampleRate != -1) {
+                sInstance.mHiddenApiAccessLogSampleRate = sampleRate;
+            }
+
+            if (newSampleRate != -1) {
+                sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate;
+            }
         }
 
         public static HiddenApiUsageLogger getInstance() {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ecc9d64..986771d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -136,8 +136,8 @@
     // ColorSpace.Named.SRGB.ordinal() = 0;
     static constexpr jint SRGB = 0;
 
-    // ColorSpace.Named.DISPLAY_P3.ordinal() = 6;
-    static constexpr jint DISPLAY_P3 = 6;
+    // ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
+    static constexpr jint DISPLAY_P3 = 7;
 };
 
 constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index e889e85..96a642c 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -60,7 +60,7 @@
         android:minWidth="48dp"
         android:minHeight="48dp"
         android:clickable="true"
-        android:background="?android:attr/selectableItemBackgroundBorderless">
+        style="?attr/borderlessButtonStyle">
 
       <ImageView
           android:layout_width="24dp"
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
index f1cfe24..d54ce51 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
@@ -26,16 +26,17 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.IOException;
+import java.util.function.Supplier;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ConfigParserTest {
-    private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc";
+    private static final Supplier<String> SETTINGS =
+            () -> "int=42,float=12.3,boolean=true,string=abc";
     private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
             "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER;
     private static final String[] DEVICE_CONFIG_KEYS = new String[]{
@@ -59,7 +60,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getBoolean_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -79,7 +79,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getInt_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -97,7 +96,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getFloat_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
@@ -115,7 +113,6 @@
     }
 
     @Test
-    @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized
     public void getString_deviceConfig() {
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index f6bb1bf..789b829 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -50,9 +50,11 @@
                 + "in_app_conversation_action_types_default=text_reply,"
                 + "notification_conversation_action_types_default=send_email:call_phone,"
                 + "lang_id_threshold_override=0.3,"
-                + "lang_id_context_settings=10:1:0.5";
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString(s);
+                + "lang_id_context_settings=10:1:0.5,"
+                + "detect_language_from_text_enabled=true,"
+                + "template_intent_factory_enabled=true,"
+                + "translate_in_classification_enabled=true";
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -95,6 +97,12 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
     }
 
     @Test
@@ -116,9 +124,11 @@
                 + "in_app_conversation_action_types_default=view_map:track_flight,"
                 + "notification_conversation_action_types_default=share_location,"
                 + "lang_id_threshold_override=2,"
-                + "lang_id_context_settings=30:0.5:0.3";
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString(s);
+                + "lang_id_context_settings=30:0.5:0.3,"
+                + "detect_language_from_text_enabled=false,"
+                + "template_intent_factory_enabled=false,"
+                + "translate_in_classification_enabled=false";
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isFalse();
@@ -161,12 +171,17 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isFalse();
     }
 
     @Test
     public void testLoadFromString_defaultValues() {
-        final TextClassificationConstants constants =
-                TextClassificationConstants.loadFromString("");
+        final TextClassificationConstants constants = new TextClassificationConstants(() -> "");
 
         assertWithMessage("local_textclassifier_enabled")
                 .that(constants.isLocalTextClassifierEnabled()).isTrue();
@@ -213,5 +228,11 @@
                 .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
         Assert.assertArrayEquals("lang_id_context_settings",
                 constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON);
+        assertWithMessage("detect_language_from_text_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("template_intent_factory_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        assertWithMessage("translate_in_classification_enabled")
+                .that(constants.isLocalTextClassifierEnabled()).isTrue();
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 4fcd51c..9148185 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -77,7 +77,7 @@
 
         TextClassifier fallback = TextClassifier.NO_OP;
         TextClassifier classifier = new TextClassifierImpl(
-                fakeContext, TextClassificationConstants.loadFromString(null), fallback);
+                fakeContext, new TextClassificationConstants(() -> null), fallback);
 
         String text = "Contact me at +12122537077";
         String classifiedText = "+12122537077";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index aeb8949..e3eb2a3 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -59,7 +59,7 @@
     // TODO: Implement TextClassifierService testing.
 
     private static final TextClassificationConstants TC_CONSTANTS =
-            TextClassificationConstants.loadFromString("");
+            new TextClassificationConstants(() -> "");
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 98ef00e..27e859c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -83,6 +83,7 @@
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SHUTDOWN"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.mms.service">
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 07f81c1..4471017 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2197,8 +2197,12 @@
     }
 
     /**
-     *
      * @return {@link GraphicBuffer} which is internally used by hardware bitmap
+     *
+     * Note: the GraphicBuffer does *not* have an associated {@link ColorSpace}.
+     * To render this object the same as its rendered with this Bitmap, you
+     * should also call {@link getColorSpace}.
+     *
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 9b70272..7709311 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -28,6 +28,7 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -99,11 +100,17 @@
             ActivityManager.TaskSnapshot snapshot =
                     mActivityTaskManagerInternal.getTaskSnapshot(taskId, false);
             GraphicBuffer snapshotBuffer = null;
+            int colorSpaceId = 0;
             if (snapshot != null) {
                 snapshotBuffer = snapshot.getSnapshot();
+                ColorSpace colorSpace = snapshot.getColorSpace();
+                if (colorSpace != null) {
+                    colorSpaceId = colorSpace.getId();
+                }
             }
 
-            service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras);
+            service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+                    imageContextRequestExtras);
         }
     }
 
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
index 4b36352..a8b7b81 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java
@@ -68,9 +68,9 @@
     }
 
     void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage,
-            @NonNull Bundle imageContextRequestExtras) {
+            int colorSpaceId, @NonNull Bundle imageContextRequestExtras) {
         scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage,
-                imageContextRequestExtras));
+                colorSpaceId, imageContextRequestExtras));
     }
 
     void suggestContentSelections(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11ddceb..4d0d3d2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2185,24 +2185,38 @@
                 "hidden_api_access_statslog_sampling_rate";
 
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x0);
-            if (logSampleRate < 0 || logSampleRate > 0x10000) {
-                logSampleRate = -1;
-            }
-            if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
+            int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
+                    mLogSampleRate);
+            int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE,
+                    mStatslogSampleRate);
+            setSampleRates(logSampleRate, statslogSampleRate);
+        }
+
+        private void setSampleRates(int logSampleRate, int statslogSampleRate) {
+            if (logSampleRate >= 0 && logSampleRate <= 0x10000
+                    && logSampleRate != mLogSampleRate) {
                 mLogSampleRate = logSampleRate;
                 ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
             }
 
-            int statslogSampleRate =
-                    properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
-            if (statslogSampleRate < 0 || statslogSampleRate > 0x10000) {
-                statslogSampleRate = -1;
-            }
-            if (statslogSampleRate != -1 && statslogSampleRate != mStatslogSampleRate) {
+            if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000
+                    && statslogSampleRate != mStatslogSampleRate) {
                 mStatslogSampleRate = statslogSampleRate;
                 ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate);
             }
+
+        }
+
+        /**
+         * Set initial sampling rates from DeviceConfig. This is required after each restart,
+         * if they never get updated.
+         */
+        private void initializeSampleRates() {
+            int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+                    HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0);
+            int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
+                    HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
+            setSampleRates(logSampleRate, statslogSampleRate);
         }
 
         public HiddenApiSettings(Handler handler, Context context) {
@@ -2219,6 +2233,7 @@
                     Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY),
                     false,
                     this);
+            initializeSampleRates();
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT,
                     mContext.getMainExecutor(), this);
             update();
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c56d8ea..d04aa89 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3292,7 +3292,7 @@
         pw.println("    Starts a given operation for a particular application.");
         pw.println("  stop [--user <USER_ID>] <PACKAGE | UID> <OP> ");
         pw.println("    Stops a given operation for a particular application.");
-        pw.println("  set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>");
+        pw.println("  set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
         pw.println("    Set the mode for a particular application and operation.");
         pw.println("  get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
         pw.println("    Return the mode for a particular application and optional operation.");
@@ -3305,12 +3305,11 @@
         pw.println("  read-settings");
         pw.println("    Read the last written settings, replacing current state in RAM.");
         pw.println("  options:");
-        pw.println("    <PACKAGE> an Android package name.");
+        pw.println("    <PACKAGE> an Android package name or its UID if prefixed by --uid");
         pw.println("    <OP>      an AppOps operation.");
         pw.println("    <MODE>    one of allow, ignore, deny, or default");
         pw.println("    <USER_ID> the user id under which the package is installed. If --user is not");
         pw.println("              specified, the current user is assumed.");
-        pw.println("    --uid PACKAGE refer to the UID of the package");
     }
 
     static int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index ba4dcdb..998ee1e 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1104,11 +1104,13 @@
 
     @Override
     public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+        final int callingUid = Binder.getCallingUid();
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null && callback != null) {
-                syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
+                syncManager.getSyncStorageEngine().addStatusChangeListener(
+                        mask, UserHandle.getUserId(callingUid), callback);
             }
         } finally {
             restoreCallingIdentity(identityToken);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 9f80a83..7e79a12 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3375,7 +3375,8 @@
             }
 
             scheduleSyncOperationH(op);
-            mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+            mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                    target.userId);
         }
 
         /**
@@ -3877,7 +3878,8 @@
             EventLog.writeEvent(2720,
                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
-                    resultMessage, downstreamActivity, upstreamActivity);
+                    resultMessage, downstreamActivity, upstreamActivity,
+                    syncOperation.target.userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 6b441a0..c7a3f4b 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -43,6 +43,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -54,6 +55,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IntPair;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -588,9 +590,10 @@
         return mSyncRandomOffset;
     }
 
-    public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
+    public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
         synchronized (mAuthorities) {
-            mChangeListeners.register(callback, mask);
+            final long cookie = IntPair.of(userId, mask);
+            mChangeListeners.register(callback, cookie);
         }
     }
 
@@ -622,14 +625,16 @@
         }
     }
 
-    void reportChange(int which) {
+    void reportChange(int which, int callingUserId) {
         ArrayList<ISyncStatusObserver> reports = null;
         synchronized (mAuthorities) {
             int i = mChangeListeners.beginBroadcast();
             while (i > 0) {
                 i--;
-                Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i);
-                if ((which & mask.intValue()) == 0) {
+                final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
+                final int userId = IntPair.first(cookie);
+                final int mask = IntPair.second(cookie);
+                if ((which & mask) == 0 || callingUserId != userId) {
                     continue;
                 }
                 if (reports == null) {
@@ -719,7 +724,7 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
         queueBackup();
     }
 
@@ -787,7 +792,7 @@
             requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
                     ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
     }
 
     public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -833,7 +838,7 @@
             }
         }
         if (changed) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
         }
     }
 
@@ -871,7 +876,7 @@
     }
 
     public void clearAllBackoffsLocked() {
-        boolean changed = false;
+        final ArraySet<Integer> changedUserIds = new ArraySet<>();
         synchronized (mAuthorities) {
             // Clear backoff for all sync adapters.
             for (AccountInfo accountInfo : mAccounts.values()) {
@@ -888,14 +893,14 @@
                         }
                         authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
                         authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
-                        changed = true;
+                        changedUserIds.add(accountInfo.accountAndUser.userId);
                     }
                 }
             }
         }
 
-        if (changed) {
-            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        for (int i = changedUserIds.size() - 1; i > 0; i--) {
+            reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
         }
     }
 
@@ -921,7 +926,7 @@
             }
             authority.delayUntil = delayUntil;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
     }
 
     /**
@@ -964,7 +969,7 @@
                     new Bundle(),
                     syncExemptionFlag, callingUid, callingPid);
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
         mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
         queueBackup();
     }
@@ -1015,7 +1020,7 @@
             SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
             status.pending = pendingValue;
         }
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
     }
 
     /**
@@ -1103,7 +1108,7 @@
                     activeSyncContext.mStartTime);
             getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
         }
-        reportActiveChange();
+        reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
         return syncInfo;
     }
 
@@ -1120,14 +1125,14 @@
             getCurrentSyncs(userId).remove(syncInfo);
         }
 
-        reportActiveChange();
+        reportActiveChange(userId);
     }
 
     /**
      * To allow others to send active change reports, to poke clients.
      */
-    public void reportActiveChange() {
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
+    public void reportActiveChange(int userId) {
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
     }
 
     /**
@@ -1162,12 +1167,12 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
         return id;
     }
 
     public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
-                              long downstreamActivity, long upstreamActivity) {
+                              long downstreamActivity, long upstreamActivity, int userId) {
         synchronized (mAuthorities) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1307,7 +1312,7 @@
             }
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
+        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 803ab2d..bb9f674 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -44,13 +44,11 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 
 import java.io.File;
@@ -255,21 +253,6 @@
             }
         }
 
-        // Make sure we start a filesystem checkpoint on the next boot.
-        try {
-            IStorageManager storageManager = PackageHelper.getStorageManager();
-            if (storageManager.supportsCheckpoint()) {
-                storageManager.startCheckpoint(1 /* numRetries */);
-            }
-        } catch (Exception e) { // TODO(b/130190815) make a RemoteException again
-            // While StorageManager lives in the same process, the native implementation
-            // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't
-            // reachable.
-            // Since we can live without filesystem checkpointing, just warn in this case
-            // and continue.
-            Slog.w(TAG, "Could not start filesystem checkpoint:", e);
-        }
-
         session.setStagedSessionReady();
         if (sessionContainsApex(session)
                 && !mApexManager.markStagedSessionReady(session.sessionId)) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index db2c742..1682858 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -649,8 +649,7 @@
                 // hasn't actually been updated.
                 onPackageReplaced(apexPackageName);
             }
-
-            mPackageHealthObserver.onBootCompleted();
+            mPackageHealthObserver.onBootCompletedAsync();
         });
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 748a661..bcef66c 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -32,6 +32,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.StatsLog;
@@ -49,9 +50,12 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
- * {@code PackageHealthObserver} for {@code RollbackManagerService}.
+ * {@link PackageHealthObserver} for {@link RollbackManagerService}.
+ * This class monitors crashes and triggers RollbackManager rollback accordingly.
+ * It also monitors native crashes for some short while after boot.
  *
  * @hide
  */
@@ -59,12 +63,21 @@
     private static final String TAG = "RollbackPackageHealthObserver";
     private static final String NAME = "rollback-observer";
     private static final int INVALID_ROLLBACK_ID = -1;
+    // TODO: make the following values configurable via DeviceConfig
+    private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
+            TimeUnit.SECONDS.toMillis(30);
+    private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
+
     private final Context mContext;
     private final Handler mHandler;
     private final File mLastStagedRollbackIdFile;
+    // this field is initialized in the c'tor and then only accessed from mHandler thread, so
+    // no need to guard with a lock
+    private long mNumberOfNativeCrashPollsRemaining;
 
     RollbackPackageHealthObserver(Context context) {
         mContext = context;
+        mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
         HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
         handlerThread.start();
         mHandler = handlerThread.getThreadHandler();
@@ -76,8 +89,6 @@
 
     @Override
     public int onHealthCheckFailed(VersionedPackage failedPackage) {
-        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-
         if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
                 == null) {
             // Don't handle the notification, no rollbacks available for the package
@@ -145,16 +156,29 @@
         PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
     }
 
-    /** Verifies the rollback state after a reboot. */
-    public void onBootCompleted() {
+    /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot
+     * to check for native crashes and mitigate them if needed.
+     */
+    public void onBootCompletedAsync() {
+        mHandler.post(()->onBootCompleted());
+    }
+
+    private void onBootCompleted() {
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+        String moduleMetadataPackageName = getModuleMetadataPackageName();
+        VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
+
+        if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
+            scheduleCheckAndMitigateNativeCrashes();
+        }
+
         int rollbackId = popLastStagedRollbackId();
         if (rollbackId == INVALID_ROLLBACK_ID) {
             // No staged rollback before reboot
             return;
         }
 
-        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
         RollbackInfo rollback = null;
         for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) {
             if (rollbackId == info.getRollbackId()) {
@@ -168,14 +192,12 @@
             return;
         }
 
-        String moduleMetadataPackageName = getModuleMetadataPackageName();
-
         // Use the version of the metadata package that was installed before
         // we rolled back for logging purposes.
-        VersionedPackage moduleMetadataPackage = null;
+        VersionedPackage oldModuleMetadataPackage = null;
         for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
             if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
-                moduleMetadataPackage = packageRollback.getVersionRolledBackFrom();
+                oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom();
                 break;
             }
         }
@@ -187,12 +209,12 @@
             return;
         }
         if (sessionInfo.isStagedSessionApplied()) {
-            logEvent(moduleMetadataPackage,
+            logEvent(oldModuleMetadataPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS);
         } else if (sessionInfo.isStagedSessionReady()) {
             // TODO: What do for staged session ready but not applied
         } else {
-            logEvent(moduleMetadataPackage,
+            logEvent(oldModuleMetadataPackage,
                     StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
         }
     }
@@ -320,4 +342,34 @@
                     moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode());
         }
     }
+
+    /**
+     * This method should be only called on mHandler thread, since it modifies
+     * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free.
+     */
+    private void checkAndMitigateNativeCrashes() {
+        mNumberOfNativeCrashPollsRemaining--;
+        // Check if native watchdog reported a crash
+        if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) {
+            execute(getModuleMetadataPackage());
+            // we stop polling after an attempt to execute rollback, regardless of whether the
+            // attempt succeeds or not
+        } else {
+            if (mNumberOfNativeCrashPollsRemaining > 0) {
+                mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
+                        NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
+            }
+        }
+    }
+
+    /**
+     * Since this method can eventually trigger a RollbackManager rollback, it should be called
+     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+     * session must be entirely completed before we try to rollback.
+     */
+    private void scheduleCheckAndMitigateNativeCrashes() {
+        Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+                + "and mitigate native crashes");
+        mHandler.post(()->checkAndMitigateNativeCrashes());
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 6318486..4d972dc 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -129,7 +129,7 @@
         mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, position,
                         mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(),
-                        mAppToken.getWindowCornerRadiusForAnimation()),
+                        mAppToken.getDisplayContent().getWindowCornerRadius()),
                 mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8785b01..81d6898 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2558,7 +2558,7 @@
                                     getDisplayContent().mAppTransition.canSkipFirstFrame(),
                                     appStackClipMode,
                                     true /* isAppAnimation */,
-                                    getWindowCornerRadiusForAnimation()),
+                                    getDisplayContent().getWindowCornerRadius()),
                             mWmService.mSurfaceAnimationRunner);
                     if (a.getZAdjustment() == Animation.ZORDER_TOP) {
                         mNeedsZBoost = true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b4b60ea..6a5c84a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -554,6 +554,9 @@
     // Last systemUiVisibility we dispatched to windows.
     private int mLastDispatchedSystemUiVisibility = 0;
 
+    /** Corner radius that windows should have in order to match the display. */
+    private final float mWindowCornerRadius;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
@@ -914,6 +917,7 @@
         if (mWmService.mSystemReady) {
             mDisplayPolicy.systemReady();
         }
+        mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
@@ -958,6 +962,10 @@
         return mDisplayId;
     }
 
+    float getWindowCornerRadius() {
+        return mWindowCornerRadius;
+    }
+
     WindowToken getWindowToken(IBinder binder) {
         return mTokenMap.get(binder);
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32d0b32..26430fb 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.Display.TYPE_BUILT_IN;
 import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.InsetsState.TYPE_TOP_GESTURES;
 import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
@@ -157,6 +158,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
@@ -2870,6 +2872,16 @@
                 - statusBarHeight;
     }
 
+    /**
+     * Return corner radius in pixels that should be used on windows in order to cover the display.
+     * The radius is only valid for built-in displays since the one who configures window corner
+     * radius cannot know the corner radius of non-built-in display.
+     */
+    float getWindowCornerRadius() {
+        return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN
+                ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f;
+    }
+
     boolean isShowingDreamLw() {
         return mShowingDream;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7c1ea20..7ac887e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -240,7 +240,6 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
-import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
@@ -793,9 +792,6 @@
     final DisplayManager mDisplayManager;
     final ActivityTaskManagerService mAtmService;
 
-    /** Corner radius that windows should have in order to match the display. */
-    final float mWindowCornerRadius;
-
     /** Indicates whether this device supports wide color gamut / HDR rendering */
     private boolean mHasWideColorGamutSupport;
     private boolean mHasHdrSupport;
@@ -1020,7 +1016,6 @@
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplayWindowSettings = new DisplayWindowSettings(this);
-        mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources());
 
         mTransactionFactory = transactionFactory;
         mTransaction = mTransactionFactory.make();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2a621be..8cbad2d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4599,7 +4599,7 @@
         anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
-                        mToken.getWindowCornerRadiusForAnimation()),
+                        getDisplayContent().getWindowCornerRadius()),
                 mWmService.mSurfaceAnimationRunner);
         startAnimation(mPendingTransaction, adapter);
         commitPendingTransaction();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index f65f0ab..f0b9c62 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -345,8 +345,4 @@
                 mOwnerCanManageAppTokens);
         return mOwnerCanManageAppTokens && (layer > navLayer);
     }
-
-    float getWindowCornerRadiusForAnimation() {
-        return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0;
-    }
 }