Merge "Dead code: the original screenshot policy code" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index deb6f13..1ae9ada 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -789,7 +789,6 @@
     min_sdk_version: "30",
     apex_available: [
         "//apex_available:platform",
-        "com.android.healthfitness",
         "com.android.permission",
         "com.android.nfcservices",
     ],
diff --git a/apex/blobstore/OWNERS b/apex/blobstore/OWNERS
index 676cbc7..f820883 100644
--- a/apex/blobstore/OWNERS
+++ b/apex/blobstore/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 25692
+# Bug component: 1628187
 set noparent
 
 sudheersai@google.com
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 98e53ab..810be8f 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -88,4 +88,11 @@
    namespace: "backstage_power"
    description: "Adjust quota default parameters"
    bug: "347058927"
+}
+
+flag {
+   name: "enforce_quota_policy_to_top_started_jobs"
+   namespace: "backstage_power"
+   description: "Apply the quota policy to jobs started when the app was in TOP state"
+   bug: "374323858"
 }
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 885bad5..37e2fe2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -619,7 +619,7 @@
         }
 
         final int uid = jobStatus.getSourceUid();
-        if (mTopAppCache.get(uid)) {
+        if (!Flags.enforceQuotaPolicyToTopStartedJobs() && mTopAppCache.get(uid)) {
             if (DEBUG) {
                 Slog.d(TAG, jobStatus.toShortString() + " is top started job");
             }
@@ -656,7 +656,9 @@
                 timer.stopTrackingJob(jobStatus);
             }
         }
-        mTopStartedJobs.remove(jobStatus);
+        if (!Flags.enforceQuotaPolicyToTopStartedJobs()) {
+            mTopStartedJobs.remove(jobStatus);
+        }
     }
 
     @Override
@@ -767,7 +769,7 @@
 
     /** @return true if the job was started while the app was in the TOP state. */
     private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
-        return mTopStartedJobs.contains(jobStatus);
+        return !Flags.enforceQuotaPolicyToTopStartedJobs() && mTopStartedJobs.contains(jobStatus);
     }
 
     /** Returns the maximum amount of time this job could run for. */
@@ -4379,11 +4381,13 @@
     @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
-        pw.println("Flags: ");
+        pw.println("Aconfig Flags:");
         pw.println("    " + Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS
                 + ": " + Flags.adjustQuotaDefaultConstants());
         pw.println("    " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS
                 + ": " + Flags.enforceQuotaPolicyToFgsJobs());
+        pw.println("    " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS
+                + ": " + Flags.enforceQuotaPolicyToTopStartedJobs());
         pw.println();
 
         pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
diff --git a/core/api/current.txt b/core/api/current.txt
index 911e7de..a8b9e33 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11240,6 +11240,7 @@
     method public void removeCategory(String);
     method public void removeExtra(String);
     method public void removeFlags(int);
+    method @FlaggedApi("android.security.prevent_intent_redirect") public void removeLaunchSecurityProtection();
     method @NonNull public android.content.Intent replaceExtras(@NonNull android.content.Intent);
     method @NonNull public android.content.Intent replaceExtras(@Nullable android.os.Bundle);
     method public android.content.ComponentName resolveActivity(@NonNull android.content.pm.PackageManager);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1ff8c51..79bea01 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1892,8 +1892,7 @@
   }
 
   @FlaggedApi("android.media.soundtrigger.manager_api") public static final class SoundTrigger.RecognitionConfig implements android.os.Parcelable {
-    ctor @Deprecated public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[], int);
-    ctor public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]);
+    ctor @Deprecated public SoundTrigger.RecognitionConfig(boolean, boolean, @Nullable android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra[], @Nullable byte[]);
   }
 
   public static class SoundTrigger.RecognitionEvent {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ed6b851..fb5a12b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -132,6 +132,7 @@
 import com.android.internal.annotations.Immutable;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.util.UserIcons;
 
 import dalvik.system.VMRuntime;
@@ -822,6 +823,16 @@
                 @Override
                 public Boolean recompute(HasSystemFeatureQuery query) {
                     try {
+                        // As an optimization, check first to see if the feature was defined at
+                        // compile-time as either available or unavailable.
+                        // TODO(b/203143243): Consider hoisting this optimization out of the cache
+                        // after the trunk stable (build) flag has soaked and more features are
+                        // defined at compile-time.
+                        Boolean maybeHasSystemFeature =
+                                RoSystemFeatures.maybeHasFeature(query.name, query.version);
+                        if (maybeHasSystemFeature != null) {
+                            return maybeHasSystemFeature.booleanValue();
+                        }
                         return ActivityThread.currentActivityThread().getPackageManager().
                             hasSystemFeature(query.name, query.version);
                     } catch (RemoteException e) {
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 37fa9a26..1d4c18f 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -286,4 +286,11 @@
   namespace: "systemui"
   description: "Adds logging for notification/modes backup and restore events"
   bug: "289524803"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "notification_classification_ui"
+  namespace: "systemui"
+  description: "Adds UI for NAS classification of notifications"
+  bug: "367996732"
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f719528..e8cec70 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -20,6 +20,7 @@
 import static android.content.ContentProvider.maybeAddUserId;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.security.Flags.FLAG_FRP_ENFORCEMENT;
+import static android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT;
 import static android.security.Flags.preventIntentRedirect;
 
 import android.Manifest;
@@ -40,7 +41,10 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.StatusBarManager;
+import android.app.compat.CompatChanges;
 import android.bluetooth.BluetoothDevice;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -670,6 +674,11 @@
 public class Intent implements Parcelable, Cloneable {
     private static final String TAG = "Intent";
 
+    /** @hide */
+    @ChangeId
+    @Overridable
+    public static final long ENABLE_PREVENT_INTENT_REDIRECT = 29076063L;
+
     private static final String ATTR_ACTION = "action";
     private static final String TAG_CATEGORIES = "categories";
     private static final String ATTR_CATEGORY = "category";
@@ -12240,7 +12249,7 @@
      * @hide
      */
     public void collectExtraIntentKeys() {
-        if (!preventIntentRedirect()) return;
+        if (!isPreventIntentRedirectEnabled()) return;
 
         if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
             for (String key : mExtras.keySet()) {
@@ -12257,6 +12266,14 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public static boolean isPreventIntentRedirectEnabled() {
+        return preventIntentRedirect() && CompatChanges.isChangeEnabled(
+                ENABLE_PREVENT_INTENT_REDIRECT);
+    }
+
     /** @hide */
     public void checkCreatorToken() {
         if (mExtras == null) return;
@@ -12281,6 +12298,20 @@
         mExtras.setIsIntentExtra();
     }
 
+    /**
+     * When an intent comes from another app or component as an embedded extra intent, the system
+     * creates a token to identify the creator of this foreign intent. If this token is missing or
+     * invalid, the system will block the launch of this intent. If it contains a valid token, the
+     * system will perform verification against the creator to block launching target it has no
+     * permission to launch or block it from granting URI access to the tagert it cannot access.
+     * This method provides a way to opt out this feature.
+     */
+    @FlaggedApi(FLAG_PREVENT_INTENT_REDIRECT)
+    public void removeLaunchSecurityProtection() {
+        mExtendedFlags &= ~EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN;
+        removeCreatorTokenInfo();
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString8(mAction);
         Uri.writeToParcel(out, mData);
@@ -12331,7 +12362,7 @@
             out.writeInt(0);
         }
 
-        if (preventIntentRedirect()) {
+        if (isPreventIntentRedirectEnabled()) {
             if (mCreatorTokenInfo == null) {
                 out.writeInt(0);
             } else {
@@ -12398,7 +12429,7 @@
             mOriginalIntent = new Intent(in);
         }
 
-        if (preventIntentRedirect()) {
+        if (isPreventIntentRedirectEnabled()) {
             if (in.readInt() != 0) {
                 mCreatorTokenInfo = new CreatorTokenInfo();
                 mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 22ae676..2ba1078 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -40,6 +40,7 @@
 import android.system.ErrnoException;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.UUID;
@@ -170,17 +171,18 @@
 
     public static SoundTrigger.RecognitionConfig aidl2apiRecognitionConfig(
             RecognitionConfig aidlConfig) {
-        var keyphrases =
-            new SoundTrigger.KeyphraseRecognitionExtra[aidlConfig.phraseRecognitionExtras.length];
-        int i = 0;
+        var keyphrases = new ArrayList<SoundTrigger.KeyphraseRecognitionExtra>(
+            aidlConfig.phraseRecognitionExtras.length);
         for (var extras : aidlConfig.phraseRecognitionExtras) {
-            keyphrases[i++] = aidl2apiPhraseRecognitionExtra(extras);
+            keyphrases.add(aidl2apiPhraseRecognitionExtra(extras));
         }
-        return new SoundTrigger.RecognitionConfig(aidlConfig.captureRequested,
-                false /** allowMultipleTriggers **/,
-                keyphrases,
-                Arrays.copyOf(aidlConfig.data, aidlConfig.data.length),
-                aidl2apiAudioCapabilities(aidlConfig.audioCapabilities));
+        return new SoundTrigger.RecognitionConfig.Builder()
+            .setCaptureRequested(aidlConfig.captureRequested)
+            .setAllowMultipleTriggers(false)
+            .setKeyphrases(keyphrases)
+            .setData(Arrays.copyOf(aidlConfig.data, aidlConfig.data.length))
+            .setAudioCapabilities(aidl2apiAudioCapabilities(aidlConfig.audioCapabilities))
+            .build();
     }
 
     public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 05e91e4..a1e7567 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1529,8 +1529,6 @@
          * config that can be used by
          * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)}
          *
-         * @deprecated should use builder-based constructor instead.
-         *             TODO(b/368042125): remove this method.
          * @param captureRequested Whether the DSP should capture the trigger sound.
          * @param allowMultipleTriggers Whether the service should restart listening after the DSP
          *                              triggers.
@@ -1538,15 +1536,10 @@
          * @param data Opaque data for use by system applications who know about voice engine
          *             internals, typically during enrollment.
          * @param audioCapabilities Bit field encoding of the AudioCapabilities.
-         *
-         * @hide
          */
-        @Deprecated
-        @SuppressWarnings("Todo")
-        @TestApi
-        public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
-                @Nullable byte[] data, int audioCapabilities) {
+        private RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
+                int audioCapabilities) {
             this.mCaptureRequested = captureRequested;
             this.mAllowMultipleTriggers = allowMultipleTriggers;
             this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
@@ -1558,6 +1551,7 @@
          * Constructor for {@link RecognitionConfig} without audioCapabilities. The
          * audioCapabilities is set to 0.
          *
+         * @deprecated Use {@link Builder} instead.
          * @param captureRequested Whether the DSP should capture the trigger sound.
          * @param allowMultipleTriggers Whether the service should restart listening after the DSP
          *                              triggers.
@@ -1567,10 +1561,10 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @Deprecated
         @TestApi
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                @SuppressLint("ArrayReturn") @Nullable KeyphraseRecognitionExtra[] keyphrases,
-                @Nullable byte[] data) {
+                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
             this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
         }
 
@@ -1718,7 +1712,7 @@
 
             /**
              * Sets capture requested state.
-             * @param captureRequested The new requested state.
+             * @param captureRequested Whether the DSP should capture the trigger sound.
              * @return the same Builder instance.
              */
             public @NonNull Builder setCaptureRequested(boolean captureRequested) {
@@ -1728,7 +1722,8 @@
 
             /**
              * Sets allow multiple triggers state.
-             * @param allowMultipleTriggers The new allow multiple triggers state.
+             * @param allowMultipleTriggers Whether the service should restart listening after the
+             *                              DSP triggers.
              * @return the same Builder instance.
              */
             public @NonNull Builder setAllowMultipleTriggers(boolean allowMultipleTriggers) {
@@ -1738,7 +1733,8 @@
 
             /**
              * Sets the keyphrases field.
-             * @param keyphrases The new keyphrases.
+             * @param keyphrases The list of keyphrase specific data associated with this
+             *                   recognition session.
              * @return the same Builder instance.
              */
             public @NonNull Builder setKeyphrases(
@@ -1749,7 +1745,9 @@
 
             /**
              * Sets the data field.
-             * @param data The new data.
+             * @param data Opaque data provided to the DSP associated with this recognition session,
+             *             which is used by system applications who know about voice engine
+             *             internals, typically during enrollment.
              * @return the same Builder instance.
              */
             public @NonNull Builder setData(@Nullable byte[] data) {
@@ -1759,7 +1757,8 @@
 
             /**
              * Sets the audio capabilities field.
-             * @param audioCapabilities The new audio capabilities.
+             * @param audioCapabilities The bit field encoding of the audio capabilities associated
+             *                          with this recognition session.
              * @return the same Builder instance.
              */
             public @NonNull Builder setAudioCapabilities(int audioCapabilities) {
diff --git a/core/java/android/security/forensic/ForensicEvent.aidl b/core/java/android/security/forensic/ForensicEvent.aidl
new file mode 100644
index 0000000..a321fb0
--- /dev/null
+++ b/core/java/android/security/forensic/ForensicEvent.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+
+/** {@hide} */
+parcelable ForensicEvent;
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
new file mode 100644
index 0000000..9cbc5ec
--- /dev/null
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.security.Flags;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * A class that represents a forensic event.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_AFL_API)
+public final class ForensicEvent implements Parcelable {
+    private static final String TAG = "ForensicEvent";
+
+    @NonNull
+    private final String mType;
+
+    @NonNull
+    private final Map<String, String> mKeyValuePairs;
+
+    public static final @NonNull Parcelable.Creator<ForensicEvent> CREATOR =
+            new Parcelable.Creator<>() {
+                public ForensicEvent createFromParcel(Parcel in) {
+                    return new ForensicEvent(in);
+                }
+
+                public ForensicEvent[] newArray(int size) {
+                    return new ForensicEvent[size];
+                }
+            };
+
+    public ForensicEvent(@NonNull String type, @NonNull Map<String, String> keyValuePairs) {
+        mType = type;
+        mKeyValuePairs = keyValuePairs;
+    }
+
+    private ForensicEvent(@NonNull Parcel in) {
+        mType = in.readString();
+        mKeyValuePairs = new ArrayMap<>(in.readInt());
+        in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mType);
+        out.writeInt(mKeyValuePairs.size());
+        out.writeMap(mKeyValuePairs);
+    }
+
+    @FlaggedApi(Flags.FLAG_AFL_API)
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "ForensicEvent{"
+                + "mType=" + mType
+                + ", mKeyValuePairs=" + mKeyValuePairs
+                + '}';
+    }
+}
diff --git a/core/java/android/security/forensic/IBackupTransport.aidl b/core/java/android/security/forensic/IBackupTransport.aidl
new file mode 100644
index 0000000..c2cbc83
--- /dev/null
+++ b/core/java/android/security/forensic/IBackupTransport.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.forensic;
+import android.security.forensic.ForensicEvent;
+
+import com.android.internal.infra.AndroidFuture;
+
+/** {@hide} */
+oneway interface IBackupTransport {
+    /**
+     * Initialize the server side.
+     */
+    void initialize(in AndroidFuture<int> resultFuture);
+
+    /**
+     * Send forensic logging data to the backup destination.
+     * The data is a list of ForensicEvent.
+     * The ForensicEvent is an abstract class that represents
+     * different type of events.
+     */
+    void addData(in List<ForensicEvent> events, in AndroidFuture<int> resultFuture);
+
+    /**
+     * Release the binder to the server.
+     */
+    void release(in AndroidFuture<int> resultFuture);
+}
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index b593902a9..9bb1039 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -77,4 +77,11 @@
     description: "Prevent intent redirect attacks"
     bug: "361143368"
     is_fixed_read_only: true
+}
+
+flag {
+    name: "prevent_intent_redirect_abort_or_throw_exception"
+    namespace: "responsible_apis"
+    description: "Prevent intent redirect attacks by aborting or throwing security exception"
+    bug: "361143368"
 }
\ No newline at end of file
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index ccc17ec..2e660fc 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -73,6 +73,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -1513,10 +1514,11 @@
                     "Recognition for the given keyphrase is not supported");
         }
 
-        KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
+        List<KeyphraseRecognitionExtra> recognitionExtra =
+            new ArrayList<KeyphraseRecognitionExtra>(1);
         // TODO: Do we need to do something about the confidence level here?
-        recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(),
-                mKeyphraseMetadata.getRecognitionModeFlags(), 0, new ConfidenceLevel[0]);
+        recognitionExtra.add(new KeyphraseRecognitionExtra(mKeyphraseMetadata.getId(),
+            mKeyphraseMetadata.getRecognitionModeFlags(), 0, new ConfidenceLevel[0]));
         boolean captureTriggerAudio =
                 (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
         boolean allowMultipleTriggers =
@@ -1534,10 +1536,17 @@
         int code;
         try {
             code = mSoundTriggerSession.startRecognition(
-                    mKeyphraseMetadata.getId(), mLocale.toLanguageTag(), mInternalCallback,
-                    new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                            recognitionExtra, data, audioCapabilities),
-                    runInBatterySaver);
+                mKeyphraseMetadata.getId(),
+                mLocale.toLanguageTag(),
+                mInternalCallback,
+                new RecognitionConfig.Builder()
+                    .setCaptureRequested(captureTriggerAudio)
+                    .setAllowMultipleTriggers(allowMultipleTriggers)
+                    .setKeyphrases(recognitionExtra)
+                    .setData(data)
+                    .setAudioCapabilities(audioCapabilities)
+                    .build(),
+                runInBatterySaver);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4198171..42ac90dd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7175,4 +7175,7 @@
     <string name="identity_check_settings_action"></string>
     <!-- Package for opening identity check settings page [CHAR LIMIT=NONE] [DO NOT TRANSLATE] -->
     <string name="identity_check_settings_package_name">com\u002eandroid\u002esettings</string>
+
+    <!-- The name of the service for forensic backup transport. -->
+    <string name="config_forensicBackupTransport" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0b2b345..dfee85a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5636,4 +5636,7 @@
   <!-- Identity check strings -->
   <java-symbol type="string" name="identity_check_settings_action" />
   <java-symbol type="string" name="identity_check_settings_package_name" />
+
+  <!-- Forensic backup transport -->
+  <java-symbol type="string" name="config_forensicBackupTransport" />
 </resources>
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index 4edf52b..0964aab 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -53,22 +53,9 @@
     name: "use_var_font",
 }
 
-soong_config_module_type {
-    name: "prebuilt_fonts_xml",
-    module_type: "prebuilt_etc",
-    config_namespace: "noto_sans_cjk_config",
-    bool_variables: ["use_var_font"],
-    properties: ["src"],
-}
-
-prebuilt_fonts_xml {
+prebuilt_etc {
     name: "fonts.xml",
     src: "fonts.xml",
-    soong_config_variables: {
-        use_var_font: {
-            src: "fonts_cjkvf.xml",
-        },
-    },
 }
 
 prebuilt_etc {
diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml
deleted file mode 100644
index ae50da1..0000000
--- a/data/fonts/font_fallback.xml
+++ /dev/null
@@ -1,950 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    In this file, all fonts without names are added to the default list.
-    Fonts are chosen based on a match: full BCP-47 language tag including
-    script, then just language, and finally order (the first font containing
-    the glyph).
-
-    Order of appearance is also the tiebreaker for weight matching. This is
-    the reason why the 900 weights of Roboto precede the 700 weights - we
-    prefer the former when an 800 weight is requested. Since bold spans
-    effectively add 300 to the weight, this ensures that 900 is the bold
-    paired with the 500 weight, ensuring adequate contrast.
-
-
-    The font_fallback.xml defines the list of font used by the system.
-
-    `familyset` node:
-      A `familyset` element must be a root node of the font_fallback.xml. No attributes are allowed
-      to `familyset` node.
-      The `familyset` node can contains `family` and `alias` nodes. Any other nodes will be ignored.
-
-    `family` node:
-      A `family` node defines a single font family definition.
-      A font family is a set of fonts for drawing text in various styles such as weight, slant.
-      There are three types of families, default family, named family and locale fallback family.
-
-      The default family is a special family node appeared the first node of the `familyset` node.
-      The default family is used as first priority fallback.
-      Only `name` attribute can be used for default family node. If the `name` attribute is
-      specified, This family will also works as named family.
-
-      The named family is a family that has name attribute. The named family defines a new fallback.
-      For example, if the name attribute is "serif", it creates serif fallback. Developers can
-      access the fallback by using Typeface#create API.
-      The named family can not have attribute other than `name` attribute. The `name` attribute
-      cannot be empty.
-
-      The locale fallback family is a font family that is used for fallback. The fallback family is
-      used when the named family or default family cannot be used. The locale fallback family can
-      have `lang` attribute and `variant` attribute. The `lang` attribute is an optional comma
-      separated BCP-47i language tag. The `variant` is an optional attribute that can be one one
-      `element`, `compact`. If a `variant` attribute is not specified, it is treated as default.
-
-    `alias` node:
-      An `alias` node defines a alias of named family with changing weight offset. An `alias` node
-      can have mandatory `name` and `to` attribute and optional `weight` attribute. This `alias`
-      defines new fallback that has the name of specified `name` attribute. The fallback list is
-      the same to the fallback that of the name specified with `to` attribute. If `weight` attribute
-      is specified, the base weight offset is shifted to the specified value. For example, if the
-      `weight` is 500, the output text is drawn with 500 of weight.
-
-    `font` node:
-      A `font` node defines a single font definition. There are two types of fonts, static font and
-      variable font.
-
-      A static font can have `weight`, `style`, `index` and `postScriptName` attributes. A `weight`
-      is a mandatory attribute that defines the weight of the font. Any number between 0 to 1000 is
-      valid. A `style` is a mandatory attribute that defines the style of the font. A 'style'
-      attribute can be `normal` or `italic`. An `index` is an optional attribute that defines the
-      index of the font collection. If this is not specified, it is treated as 0. If the font file
-      is not a font collection, this attribute is ignored. A `postScriptName` attribute is an
-      optional attribute. A PostScript name is used for identifying target of system font update.
-      If this is not specified, the system assumes the filename is same to PostScript name of the
-      font file. For example, if the font file is "Roboto-Regular.ttf", the system assume the
-      PostScript name of this font is "Roboto-Regular".
-
-      A variable font can be only defined for the variable font file. A variable font can have
-      `axis` child nodes for specifying axis values. A variable font can have all attribute of
-      static font and can have additional `supportedAxes` attribute. A `supportedAxes` attribute
-      is a comma separated supported axis tags. As of Android V, only `wght` and `ital` axes can
-      be specified.
-
-      If `supportedAxes` attribute is not specified, this `font` node works as static font of the
-      single instance of variable font specified with `axis` children.
-
-      If `supportedAxes` attribute is specified, the system dynamically create font instance for the
-      given weight and style value. If `wght` is specified in `supportedAxes` attribute the `weight`
-      attribute and `axis` child that has `wght` tag become optional and ignored because it is
-      determined by system at runtime. Similarly, if `ital` is specified in `supportedAxes`
-      attribute, the `style` attribute and `axis` child that has `ital` tag become optional and
-      ignored.
-
-    `axis` node:
-      An `axis` node defines a font variation value for a tag. An `axis` node can have two mandatory
-      attributes, `tag` and `value`. If the font is variable font and the same tag `axis` node is
-      specified in `supportedAxes` attribute, the style value works like a default instance.
--->
-<familyset>
-    <!-- first font is default -->
-    <family name="sans-serif">
-        <font supportedAxes="wght,ital">Roboto-Regular.ttf
-          <axis tag="wdth" stylevalue="100" />
-        </font>
-   </family>
-
-
-    <!-- Note that aliases must come after the fonts they reference. -->
-    <alias name="sans-serif-thin" to="sans-serif" weight="100" />
-    <alias name="sans-serif-light" to="sans-serif" weight="300" />
-    <alias name="sans-serif-medium" to="sans-serif" weight="500" />
-    <alias name="sans-serif-black" to="sans-serif" weight="900" />
-    <alias name="arial" to="sans-serif" />
-    <alias name="helvetica" to="sans-serif" />
-    <alias name="tahoma" to="sans-serif" />
-    <alias name="verdana" to="sans-serif" />
-
-    <family name="sans-serif-condensed">
-      <font supportedAxes="wght,ital">Roboto-Regular.ttf
-        <axis tag="wdth" stylevalue="75" />
-      </font>
-    </family>
-    <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
-    <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
-
-    <family name="serif">
-        <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
-        <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
-        <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
-    </family>
-    <alias name="serif-bold" to="serif" weight="700" />
-    <alias name="times" to="serif" />
-    <alias name="times new roman" to="serif" />
-    <alias name="palatino" to="serif" />
-    <alias name="georgia" to="serif" />
-    <alias name="baskerville" to="serif" />
-    <alias name="goudy" to="serif" />
-    <alias name="fantasy" to="serif" />
-    <alias name="ITC Stone Serif" to="serif" />
-
-    <family name="monospace">
-        <font weight="400" style="normal">DroidSansMono.ttf</font>
-    </family>
-    <alias name="sans-serif-monospace" to="monospace" />
-    <alias name="monaco" to="monospace" />
-
-    <family name="serif-monospace">
-        <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
-    </family>
-    <alias name="courier" to="serif-monospace" />
-    <alias name="courier new" to="serif-monospace" />
-
-    <family name="casual">
-        <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font>
-    </family>
-
-    <family name="cursive">
-      <font supportedAxes="wght">DancingScript-Regular.ttf</font>
-    </family>
-
-    <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
-    </family>
-
-    <family name="source-sans-pro">
-        <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
-        <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
-        <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
-        <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
-        <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
-        <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
-    </family>
-    <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
-
-    <family name="roboto-flex">
-        <font supportedAxes="wght">RobotoFlex-Regular.ttf
-          <axis tag="wdth" stylevalue="100" />
-        </font>
-    </family>
-
-    <!-- fallback fonts -->
-    <family lang="und-Arab" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
-            NotoNaskhArabic-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
-    </family>
-    <family lang="und-Arab" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
-            NotoNaskhArabicUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Ethi">
-        <font postScriptName="NotoSansEthiopic-Regular" supportedAxes="wght">
-            NotoSansEthiopic-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular" supportedAxes="wght">
-            NotoSerifEthiopic-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Hebr">
-        <font weight="400" style="normal" postScriptName="NotoSansHebrew">
-            NotoSansHebrew-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
-            NotoSansThaiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Armn">
-        <font postScriptName="NotoSansArmenian-Regular" supportedAxes="wght">
-            NotoSansArmenian-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular" supportedAxes="wght">
-            NotoSerifArmenian-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Geor,und-Geok">
-        <font postScriptName="NotoSansGeorgian-Regular" supportedAxes="wght">
-            NotoSansGeorgian-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular" supportedAxes="wght">
-            NotoSerifGeorgian-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Deva" variant="elegant">
-        <font postScriptName="NotoSansDevanagari-Regular" supportedAxes="wght">
-            NotoSansDevanagari-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular" supportedAxes="wght">
-            NotoSerifDevanagari-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Deva" variant="compact">
-        <font postScriptName="NotoSansDevanagariUI-Regular" supportedAxes="wght">
-            NotoSansDevanagariUI-VF.ttf
-        </font>
-    </family>
-
-    <!-- All scripts of India should come after Devanagari, due to shared
-         danda characters.
-    -->
-    <family lang="und-Gujr" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansGujarati">
-            NotoSansGujarati-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
-        <font style="normal" fallbackFor="serif" postScriptName="NotoSerifGujarati-Regular"
-          supportedAxes="wght">
-          NotoSerifGujarati-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Gujr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
-            NotoSansGujaratiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Guru" variant="elegant">
-        <font postScriptName="NotoSansGurmukhi-Regular" supportedAxes="wght">
-            NotoSansGurmukhi-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular" supportedAxes="wght">
-            NotoSerifGurmukhi-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Guru" variant="compact">
-        <font postScriptName="NotoSansGurmukhiUI-Regular" supportedAxes="wght">
-            NotoSansGurmukhiUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Taml" variant="elegant">
-        <font postScriptName="NotoSansTamil-Regular" supportedAxes="wght">
-            NotoSansTamil-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular" supportedAxes="wght">
-            NotoSerifTamil-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Taml" variant="compact">
-        <font postScriptName="NotoSansTamilUI-Regular" supportedAxes="wght">
-            NotoSansTamilUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="elegant">
-        <font postScriptName="NotoSansMalayalam-Regular" supportedAxes="wght">
-            NotoSansMalayalam-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular" supportedAxes="wght">
-            NotoSerifMalayalam-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="compact">
-        <font postScriptName="NotoSansMalayalamUI-Regular" supportedAxes="wght">
-            NotoSansMalayalamUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Beng" variant="elegant">
-        <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght">
-            NotoSansBengali-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght">
-            NotoSerifBengali-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Beng" variant="compact">
-        <font postScriptName="NotoSansBengaliUI-Regular" supportedAxes="wght">
-            NotoSansBengaliUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Telu" variant="elegant">
-        <font postScriptName="NotoSansTelugu-Regular" supportedAxes="wght">
-            NotoSansTelugu-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular" supportedAxes="wght">
-            NotoSerifTelugu-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Telu" variant="compact">
-        <font postScriptName="NotoSansTeluguUI-Regular" supportedAxes="wght">
-            NotoSansTeluguUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Knda" variant="elegant">
-        <font postScriptName="NotoSansKannada-Regular" supportedAxes="wght">
-            NotoSansKannada-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular" supportedAxes="wght">
-            NotoSerifKannada-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Knda" variant="compact">
-        <font postScriptName="NotoSansKannadaUI-Regular" supportedAxes="wght">
-            NotoSansKannadaUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Orya" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
-    </family>
-    <family lang="und-Orya" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
-            NotoSansOriyaUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Sinh" variant="elegant">
-        <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght">
-            NotoSansSinhala-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght">
-            NotoSerifSinhala-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Sinh" variant="compact">
-        <font postScriptName="NotoSansSinhalaUI-Regular" supportedAxes="wght">
-            NotoSansSinhalaUI-VF.ttf
-        </font>
-    </family>
-    <!-- TODO: NotoSansKhmer uses non-standard wght value, so cannot use auto-adjustment. -->
-    <family lang="und-Khmr" variant="elegant">
-        <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="26.0"/>
-        </font>
-        <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="39.0"/>
-        </font>
-        <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="58.0"/>
-        </font>
-        <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="90.0"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="108.0"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="128.0"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="151.0"/>
-        </font>
-        <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="169.0"/>
-        </font>
-        <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="190.0"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
-    </family>
-    <family lang="und-Khmr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
-            NotoSansKhmerUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="elegant">
-        <font weight="400" style="normal">NotoSansLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
-    </family>
-    <family lang="und-Mymr" variant="compact">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
-    </family>
-    <family lang="und-Thaa">
-        <font weight="400" style="normal" postScriptName="NotoSansThaana">
-            NotoSansThaana-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
-    </family>
-    <family lang="und-Cham">
-        <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
-    </family>
-    <family lang="und-Ahom">
-        <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
-    </family>
-    <family lang="und-Adlm">
-        <font postScriptName="NotoSansAdlam-Regular" supportedAxes="wght">
-            NotoSansAdlam-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Avst">
-        <font weight="400" style="normal" postScriptName="NotoSansAvestan">
-            NotoSansAvestan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bali">
-        <font weight="400" style="normal" postScriptName="NotoSansBalinese">
-            NotoSansBalinese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bamu">
-        <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Batk">
-        <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Brah">
-        <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
-            NotoSansBrahmi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bugi">
-        <font weight="400" style="normal" postScriptName="NotoSansBuginese">
-            NotoSansBuginese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Buhd">
-        <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cans">
-        <font weight="400" style="normal">
-            NotoSansCanadianAboriginal-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cari">
-        <font weight="400" style="normal" postScriptName="NotoSansCarian">
-            NotoSansCarian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cakm">
-        <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
-    </family>
-    <family lang="und-Cher">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
-    </family>
-    <family lang="und-Copt">
-        <font weight="400" style="normal" postScriptName="NotoSansCoptic">
-            NotoSansCoptic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xsux">
-        <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
-            NotoSansCuneiform-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cprt">
-        <font weight="400" style="normal" postScriptName="NotoSansCypriot">
-            NotoSansCypriot-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Dsrt">
-        <font weight="400" style="normal" postScriptName="NotoSansDeseret">
-            NotoSansDeseret-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Egyp">
-        <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
-            NotoSansEgyptianHieroglyphs-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Elba">
-        <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
-    </family>
-    <family lang="und-Glag">
-        <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
-            NotoSansGlagolitic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Goth">
-        <font weight="400" style="normal" postScriptName="NotoSansGothic">
-            NotoSansGothic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hano">
-        <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
-            NotoSansHanunoo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Armi">
-        <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
-            NotoSansImperialAramaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phli">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
-            NotoSansInscriptionalPahlavi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Prti">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
-            NotoSansInscriptionalParthian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Java">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
-    </family>
-    <family lang="und-Kthi">
-        <font weight="400" style="normal" postScriptName="NotoSansKaithi">
-            NotoSansKaithi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Kali">
-        <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
-            NotoSansKayahLi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Khar">
-        <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
-            NotoSansKharoshthi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lepc">
-        <font weight="400" style="normal" postScriptName="NotoSansLepcha">
-            NotoSansLepcha-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Limb">
-        <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Linb">
-        <font weight="400" style="normal" postScriptName="NotoSansLinearB">
-            NotoSansLinearB-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lisu">
-        <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lyci">
-        <font weight="400" style="normal" postScriptName="NotoSansLycian">
-            NotoSansLycian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lydi">
-        <font weight="400" style="normal" postScriptName="NotoSansLydian">
-            NotoSansLydian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mand">
-        <font weight="400" style="normal" postScriptName="NotoSansMandaic">
-            NotoSansMandaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mtei">
-        <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
-            NotoSansMeeteiMayek-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Talu">
-        <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
-            NotoSansNewTaiLue-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Nkoo">
-        <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ogam">
-        <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Olck">
-        <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
-            NotoSansOlChiki-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ital">
-        <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
-            NotoSansOldItalic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xpeo">
-        <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
-            NotoSansOldPersian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sarb">
-        <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
-            NotoSansOldSouthArabian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Orkh">
-        <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
-            NotoSansOldTurkic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Osge">
-        <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
-    </family>
-    <family lang="und-Osma">
-        <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
-            NotoSansOsmanya-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phnx">
-        <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
-            NotoSansPhoenician-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Rjng">
-        <font weight="400" style="normal" postScriptName="NotoSansRejang">
-            NotoSansRejang-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Runr">
-        <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Samr">
-        <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
-            NotoSansSamaritan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Saur">
-        <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
-            NotoSansSaurashtra-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Shaw">
-        <font weight="400" style="normal" postScriptName="NotoSansShavian">
-            NotoSansShavian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sund">
-        <font weight="400" style="normal" postScriptName="NotoSansSundanese">
-            NotoSansSundanese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sylo">
-        <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
-            NotoSansSylotiNagri-Regular.ttf
-        </font>
-    </family>
-    <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
-    <family lang="und-Syre">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
-            NotoSansSyriacEstrangela-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrn">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
-            NotoSansSyriacEastern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrj">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
-            NotoSansSyriacWestern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tglg">
-        <font weight="400" style="normal" postScriptName="NotoSansTagalog">
-            NotoSansTagalog-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tagb">
-        <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
-            NotoSansTagbanwa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lana">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
-            NotoSansTaiTham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tavt">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
-            NotoSansTaiViet-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tibt">
-        <font postScriptName="NotoSerifTibetan-Regular" supportedAxes="wght">
-            NotoSerifTibetan-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Tfng">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
-    </family>
-    <family lang="und-Ugar">
-        <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
-            NotoSansUgaritic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Vaii">
-        <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
-        </font>
-    </family>
-    <family>
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
-    </family>
-    <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular">
-            NotoSansCJK-Regular.ttc
-        </font>
-        <font weight="400" style="normal" index="2" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular">
-            NotoSansCJK-Regular.ttc
-        </font>
-        <font weight="400" style="normal" index="3" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular">
-            NotoSansCJK-Regular.ttc
-        </font>
-        <font weight="400" style="normal" index="0" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
-            NotoSerifHentaigana.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-    </family>
-    <family lang="ko">
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">
-            NotoSansCJK-Regular.ttc
-        </font>
-        <font weight="400" style="normal" index="1" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmoji.ttf</font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font>
-    </family>
-    <family lang="und-Zsym">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
-    </family>
-    <!--
-        Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
-        override the East Asian punctuation for Chinese.
-    -->
-    <family lang="und-Tale">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Yiii">
-        <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
-    </family>
-    <family lang="und-Mong">
-        <font weight="400" style="normal" postScriptName="NotoSansMongolian">
-            NotoSansMongolian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phag">
-        <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
-            NotoSansPhagsPa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hluw">
-        <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
-    </family>
-    <family lang="und-Bass">
-        <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
-    </family>
-    <family lang="und-Bhks">
-        <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
-    </family>
-    <family lang="und-Hatr">
-        <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
-    </family>
-    <family lang="und-Lina">
-        <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
-    </family>
-    <family lang="und-Mani">
-        <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
-    </family>
-    <family lang="und-Marc">
-        <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
-    </family>
-    <family lang="und-Merc">
-        <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
-    </family>
-    <family lang="und-Plrd">
-        <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
-    </family>
-    <family lang="und-Mroo">
-        <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
-    </family>
-    <family lang="und-Mult">
-        <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
-    </family>
-    <family lang="und-Nbat">
-        <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
-    </family>
-    <family lang="und-Newa">
-        <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
-    </family>
-    <family lang="und-Narb">
-        <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
-    </family>
-    <family lang="und-Perm">
-        <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
-    </family>
-    <family lang="und-Hmng">
-        <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
-    </family>
-    <family lang="und-Palm">
-        <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
-    </family>
-    <family lang="und-Pauc">
-        <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
-    </family>
-    <family lang="und-Shrd">
-        <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
-    </family>
-    <family lang="und-Sora">
-        <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
-    </family>
-    <family lang="und-Gong">
-        <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Rohg">
-        <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font>
-    </family>
-    <family lang="und-Khoj">
-        <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font>
-    </family>
-    <family lang="und-Gonm">
-        <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Wcho">
-        <font weight="400" style="normal">NotoSansWancho-Regular.otf</font>
-    </family>
-    <family lang="und-Wara">
-        <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
-    </family>
-    <family lang="und-Gran">
-        <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
-    </family>
-    <family lang="und-Modi">
-        <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
-    </family>
-    <family lang="und-Dogr">
-        <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
-    </family>
-    <family lang="und-Medf">
-        <font postScriptName="NotoSansMedefaidrin-Regular" supportedAxes="wght">
-            NotoSansMedefaidrin-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Soyo">
-        <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght">
-            NotoSansSoyombo-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Takr">
-        <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght">
-            NotoSansTakri-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Hmnp">
-        <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Yezi">
-        <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght">
-            NotoSerifYezidi-VF.ttf
-        </font>
-    </family>
-</familyset>
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
deleted file mode 100644
index 407d704..0000000
--- a/data/fonts/font_fallback_cjkvf.xml
+++ /dev/null
@@ -1,966 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    In this file, all fonts without names are added to the default list.
-    Fonts are chosen based on a match: full BCP-47 language tag including
-    script, then just language, and finally order (the first font containing
-    the glyph).
-
-    Order of appearance is also the tiebreaker for weight matching. This is
-    the reason why the 900 weights of Roboto precede the 700 weights - we
-    prefer the former when an 800 weight is requested. Since bold spans
-    effectively add 300 to the weight, this ensures that 900 is the bold
-    paired with the 500 weight, ensuring adequate contrast.
-
-
-    The font_fallback.xml defines the list of font used by the system.
-
-    `familyset` node:
-      A `familyset` element must be a root node of the font_fallback.xml. No attributes are allowed
-      to `familyset` node.
-      The `familyset` node can contains `family` and `alias` nodes. Any other nodes will be ignored.
-
-    `family` node:
-      A `family` node defines a single font family definition.
-      A font family is a set of fonts for drawing text in various styles such as weight, slant.
-      There are three types of families, default family, named family and locale fallback family.
-
-      The default family is a special family node appeared the first node of the `familyset` node.
-      The default family is used as first priority fallback.
-      Only `name` attribute can be used for default family node. If the `name` attribute is
-      specified, This family will also works as named family.
-
-      The named family is a family that has name attribute. The named family defines a new fallback.
-      For example, if the name attribute is "serif", it creates serif fallback. Developers can
-      access the fallback by using Typeface#create API.
-      The named family can not have attribute other than `name` attribute. The `name` attribute
-      cannot be empty.
-
-      The locale fallback family is a font family that is used for fallback. The fallback family is
-      used when the named family or default family cannot be used. The locale fallback family can
-      have `lang` attribute and `variant` attribute. The `lang` attribute is an optional comma
-      separated BCP-47i language tag. The `variant` is an optional attribute that can be one one
-      `element`, `compact`. If a `variant` attribute is not specified, it is treated as default.
-
-    `alias` node:
-      An `alias` node defines a alias of named family with changing weight offset. An `alias` node
-      can have mandatory `name` and `to` attribute and optional `weight` attribute. This `alias`
-      defines new fallback that has the name of specified `name` attribute. The fallback list is
-      the same to the fallback that of the name specified with `to` attribute. If `weight` attribute
-      is specified, the base weight offset is shifted to the specified value. For example, if the
-      `weight` is 500, the output text is drawn with 500 of weight.
-
-    `font` node:
-      A `font` node defines a single font definition. There are two types of fonts, static font and
-      variable font.
-
-      A static font can have `weight`, `style`, `index` and `postScriptName` attributes. A `weight`
-      is a mandatory attribute that defines the weight of the font. Any number between 0 to 1000 is
-      valid. A `style` is a mandatory attribute that defines the style of the font. A 'style'
-      attribute can be `normal` or `italic`. An `index` is an optional attribute that defines the
-      index of the font collection. If this is not specified, it is treated as 0. If the font file
-      is not a font collection, this attribute is ignored. A `postScriptName` attribute is an
-      optional attribute. A PostScript name is used for identifying target of system font update.
-      If this is not specified, the system assumes the filename is same to PostScript name of the
-      font file. For example, if the font file is "Roboto-Regular.ttf", the system assume the
-      PostScript name of this font is "Roboto-Regular".
-
-      A variable font can be only defined for the variable font file. A variable font can have
-      `axis` child nodes for specifying axis values. A variable font can have all attribute of
-      static font and can have additional `supportedAxes` attribute. A `supportedAxes` attribute
-      is a comma separated supported axis tags. As of Android V, only `wght` and `ital` axes can
-      be specified.
-
-      If `supportedAxes` attribute is not specified, this `font` node works as static font of the
-      single instance of variable font specified with `axis` children.
-
-      If `supportedAxes` attribute is specified, the system dynamically create font instance for the
-      given weight and style value. If `wght` is specified in `supportedAxes` attribute the `weight`
-      attribute and `axis` child that has `wght` tag become optional and ignored because it is
-      determined by system at runtime. Similarly, if `ital` is specified in `supportedAxes`
-      attribute, the `style` attribute and `axis` child that has `ital` tag become optional and
-      ignored.
-
-    `axis` node:
-      An `axis` node defines a font variation value for a tag. An `axis` node can have two mandatory
-      attributes, `tag` and `value`. If the font is variable font and the same tag `axis` node is
-      specified in `supportedAxes` attribute, the style value works like a default instance.
--->
-<familyset>
-    <!-- first font is default -->
-    <family name="sans-serif">
-        <font supportedAxes="wght,ital">Roboto-Regular.ttf
-          <axis tag="wdth" stylevalue="100" />
-        </font>
-   </family>
-
-
-    <!-- Note that aliases must come after the fonts they reference. -->
-    <alias name="sans-serif-thin" to="sans-serif" weight="100" />
-    <alias name="sans-serif-light" to="sans-serif" weight="300" />
-    <alias name="sans-serif-medium" to="sans-serif" weight="500" />
-    <alias name="sans-serif-black" to="sans-serif" weight="900" />
-    <alias name="arial" to="sans-serif" />
-    <alias name="helvetica" to="sans-serif" />
-    <alias name="tahoma" to="sans-serif" />
-    <alias name="verdana" to="sans-serif" />
-
-    <family name="sans-serif-condensed">
-      <font supportedAxes="wght,ital">Roboto-Regular.ttf
-        <axis tag="wdth" stylevalue="75" />
-      </font>
-    </family>
-    <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
-    <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
-
-    <family name="serif">
-        <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
-        <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
-        <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
-    </family>
-    <alias name="serif-bold" to="serif" weight="700" />
-    <alias name="times" to="serif" />
-    <alias name="times new roman" to="serif" />
-    <alias name="palatino" to="serif" />
-    <alias name="georgia" to="serif" />
-    <alias name="baskerville" to="serif" />
-    <alias name="goudy" to="serif" />
-    <alias name="fantasy" to="serif" />
-    <alias name="ITC Stone Serif" to="serif" />
-
-    <family name="monospace">
-        <font weight="400" style="normal">DroidSansMono.ttf</font>
-    </family>
-    <alias name="sans-serif-monospace" to="monospace" />
-    <alias name="monaco" to="monospace" />
-
-    <family name="serif-monospace">
-        <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
-    </family>
-    <alias name="courier" to="serif-monospace" />
-    <alias name="courier new" to="serif-monospace" />
-
-    <family name="casual">
-        <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font>
-    </family>
-
-    <family name="cursive">
-      <font supportedAxes="wght">DancingScript-Regular.ttf</font>
-    </family>
-
-    <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
-    </family>
-
-    <family name="source-sans-pro">
-        <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
-        <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
-        <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
-        <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
-        <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
-        <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
-    </family>
-    <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
-
-    <family name="roboto-flex">
-        <font supportedAxes="wght">RobotoFlex-Regular.ttf
-          <axis tag="wdth" stylevalue="100" />
-        </font>
-    </family>
-
-    <!-- fallback fonts -->
-    <family lang="und-Arab" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
-            NotoNaskhArabic-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
-    </family>
-    <family lang="und-Arab" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
-            NotoNaskhArabicUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Ethi">
-        <font postScriptName="NotoSansEthiopic-Regular" supportedAxes="wght">
-            NotoSansEthiopic-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular" supportedAxes="wght">
-            NotoSerifEthiopic-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Hebr">
-        <font weight="400" style="normal" postScriptName="NotoSansHebrew">
-            NotoSansHebrew-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
-            NotoSansThaiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Armn">
-        <font postScriptName="NotoSansArmenian-Regular" supportedAxes="wght">
-            NotoSansArmenian-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular" supportedAxes="wght">
-            NotoSerifArmenian-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Geor,und-Geok">
-        <font postScriptName="NotoSansGeorgian-Regular" supportedAxes="wght">
-            NotoSansGeorgian-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular" supportedAxes="wght">
-            NotoSerifGeorgian-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Deva" variant="elegant">
-        <font postScriptName="NotoSansDevanagari-Regular" supportedAxes="wght">
-            NotoSansDevanagari-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular" supportedAxes="wght">
-            NotoSerifDevanagari-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Deva" variant="compact">
-        <font postScriptName="NotoSansDevanagariUI-Regular" supportedAxes="wght">
-            NotoSansDevanagariUI-VF.ttf
-        </font>
-    </family>
-
-    <!-- All scripts of India should come after Devanagari, due to shared
-         danda characters.
-    -->
-    <family lang="und-Gujr" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansGujarati">
-            NotoSansGujarati-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
-        <font style="normal" fallbackFor="serif" postScriptName="NotoSerifGujarati-Regular"
-          supportedAxes="wght">
-          NotoSerifGujarati-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Gujr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
-            NotoSansGujaratiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Guru" variant="elegant">
-        <font postScriptName="NotoSansGurmukhi-Regular" supportedAxes="wght">
-            NotoSansGurmukhi-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular" supportedAxes="wght">
-            NotoSerifGurmukhi-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Guru" variant="compact">
-        <font postScriptName="NotoSansGurmukhiUI-Regular" supportedAxes="wght">
-            NotoSansGurmukhiUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Taml" variant="elegant">
-        <font postScriptName="NotoSansTamil-Regular" supportedAxes="wght">
-            NotoSansTamil-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular" supportedAxes="wght">
-            NotoSerifTamil-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Taml" variant="compact">
-        <font postScriptName="NotoSansTamilUI-Regular" supportedAxes="wght">
-            NotoSansTamilUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="elegant">
-        <font postScriptName="NotoSansMalayalam-Regular" supportedAxes="wght">
-            NotoSansMalayalam-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular" supportedAxes="wght">
-            NotoSerifMalayalam-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="compact">
-        <font postScriptName="NotoSansMalayalamUI-Regular" supportedAxes="wght">
-            NotoSansMalayalamUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Beng" variant="elegant">
-        <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght">
-            NotoSansBengali-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght">
-            NotoSerifBengali-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Beng" variant="compact">
-        <font postScriptName="NotoSansBengaliUI-Regular" supportedAxes="wght">
-            NotoSansBengaliUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Telu" variant="elegant">
-        <font postScriptName="NotoSansTelugu-Regular" supportedAxes="wght">
-            NotoSansTelugu-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular" supportedAxes="wght">
-            NotoSerifTelugu-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Telu" variant="compact">
-        <font postScriptName="NotoSansTeluguUI-Regular" supportedAxes="wght">
-            NotoSansTeluguUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Knda" variant="elegant">
-        <font postScriptName="NotoSansKannada-Regular" supportedAxes="wght">
-            NotoSansKannada-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular" supportedAxes="wght">
-            NotoSerifKannada-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Knda" variant="compact">
-        <font postScriptName="NotoSansKannadaUI-Regular" supportedAxes="wght">
-            NotoSansKannadaUI-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Orya" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
-    </family>
-    <family lang="und-Orya" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
-            NotoSansOriyaUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Sinh" variant="elegant">
-        <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght">
-            NotoSansSinhala-VF.ttf
-        </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght">
-            NotoSerifSinhala-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Sinh" variant="compact">
-        <font postScriptName="NotoSansSinhalaUI-Regular" supportedAxes="wght">
-            NotoSansSinhalaUI-VF.ttf
-        </font>
-    </family>
-    <!-- TODO: NotoSansKhmer uses non-standard wght value, so cannot use auto-adjustment. -->
-    <family lang="und-Khmr" variant="elegant">
-        <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="26.0"/>
-        </font>
-        <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="39.0"/>
-        </font>
-        <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="58.0"/>
-        </font>
-        <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="90.0"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="108.0"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="128.0"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="151.0"/>
-        </font>
-        <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="169.0"/>
-        </font>
-        <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="190.0"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
-    </family>
-    <family lang="und-Khmr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
-            NotoSansKhmerUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="elegant">
-        <font weight="400" style="normal">NotoSansLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
-    </family>
-    <family lang="und-Mymr" variant="compact">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
-    </family>
-    <family lang="und-Thaa">
-        <font weight="400" style="normal" postScriptName="NotoSansThaana">
-            NotoSansThaana-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
-    </family>
-    <family lang="und-Cham">
-        <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
-    </family>
-    <family lang="und-Ahom">
-        <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
-    </family>
-    <family lang="und-Adlm">
-        <font postScriptName="NotoSansAdlam-Regular" supportedAxes="wght">
-            NotoSansAdlam-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Avst">
-        <font weight="400" style="normal" postScriptName="NotoSansAvestan">
-            NotoSansAvestan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bali">
-        <font weight="400" style="normal" postScriptName="NotoSansBalinese">
-            NotoSansBalinese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bamu">
-        <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Batk">
-        <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Brah">
-        <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
-            NotoSansBrahmi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bugi">
-        <font weight="400" style="normal" postScriptName="NotoSansBuginese">
-            NotoSansBuginese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Buhd">
-        <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cans">
-        <font weight="400" style="normal">
-            NotoSansCanadianAboriginal-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cari">
-        <font weight="400" style="normal" postScriptName="NotoSansCarian">
-            NotoSansCarian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cakm">
-        <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
-    </family>
-    <family lang="und-Cher">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
-    </family>
-    <family lang="und-Copt">
-        <font weight="400" style="normal" postScriptName="NotoSansCoptic">
-            NotoSansCoptic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xsux">
-        <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
-            NotoSansCuneiform-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cprt">
-        <font weight="400" style="normal" postScriptName="NotoSansCypriot">
-            NotoSansCypriot-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Dsrt">
-        <font weight="400" style="normal" postScriptName="NotoSansDeseret">
-            NotoSansDeseret-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Egyp">
-        <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
-            NotoSansEgyptianHieroglyphs-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Elba">
-        <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
-    </family>
-    <family lang="und-Glag">
-        <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
-            NotoSansGlagolitic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Goth">
-        <font weight="400" style="normal" postScriptName="NotoSansGothic">
-            NotoSansGothic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hano">
-        <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
-            NotoSansHanunoo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Armi">
-        <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
-            NotoSansImperialAramaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phli">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
-            NotoSansInscriptionalPahlavi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Prti">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
-            NotoSansInscriptionalParthian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Java">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
-    </family>
-    <family lang="und-Kthi">
-        <font weight="400" style="normal" postScriptName="NotoSansKaithi">
-            NotoSansKaithi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Kali">
-        <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
-            NotoSansKayahLi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Khar">
-        <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
-            NotoSansKharoshthi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lepc">
-        <font weight="400" style="normal" postScriptName="NotoSansLepcha">
-            NotoSansLepcha-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Limb">
-        <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Linb">
-        <font weight="400" style="normal" postScriptName="NotoSansLinearB">
-            NotoSansLinearB-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lisu">
-        <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lyci">
-        <font weight="400" style="normal" postScriptName="NotoSansLycian">
-            NotoSansLycian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lydi">
-        <font weight="400" style="normal" postScriptName="NotoSansLydian">
-            NotoSansLydian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mand">
-        <font weight="400" style="normal" postScriptName="NotoSansMandaic">
-            NotoSansMandaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mtei">
-        <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
-            NotoSansMeeteiMayek-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Talu">
-        <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
-            NotoSansNewTaiLue-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Nkoo">
-        <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ogam">
-        <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Olck">
-        <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
-            NotoSansOlChiki-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ital">
-        <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
-            NotoSansOldItalic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xpeo">
-        <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
-            NotoSansOldPersian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sarb">
-        <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
-            NotoSansOldSouthArabian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Orkh">
-        <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
-            NotoSansOldTurkic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Osge">
-        <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
-    </family>
-    <family lang="und-Osma">
-        <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
-            NotoSansOsmanya-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phnx">
-        <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
-            NotoSansPhoenician-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Rjng">
-        <font weight="400" style="normal" postScriptName="NotoSansRejang">
-            NotoSansRejang-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Runr">
-        <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Samr">
-        <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
-            NotoSansSamaritan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Saur">
-        <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
-            NotoSansSaurashtra-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Shaw">
-        <font weight="400" style="normal" postScriptName="NotoSansShavian">
-            NotoSansShavian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sund">
-        <font weight="400" style="normal" postScriptName="NotoSansSundanese">
-            NotoSansSundanese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sylo">
-        <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
-            NotoSansSylotiNagri-Regular.ttf
-        </font>
-    </family>
-    <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
-    <family lang="und-Syre">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
-            NotoSansSyriacEstrangela-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrn">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
-            NotoSansSyriacEastern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrj">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
-            NotoSansSyriacWestern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tglg">
-        <font weight="400" style="normal" postScriptName="NotoSansTagalog">
-            NotoSansTagalog-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tagb">
-        <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
-            NotoSansTagbanwa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lana">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
-            NotoSansTaiTham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tavt">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
-            NotoSansTaiViet-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tibt">
-        <font postScriptName="NotoSerifTibetan-Regular" supportedAxes="wght">
-            NotoSerifTibetan-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Tfng">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
-    </family>
-    <family lang="und-Ugar">
-        <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
-            NotoSansUgaritic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Vaii">
-        <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
-        </font>
-    </family>
-    <family>
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
-    </family>
-    <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"
-            supportedAxes="wght">
-            NotoSansCJK-Regular.ttc
-            <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
-                 for making regular style as default. -->
-            <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="400" style="normal" index="2" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"
-            supportedAxes="wght">
-            NotoSansCJK-Regular.ttc
-            <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
-                 for making regular style as default. -->
-            <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="400" style="normal" index="3" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"
-            supportedAxes="wght">
-            NotoSansCJK-Regular.ttc
-            <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
-                 for making regular style as default. -->
-            <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="400" style="normal" index="0" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
-            NotoSerifHentaigana.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-    </family>
-    <family lang="ko">
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"
-            supportedAxes="wght">
-            NotoSansCJK-Regular.ttc
-            <!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
-                 for making regular style as default. -->
-            <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="400" style="normal" index="1" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmoji.ttf</font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font>
-    </family>
-    <family lang="und-Zsym">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
-    </family>
-    <!--
-        Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
-        override the East Asian punctuation for Chinese.
-    -->
-    <family lang="und-Tale">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Yiii">
-        <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
-    </family>
-    <family lang="und-Mong">
-        <font weight="400" style="normal" postScriptName="NotoSansMongolian">
-            NotoSansMongolian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phag">
-        <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
-            NotoSansPhagsPa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hluw">
-        <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
-    </family>
-    <family lang="und-Bass">
-        <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
-    </family>
-    <family lang="und-Bhks">
-        <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
-    </family>
-    <family lang="und-Hatr">
-        <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
-    </family>
-    <family lang="und-Lina">
-        <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
-    </family>
-    <family lang="und-Mani">
-        <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
-    </family>
-    <family lang="und-Marc">
-        <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
-    </family>
-    <family lang="und-Merc">
-        <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
-    </family>
-    <family lang="und-Plrd">
-        <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
-    </family>
-    <family lang="und-Mroo">
-        <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
-    </family>
-    <family lang="und-Mult">
-        <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
-    </family>
-    <family lang="und-Nbat">
-        <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
-    </family>
-    <family lang="und-Newa">
-        <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
-    </family>
-    <family lang="und-Narb">
-        <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
-    </family>
-    <family lang="und-Perm">
-        <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
-    </family>
-    <family lang="und-Hmng">
-        <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
-    </family>
-    <family lang="und-Palm">
-        <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
-    </family>
-    <family lang="und-Pauc">
-        <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
-    </family>
-    <family lang="und-Shrd">
-        <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
-    </family>
-    <family lang="und-Sora">
-        <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
-    </family>
-    <family lang="und-Gong">
-        <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Rohg">
-        <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font>
-    </family>
-    <family lang="und-Khoj">
-        <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font>
-    </family>
-    <family lang="und-Gonm">
-        <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Wcho">
-        <font weight="400" style="normal">NotoSansWancho-Regular.otf</font>
-    </family>
-    <family lang="und-Wara">
-        <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
-    </family>
-    <family lang="und-Gran">
-        <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
-    </family>
-    <family lang="und-Modi">
-        <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
-    </family>
-    <family lang="und-Dogr">
-        <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
-    </family>
-    <family lang="und-Medf">
-        <font postScriptName="NotoSansMedefaidrin-Regular" supportedAxes="wght">
-            NotoSansMedefaidrin-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Soyo">
-        <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght">
-            NotoSansSoyombo-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Takr">
-        <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght">
-            NotoSansTakri-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Hmnp">
-        <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-        </font>
-    </family>
-    <family lang="und-Yezi">
-        <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght">
-            NotoSerifYezidi-VF.ttf
-        </font>
-    </family>
-</familyset>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index d1aa8e9..8cbc300 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -1409,24 +1409,123 @@
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular">
+        <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="100"/>
+        </font>
+        <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="200"/>
+        </font>
+        <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="300"/>
+        </font>
+        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="400"/>
+        </font>
+        <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="500"/>
+        </font>
+        <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="600"/>
+        </font>
+        <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="700"/>
+        </font>
+        <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="800"/>
+        </font>
+        <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="900"/>
         </font>
         <font weight="400" style="normal" index="2" fallbackFor="serif"
               postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
         </font>
     </family>
     <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular">
+        <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="100"/>
+        </font>
+        <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="200"/>
+        </font>
+        <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="300"/>
+        </font>
+        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="400"/>
+        </font>
+        <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="500"/>
+        </font>
+        <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="600"/>
+        </font>
+        <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="700"/>
+        </font>
+        <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="800"/>
+        </font>
+        <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="900"/>
         </font>
         <font weight="400" style="normal" index="3" fallbackFor="serif"
               postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
         </font>
     </family>
     <family lang="ja">
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular">
+        <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="100"/>
+        </font>
+        <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="200"/>
+        </font>
+        <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="300"/>
+        </font>
+        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="400"/>
+        </font>
+        <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="500"/>
+        </font>
+        <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="600"/>
+        </font>
+        <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="700"/>
+        </font>
+        <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="800"/>
+        </font>
+        <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="900"/>
         </font>
         <font weight="400" style="normal" index="0" fallbackFor="serif"
               postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
@@ -1443,8 +1542,41 @@
         </font>
     </family>
     <family lang="ko">
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">
+        <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
             NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="100"/>
+        </font>
+        <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="200"/>
+        </font>
+        <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="300"/>
+        </font>
+        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="400"/>
+        </font>
+        <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="500"/>
+        </font>
+        <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="600"/>
+        </font>
+        <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="700"/>
+        </font>
+        <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="800"/>
+        </font>
+        <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
+            NotoSansCJK-Regular.ttc
+            <axis tag="wght" stylevalue="900"/>
         </font>
         <font weight="400" style="normal" index="1" fallbackFor="serif"
               postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
deleted file mode 100644
index 8cbc300..0000000
--- a/data/fonts/fonts_cjkvf.xml
+++ /dev/null
@@ -1,1795 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    DEPRECATED: This XML file is no longer a source of the font files installed
-    in the system.
-
-    For the device vendors: please add your font configurations to the
-    platform/frameworks/base/data/font_fallback.xml and also add it to this XML
-    file as much as possible for apps that reads this XML file.
-
-    For the application developers: please stop reading this XML file and use
-    android.graphics.fonts.SystemFonts#getAvailableFonts Java API or
-    ASystemFontIterator_open NDK API for getting list of system installed
-    font files.
-
-    WARNING: Parsing of this file by third-party apps is not supported. The
-    file, and the font files it refers to, will be renamed and/or moved out
-    from their respective location in the next Android release, and/or the
-    format or syntax of the file may change significantly. If you parse this
-    file for information about system fonts, do it at your own risk. Your
-    application will almost certainly break with the next major Android
-    release.
-
-    In this file, all fonts without names are added to the default list.
-    Fonts are chosen based on a match: full BCP-47 language tag including
-    script, then just language, and finally order (the first font containing
-    the glyph).
-
-    Order of appearance is also the tiebreaker for weight matching. This is
-    the reason why the 900 weights of Roboto precede the 700 weights - we
-    prefer the former when an 800 weight is requested. Since bold spans
-    effectively add 300 to the weight, this ensures that 900 is the bold
-    paired with the 500 weight, ensuring adequate contrast.
-
-    TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required
--->
-<familyset version="23">
-    <!-- first font is default -->
-    <family name="sans-serif">
-        <font weight="100" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="100" />
-        </font>
-        <font weight="200" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="200" />
-        </font>
-        <font weight="300" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="300" />
-        </font>
-        <font weight="400" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="500" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="500" />
-        </font>
-        <font weight="600" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="600" />
-        </font>
-        <font weight="700" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="700" />
-        </font>
-        <font weight="800" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="800" />
-        </font>
-        <font weight="900" style="normal">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="900" />
-        </font>
-        <font weight="100" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="100" />
-        </font>
-        <font weight="200" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="200" />
-        </font>
-        <font weight="300" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="300" />
-        </font>
-        <font weight="400" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="500" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="500" />
-        </font>
-        <font weight="600" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="600" />
-        </font>
-        <font weight="700" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="700" />
-        </font>
-        <font weight="800" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="800" />
-        </font>
-        <font weight="900" style="italic">Roboto-Regular.ttf
-          <axis tag="ital" stylevalue="1" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="900" />
-        </font>
-   </family>
-
-
-    <!-- Note that aliases must come after the fonts they reference. -->
-    <alias name="sans-serif-thin" to="sans-serif" weight="100" />
-    <alias name="sans-serif-light" to="sans-serif" weight="300" />
-    <alias name="sans-serif-medium" to="sans-serif" weight="500" />
-    <alias name="sans-serif-black" to="sans-serif" weight="900" />
-    <alias name="arial" to="sans-serif" />
-    <alias name="helvetica" to="sans-serif" />
-    <alias name="tahoma" to="sans-serif" />
-    <alias name="verdana" to="sans-serif" />
-
-    <family name="sans-serif-condensed">
-      <font weight="100" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="100" />
-      </font>
-      <font weight="200" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="200" />
-      </font>
-      <font weight="300" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="300" />
-      </font>
-      <font weight="400" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="400" />
-      </font>
-      <font weight="500" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="500" />
-      </font>
-      <font weight="600" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="600" />
-      </font>
-      <font weight="700" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="700" />
-      </font>
-      <font weight="800" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="800" />
-      </font>
-      <font weight="900" style="normal">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="0" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="900" />
-      </font>
-      <font weight="100" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="100" />
-      </font>
-      <font weight="200" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="200" />
-      </font>
-      <font weight="300" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="300" />
-      </font>
-      <font weight="400" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="400" />
-      </font>
-      <font weight="500" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="500" />
-      </font>
-      <font weight="600" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="600" />
-      </font>
-      <font weight="700" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="700" />
-      </font>
-      <font weight="800" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="800" />
-      </font>
-      <font weight="900" style="italic">Roboto-Regular.ttf
-        <axis tag="ital" stylevalue="1" />
-        <axis tag="wdth" stylevalue="75" />
-        <axis tag="wght" stylevalue="900" />
-      </font>
-    </family>
-    <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
-    <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
-
-    <family name="serif">
-        <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
-        <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
-        <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
-        <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
-    </family>
-    <alias name="serif-bold" to="serif" weight="700" />
-    <alias name="times" to="serif" />
-    <alias name="times new roman" to="serif" />
-    <alias name="palatino" to="serif" />
-    <alias name="georgia" to="serif" />
-    <alias name="baskerville" to="serif" />
-    <alias name="goudy" to="serif" />
-    <alias name="fantasy" to="serif" />
-    <alias name="ITC Stone Serif" to="serif" />
-
-    <family name="monospace">
-        <font weight="400" style="normal">DroidSansMono.ttf</font>
-    </family>
-    <alias name="sans-serif-monospace" to="monospace" />
-    <alias name="monaco" to="monospace" />
-
-    <family name="serif-monospace">
-        <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
-    </family>
-    <alias name="courier" to="serif-monospace" />
-    <alias name="courier new" to="serif-monospace" />
-
-    <family name="casual">
-        <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font>
-    </family>
-
-    <family name="cursive">
-      <font weight="400" style="normal">DancingScript-Regular.ttf
-        <axis tag="wght" stylevalue="400" />
-      </font>
-      <font weight="700" style="normal">DancingScript-Regular.ttf
-        <axis tag="wght" stylevalue="700" />
-      </font>
-    </family>
-
-    <family name="sans-serif-smallcaps">
-        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
-    </family>
-
-    <family name="source-sans-pro">
-        <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
-        <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
-        <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
-        <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
-        <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
-        <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
-    </family>
-    <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
-
-    <family name="roboto-flex">
-        <font weight="100" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="100" />
-        </font>
-        <font weight="200" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="200" />
-        </font>
-        <font weight="300" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="300" />
-        </font>
-        <font weight="400" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="500" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="500" />
-        </font>
-        <font weight="600" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="600" />
-        </font>
-        <font weight="700" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="700" />
-        </font>
-        <font weight="800" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="800" />
-        </font>
-        <font weight="900" style="normal">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="0" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="900" />
-        </font>
-        <font weight="100" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="100" />
-        </font>
-        <font weight="200" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="200" />
-        </font>
-        <font weight="300" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="300" />
-        </font>
-        <font weight="400" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="400" />
-        </font>
-        <font weight="500" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="500" />
-        </font>
-        <font weight="600" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="600" />
-        </font>
-        <font weight="700" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="700" />
-        </font>
-        <font weight="800" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="800" />
-        </font>
-        <font weight="900" style="italic">RobotoFlex-Regular.ttf
-          <axis tag="slnt" stylevalue="-10" />
-          <axis tag="wdth" stylevalue="100" />
-          <axis tag="wght" stylevalue="900" />
-        </font>
-    </family>
-
-    <!-- fallback fonts -->
-    <family lang="und-Arab" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
-            NotoNaskhArabic-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
-    </family>
-    <family lang="und-Arab" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
-            NotoNaskhArabicUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Ethi">
-        <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular">
-            NotoSansEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular">
-            NotoSansEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular">
-            NotoSansEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular">
-            NotoSansEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Hebr">
-        <font weight="400" style="normal" postScriptName="NotoSansHebrew">
-            NotoSansHebrew-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifThai-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
-    </family>
-    <family lang="und-Thai" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
-            NotoSansThaiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Armn">
-        <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular">
-            NotoSansArmenian-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular">
-            NotoSansArmenian-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular">
-            NotoSansArmenian-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular">
-            NotoSansArmenian-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Geor,und-Geok">
-        <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular">
-            NotoSansGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular">
-            NotoSansGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular">
-            NotoSansGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular">
-            NotoSansGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Deva" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular">
-            NotoSansDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular">
-            NotoSansDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular">
-            NotoSansDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular">
-            NotoSansDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Deva" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
-            NotoSansDevanagariUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
-            NotoSansDevanagariUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
-            NotoSansDevanagariUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
-            NotoSansDevanagariUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-
-    <!-- All scripts of India should come after Devanagari, due to shared
-         danda characters.
-    -->
-    <family lang="und-Gujr" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansGujarati">
-            NotoSansGujarati-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Gujr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
-            NotoSansGujaratiUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Guru" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular">
-            NotoSansGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular">
-            NotoSansGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular">
-            NotoSansGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular">
-            NotoSansGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Guru" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
-            NotoSansGurmukhiUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
-            NotoSansGurmukhiUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
-            NotoSansGurmukhiUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
-            NotoSansGurmukhiUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Taml" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular">
-            NotoSansTamil-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular">
-            NotoSansTamil-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular">
-            NotoSansTamil-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular">
-            NotoSansTamil-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Taml" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular">
-            NotoSansTamilUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular">
-            NotoSansTamilUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular">
-            NotoSansTamilUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular">
-            NotoSansTamilUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular">
-            NotoSansMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular">
-            NotoSansMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular">
-            NotoSansMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular">
-            NotoSansMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Mlym" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
-            NotoSansMalayalamUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
-            NotoSansMalayalamUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
-            NotoSansMalayalamUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
-            NotoSansMalayalamUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Beng" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular">
-            NotoSansBengali-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular">
-            NotoSansBengali-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular">
-            NotoSansBengali-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular">
-            NotoSansBengali-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Beng" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular">
-            NotoSansBengaliUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular">
-            NotoSansBengaliUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular">
-            NotoSansBengaliUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular">
-            NotoSansBengaliUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Telu" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular">
-            NotoSansTelugu-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular">
-            NotoSansTelugu-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular">
-            NotoSansTelugu-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular">
-            NotoSansTelugu-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Telu" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular">
-            NotoSansTeluguUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular">
-            NotoSansTeluguUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular">
-            NotoSansTeluguUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular">
-            NotoSansTeluguUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Knda" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular">
-            NotoSansKannada-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular">
-            NotoSansKannada-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular">
-            NotoSansKannada-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular">
-            NotoSansKannada-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Knda" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular">
-            NotoSansKannadaUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular">
-            NotoSansKannadaUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular">
-            NotoSansKannadaUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular">
-            NotoSansKannadaUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Orya" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
-    </family>
-    <family lang="und-Orya" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
-            NotoSansOriyaUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Sinh" variant="elegant">
-        <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular">
-            NotoSansSinhala-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular">
-            NotoSansSinhala-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular">
-            NotoSansSinhala-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular">
-            NotoSansSinhala-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif"
-              postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Sinh" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
-            NotoSansSinhalaUI-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
-            NotoSansSinhalaUI-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
-            NotoSansSinhalaUI-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
-            NotoSansSinhalaUI-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Khmr" variant="elegant">
-        <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="26.0"/>
-        </font>
-        <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="39.0"/>
-        </font>
-        <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="58.0"/>
-        </font>
-        <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="90.0"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="108.0"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="128.0"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="151.0"/>
-        </font>
-        <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="169.0"/>
-        </font>
-        <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
-            NotoSansKhmer-VF.ttf
-            <axis tag="wdth" stylevalue="100.0"/>
-            <axis tag="wght" stylevalue="190.0"/>
-        </font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
-    </family>
-    <family lang="und-Khmr" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
-            NotoSansKhmerUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="elegant">
-        <font weight="400" style="normal">NotoSansLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
-        <font weight="400" style="normal" fallbackFor="serif">
-            NotoSerifLao-Regular.ttf
-        </font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
-    </family>
-    <family lang="und-Laoo" variant="compact">
-        <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
-    </family>
-    <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
-        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
-        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
-    </family>
-    <family lang="und-Mymr" variant="compact">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
-    </family>
-    <family lang="und-Thaa">
-        <font weight="400" style="normal" postScriptName="NotoSansThaana">
-            NotoSansThaana-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
-    </family>
-    <family lang="und-Cham">
-        <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
-        </font>
-        <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
-    </family>
-    <family lang="und-Ahom">
-        <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
-    </family>
-    <family lang="und-Adlm">
-        <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular">
-            NotoSansAdlam-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular">
-            NotoSansAdlam-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular">
-            NotoSansAdlam-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular">
-            NotoSansAdlam-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Avst">
-        <font weight="400" style="normal" postScriptName="NotoSansAvestan">
-            NotoSansAvestan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bali">
-        <font weight="400" style="normal" postScriptName="NotoSansBalinese">
-            NotoSansBalinese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bamu">
-        <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Batk">
-        <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Brah">
-        <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
-            NotoSansBrahmi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Bugi">
-        <font weight="400" style="normal" postScriptName="NotoSansBuginese">
-            NotoSansBuginese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Buhd">
-        <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cans">
-        <font weight="400" style="normal">
-            NotoSansCanadianAboriginal-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cari">
-        <font weight="400" style="normal" postScriptName="NotoSansCarian">
-            NotoSansCarian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cakm">
-        <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
-    </family>
-    <family lang="und-Cher">
-        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
-    </family>
-    <family lang="und-Copt">
-        <font weight="400" style="normal" postScriptName="NotoSansCoptic">
-            NotoSansCoptic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xsux">
-        <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
-            NotoSansCuneiform-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Cprt">
-        <font weight="400" style="normal" postScriptName="NotoSansCypriot">
-            NotoSansCypriot-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Dsrt">
-        <font weight="400" style="normal" postScriptName="NotoSansDeseret">
-            NotoSansDeseret-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Egyp">
-        <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
-            NotoSansEgyptianHieroglyphs-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Elba">
-        <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
-    </family>
-    <family lang="und-Glag">
-        <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
-            NotoSansGlagolitic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Goth">
-        <font weight="400" style="normal" postScriptName="NotoSansGothic">
-            NotoSansGothic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hano">
-        <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
-            NotoSansHanunoo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Armi">
-        <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
-            NotoSansImperialAramaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phli">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
-            NotoSansInscriptionalPahlavi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Prti">
-        <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
-            NotoSansInscriptionalParthian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Java">
-        <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
-    </family>
-    <family lang="und-Kthi">
-        <font weight="400" style="normal" postScriptName="NotoSansKaithi">
-            NotoSansKaithi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Kali">
-        <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
-            NotoSansKayahLi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Khar">
-        <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
-            NotoSansKharoshthi-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lepc">
-        <font weight="400" style="normal" postScriptName="NotoSansLepcha">
-            NotoSansLepcha-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Limb">
-        <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Linb">
-        <font weight="400" style="normal" postScriptName="NotoSansLinearB">
-            NotoSansLinearB-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lisu">
-        <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lyci">
-        <font weight="400" style="normal" postScriptName="NotoSansLycian">
-            NotoSansLycian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lydi">
-        <font weight="400" style="normal" postScriptName="NotoSansLydian">
-            NotoSansLydian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mand">
-        <font weight="400" style="normal" postScriptName="NotoSansMandaic">
-            NotoSansMandaic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Mtei">
-        <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
-            NotoSansMeeteiMayek-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Talu">
-        <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
-            NotoSansNewTaiLue-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Nkoo">
-        <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ogam">
-        <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Olck">
-        <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
-            NotoSansOlChiki-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Ital">
-        <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
-            NotoSansOldItalic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Xpeo">
-        <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
-            NotoSansOldPersian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sarb">
-        <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
-            NotoSansOldSouthArabian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Orkh">
-        <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
-            NotoSansOldTurkic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Osge">
-        <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
-    </family>
-    <family lang="und-Osma">
-        <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
-            NotoSansOsmanya-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phnx">
-        <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
-            NotoSansPhoenician-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Rjng">
-        <font weight="400" style="normal" postScriptName="NotoSansRejang">
-            NotoSansRejang-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Runr">
-        <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Samr">
-        <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
-            NotoSansSamaritan-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Saur">
-        <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
-            NotoSansSaurashtra-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Shaw">
-        <font weight="400" style="normal" postScriptName="NotoSansShavian">
-            NotoSansShavian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sund">
-        <font weight="400" style="normal" postScriptName="NotoSansSundanese">
-            NotoSansSundanese-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Sylo">
-        <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
-            NotoSansSylotiNagri-Regular.ttf
-        </font>
-    </family>
-    <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
-    <family lang="und-Syre">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
-            NotoSansSyriacEstrangela-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrn">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
-            NotoSansSyriacEastern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Syrj">
-        <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
-            NotoSansSyriacWestern-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tglg">
-        <font weight="400" style="normal" postScriptName="NotoSansTagalog">
-            NotoSansTagalog-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tagb">
-        <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
-            NotoSansTagbanwa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Lana">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
-            NotoSansTaiTham-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tavt">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
-            NotoSansTaiViet-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Tibt">
-        <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular">
-            NotoSerifTibetan-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular">
-            NotoSerifTibetan-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular">
-            NotoSerifTibetan-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular">
-            NotoSerifTibetan-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Tfng">
-        <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
-    </family>
-    <family lang="und-Ugar">
-        <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
-            NotoSansUgaritic-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Vaii">
-        <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
-        </font>
-    </family>
-    <family>
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
-    </family>
-    <family lang="zh-Hans">
-        <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="100"/>
-        </font>
-        <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="200"/>
-        </font>
-        <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="300"/>
-        </font>
-        <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="800"/>
-        </font>
-        <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="900"/>
-        </font>
-        <font weight="400" style="normal" index="2" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="zh-Hant,zh-Bopo">
-        <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="100"/>
-        </font>
-        <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="200"/>
-        </font>
-        <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="300"/>
-        </font>
-        <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="800"/>
-        </font>
-        <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="900"/>
-        </font>
-        <font weight="400" style="normal" index="3" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="100"/>
-        </font>
-        <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="200"/>
-        </font>
-        <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="300"/>
-        </font>
-        <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="800"/>
-        </font>
-        <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="900"/>
-        </font>
-        <font weight="400" style="normal" index="0" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="ja">
-        <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
-            NotoSerifHentaigana.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
-            NotoSerifHentaigana.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="ko">
-        <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="100"/>
-        </font>
-        <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="200"/>
-        </font>
-        <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="300"/>
-        </font>
-        <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-        <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="800"/>
-        </font>
-        <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
-            NotoSansCJK-Regular.ttc
-            <axis tag="wght" stylevalue="900"/>
-        </font>
-        <font weight="400" style="normal" index="1" fallbackFor="serif"
-              postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
-        </font>
-    </family>
-    <family lang="und-Zsye" ignore="true">
-        <font weight="400" style="normal">NotoColorEmojiLegacy.ttf</font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmoji.ttf</font>
-    </family>
-    <family lang="und-Zsye">
-        <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font>
-    </family>
-    <family lang="und-Zsym">
-        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
-    </family>
-    <!--
-        Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
-        override the East Asian punctuation for Chinese.
-    -->
-    <family lang="und-Tale">
-        <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Yiii">
-        <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
-    </family>
-    <family lang="und-Mong">
-        <font weight="400" style="normal" postScriptName="NotoSansMongolian">
-            NotoSansMongolian-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Phag">
-        <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
-            NotoSansPhagsPa-Regular.ttf
-        </font>
-    </family>
-    <family lang="und-Hluw">
-        <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
-    </family>
-    <family lang="und-Bass">
-        <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
-    </family>
-    <family lang="und-Bhks">
-        <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
-    </family>
-    <family lang="und-Hatr">
-        <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
-    </family>
-    <family lang="und-Lina">
-        <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
-    </family>
-    <family lang="und-Mani">
-        <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
-    </family>
-    <family lang="und-Marc">
-        <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
-    </family>
-    <family lang="und-Merc">
-        <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
-    </family>
-    <family lang="und-Plrd">
-        <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
-    </family>
-    <family lang="und-Mroo">
-        <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
-    </family>
-    <family lang="und-Mult">
-        <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
-    </family>
-    <family lang="und-Nbat">
-        <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
-    </family>
-    <family lang="und-Newa">
-        <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
-    </family>
-    <family lang="und-Narb">
-        <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
-    </family>
-    <family lang="und-Perm">
-        <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
-    </family>
-    <family lang="und-Hmng">
-        <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
-    </family>
-    <family lang="und-Palm">
-        <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
-    </family>
-    <family lang="und-Pauc">
-        <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
-    </family>
-    <family lang="und-Shrd">
-        <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
-    </family>
-    <family lang="und-Sora">
-        <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
-    </family>
-    <family lang="und-Gong">
-        <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Rohg">
-        <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font>
-    </family>
-    <family lang="und-Khoj">
-        <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font>
-    </family>
-    <family lang="und-Gonm">
-        <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font>
-    </family>
-    <family lang="und-Wcho">
-        <font weight="400" style="normal">NotoSansWancho-Regular.otf</font>
-    </family>
-    <family lang="und-Wara">
-        <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
-    </family>
-    <family lang="und-Gran">
-        <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
-    </family>
-    <family lang="und-Modi">
-        <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
-    </family>
-    <family lang="und-Dogr">
-        <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
-    </family>
-    <family lang="und-Medf">
-        <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
-            NotoSansMedefaidrin-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
-            NotoSansMedefaidrin-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
-            NotoSansMedefaidrin-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
-            NotoSansMedefaidrin-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Soyo">
-        <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular">
-            NotoSansSoyombo-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular">
-            NotoSansSoyombo-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular">
-            NotoSansSoyombo-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular">
-            NotoSansSoyombo-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Takr">
-        <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular">
-            NotoSansTakri-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular">
-            NotoSansTakri-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular">
-            NotoSansTakri-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular">
-            NotoSansTakri-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Hmnp">
-        <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
-            NotoSerifNyiakengPuachueHmong-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-    <family lang="und-Yezi">
-        <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular">
-            NotoSerifYezidi-VF.ttf
-            <axis tag="wght" stylevalue="400"/>
-        </font>
-        <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular">
-            NotoSerifYezidi-VF.ttf
-            <axis tag="wght" stylevalue="500"/>
-        </font>
-        <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular">
-            NotoSerifYezidi-VF.ttf
-            <axis tag="wght" stylevalue="600"/>
-        </font>
-        <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular">
-            NotoSerifYezidi-VF.ttf
-            <axis tag="wght" stylevalue="700"/>
-        </font>
-    </family>
-</familyset>
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt
new file mode 100644
index 0000000..2d6df43
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubject.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import com.android.internal.logging.testing.UiEventLoggerFake.FakeUiEvent
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Truth
+
+/** Subclass of [Subject] to simplify verifying [FakeUiEvent] data */
+class UiEventSubject(metadata: FailureMetadata, private val actual: FakeUiEvent) :
+    Subject(metadata, actual) {
+
+    /** Check that [FakeUiEvent] contains the expected data from the [bubble] passed id */
+    fun hasBubbleInfo(bubble: Bubble) {
+        check("uid").that(actual.uid).isEqualTo(bubble.appUid)
+        check("packageName").that(actual.packageName).isEqualTo(bubble.packageName)
+        check("instanceId").that(actual.instanceId).isEqualTo(bubble.instanceId)
+    }
+
+    companion object {
+        @JvmStatic
+        fun assertThat(event: FakeUiEvent): UiEventSubject =
+            Truth.assertAbout(Factory(::UiEventSubject)).that(event)
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt
new file mode 100644
index 0000000..af238d0
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/UiEventSubjectTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.logging.testing.UiEventLoggerFake.FakeUiEvent
+import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
+import com.google.common.truth.ExpectFailure.assertThat
+import com.google.common.truth.ExpectFailure.expectFailure
+import com.google.common.truth.Subject
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.whenever
+
+/** Test for [UiEventSubject] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UiEventSubjectTest {
+
+    private val uiEventSubjectFactory =
+        Subject.Factory<UiEventSubject, FakeUiEvent> { metadata, actual ->
+            UiEventSubject(metadata, actual)
+        }
+
+    private lateinit var uiEventLoggerFake: UiEventLoggerFake
+
+    @Before
+    fun setUp() {
+        uiEventLoggerFake = UiEventLoggerFake()
+    }
+
+    @Test
+    fun test_bubbleLogEvent_hasBubbleInfo() {
+        val bubble =
+            createBubble(
+                appUid = 1,
+                packageName = "test",
+                instanceId = InstanceId.fakeInstanceId(2),
+            )
+        BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)
+        val uiEvent = uiEventLoggerFake.logs.first()
+
+        // Check that fields match the expected values
+        assertThat(uiEvent.uid).isEqualTo(1)
+        assertThat(uiEvent.packageName).isEqualTo("test")
+        assertThat(uiEvent.instanceId.id).isEqualTo(2)
+
+        // Check that hasBubbleInfo condition passes
+        assertThat(uiEvent).hasBubbleInfo(bubble)
+    }
+
+    @Test
+    fun test_bubbleLogEvent_uidMismatch() {
+        val bubble =
+            createBubble(
+                appUid = 1,
+                packageName = "test",
+                instanceId = InstanceId.fakeInstanceId(2),
+            )
+        BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)
+        val uiEvent = uiEventLoggerFake.logs.first()
+
+        // Change uid to have a mismatch
+        val otherBubble = bubble.copy(appUid = 99)
+
+        val failure = expectFailure { test ->
+            test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble)
+        }
+        assertThat(failure).factValue("value of").isEqualTo("uiEvent.uid")
+    }
+
+    @Test
+    fun test_bubbleLogEvent_packageNameMismatch() {
+        val bubble =
+            createBubble(
+                appUid = 1,
+                packageName = "test",
+                instanceId = InstanceId.fakeInstanceId(2),
+            )
+        BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)
+        val uiEvent = uiEventLoggerFake.logs.first()
+
+        // Change package name to have a mismatch
+        val otherBubble = bubble.copy(packageName = "somethingelse")
+
+        val failure = expectFailure { test ->
+            test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble)
+        }
+        assertThat(failure).factValue("value of").isEqualTo("uiEvent.packageName")
+    }
+
+    @Test
+    fun test_bubbleLogEvent_instanceIdMismatch() {
+        val bubble =
+            createBubble(
+                appUid = 1,
+                packageName = "test",
+                instanceId = InstanceId.fakeInstanceId(2),
+            )
+        BubbleLogger(uiEventLoggerFake).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)
+        val uiEvent = uiEventLoggerFake.logs.first()
+
+        // Change instance id to have a mismatch
+        val otherBubble = bubble.copy(instanceId = InstanceId.fakeInstanceId(99))
+
+        val failure = expectFailure { test ->
+            test.about(uiEventSubjectFactory).that(uiEvent).hasBubbleInfo(otherBubble)
+        }
+        assertThat(failure).factValue("value of").isEqualTo("uiEvent.instanceId")
+    }
+
+    private fun createBubble(appUid: Int, packageName: String, instanceId: InstanceId): Bubble {
+        return mock(Bubble::class.java).apply {
+            whenever(getAppUid()).thenReturn(appUid)
+            whenever(getPackageName()).thenReturn(packageName)
+            whenever(getInstanceId()).thenReturn(instanceId)
+        }
+    }
+
+    private fun Bubble.copy(
+        appUid: Int = this.appUid,
+        packageName: String = this.packageName,
+        instanceId: InstanceId = this.instanceId,
+    ): Bubble {
+        return createBubble(appUid, packageName, instanceId)
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index fa9d2ba..08d647d 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -40,6 +40,7 @@
 import com.android.wm.shell.bubbles.BubbleTaskViewFactory
 import com.android.wm.shell.bubbles.DeviceConfig
 import com.android.wm.shell.bubbles.RegionSamplingProvider
+import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 import com.android.wm.shell.shared.handles.RegionSamplingHelper
@@ -47,16 +48,14 @@
 import com.android.wm.shell.taskview.TaskViewTaskController
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import java.util.Collections
+import java.util.concurrent.Executor
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
-import java.util.Collections
-import java.util.concurrent.Executor
 
 /** Tests for [BubbleBarExpandedViewTest] */
 @SmallTest
@@ -82,7 +81,7 @@
     private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
     private var regionSamplingProvider: TestRegionSamplingProvider? = null
 
-    private val bubbleLogger = spy(BubbleLogger(UiEventLoggerFake()))
+    private val uiEventLoggerFake = UiEventLoggerFake()
 
     @Before
     fun setUp() {
@@ -116,7 +115,7 @@
         bubbleExpandedView.initialize(
             expandedViewManager,
             positioner,
-            bubbleLogger,
+            BubbleLogger(uiEventLoggerFake),
             false /* isOverflow */,
             bubbleTaskView,
             mainExecutor,
@@ -223,7 +222,10 @@
             bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG)
         assertThat(dismissMenuItem).isNotNull()
         getInstrumentation().runOnMainSync { dismissMenuItem.performClick() }
-        verify(bubbleLogger).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU)
+        assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+        assertThat(uiEventLoggerFake.logs[0].eventId)
+            .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU.id)
+        assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
     }
 
     private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index eeb7ac8..443e417 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -522,6 +522,7 @@
                 "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}"
             )
             pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
+            pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 1d17cd6..8dd7589 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -409,7 +409,7 @@
         interactionJankMonitor.begin(taskSurface, context, handler,
             CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         dragToDesktopTransitionHandler.startDragToDesktopTransition(
-            taskInfo,
+            taskInfo.taskId,
             dragToDesktopValueAnimator
         )
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 34c2f1e..d7d5519 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -109,8 +109,8 @@
      * after one of the "end" or "cancel" transitions is merged into this transition.
      */
     fun startDragToDesktopTransition(
-        taskInfo: RunningTaskInfo,
-        dragToDesktopAnimator: MoveToDesktopAnimator
+        taskId: Int,
+        dragToDesktopAnimator: MoveToDesktopAnimator,
     ) {
         if (inProgress) {
             ProtoLog.v(
@@ -137,26 +137,23 @@
             )
         val wct = WindowContainerTransaction()
         wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
-        // The home launch done above will result in an attempt to move the task to pip if
-        // applicable, resulting in a broken state. Prevent that here.
-        wct.setDoNotPip(taskInfo.token)
         val startTransitionToken =
             transitions.startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
 
         transitionState =
-            if (isSplitTask(taskInfo.taskId)) {
+            if (isSplitTask(taskId)) {
                 val otherTask =
-                    getOtherSplitTask(taskInfo.taskId)
+                    getOtherSplitTask(taskId)
                         ?: throw IllegalStateException("Expected split task to have a counterpart.")
                 TransitionState.FromSplit(
-                    draggedTaskId = taskInfo.taskId,
+                    draggedTaskId = taskId,
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken,
                     otherSplitTask = otherTask
                 )
             } else {
                 TransitionState.FromFullscreen(
-                    draggedTaskId = taskInfo.taskId,
+                    draggedTaskId = taskId,
                     dragAnimator = dragToDesktopAnimator,
                     startTransitionToken = startTransitionToken
                 )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 3e76403..9239fb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1908,60 +1908,6 @@
         }
     }
 
-    /** Callback when split roots have child or haven't under it.
-     * NOTICE: This only be called on legacy transition. */
-    @Override
-    public void onStageHasChildrenChanged(StageTaskListener stageListener) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
-                stageListener == mMainStage);
-        final boolean hasChildren = stageListener.mHasChildren;
-        final boolean isSideStage = stageListener == mSideStage;
-        if (!hasChildren && !mIsExiting && isSplitActive()) {
-            if (isSideStage && mMainStage.mVisible) {
-                // Exit to main stage if side stage no longer has children.
-                mSplitLayout.flingDividerToDismiss(
-                        mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT,
-                        EXIT_REASON_APP_FINISHED);
-            } else if (!isSideStage && mSideStage.mVisible) {
-                // Exit to side stage if main stage no longer has children.
-                mSplitLayout.flingDividerToDismiss(
-                        mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
-                        EXIT_REASON_APP_FINISHED);
-            } else if (!isSplitScreenVisible() && mSplitRequest == null) {
-                // Dismiss split screen in the background once any sides of the split become empty.
-                exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
-            }
-        } else if (isSideStage && hasChildren && !isSplitActive()) {
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            prepareEnterSplitScreen(wct);
-
-            mSyncQueue.queue(wct);
-            mSyncQueue.runInSync(t -> {
-                if (mIsDropEntering) {
-                    updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-                    mIsDropEntering = false;
-                    mSkipEvictingMainStageChildren = false;
-                } else {
-                    mShowDecorImmediately = true;
-                    mSplitLayout.flingDividerToCenter(/*finishCallback*/ null);
-                }
-            });
-        }
-        if (mMainStage.mHasChildren && mSideStage.mHasChildren) {
-            mShouldUpdateRecents = true;
-            clearRequestIfPresented();
-            updateRecentTasksSplitPair();
-
-            if (!mLogger.hasStartedSession() && !mLogger.hasValidEnterSessionId()) {
-                mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
-            }
-            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
-                    getMainStagePosition(), mMainStage.getTopChildTaskUid(),
-                    getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLeftRightSplit());
-        }
-    }
-
     @Override
     public void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener,
             ActivityManager.RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 6313231..b33f3e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -75,8 +75,6 @@
     public interface StageListenerCallbacks {
         void onRootTaskAppeared();
 
-        void onStageHasChildrenChanged(StageTaskListener stageTaskListener);
-
         void onStageVisibilityChanged(StageTaskListener stageTaskListener);
 
         void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present,
@@ -207,7 +205,10 @@
                     mIconProvider);
             mHasRootTask = true;
             mCallbacks.onRootTaskAppeared();
-            sendStatusChanged();
+            if (mVisible != mRootTaskInfo.isVisible) {
+                mVisible = mRootTaskInfo.isVisible;
+                mCallbacks.onStageVisibilityChanged(this);
+            }
             mSyncQueue.runInSync(t -> mDimLayer =
                     SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer"));
         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
@@ -498,22 +499,6 @@
         return true;
     }
 
-    private void sendStatusChanged() {
-        boolean hasChildren = mChildrenTaskInfo.size() > 0;
-        boolean visible = mRootTaskInfo.isVisible;
-        if (!mHasRootTask) return;
-
-        if (mHasChildren != hasChildren) {
-            mHasChildren = hasChildren;
-            mCallbacks.onStageHasChildrenChanged(this);
-        }
-
-        if (mVisible != visible) {
-            mVisible = visible;
-            mCallbacks.onStageVisibilityChanged(this);
-        }
-    }
-
     @Override
     @CallSuper
     public void dump(@NonNull PrintWriter pw, String prefix) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 0bd3e08..79e16fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -607,7 +607,7 @@
                 )
             )
             .thenReturn(token)
-        handler.startDragToDesktopTransition(task, dragAnimator)
+        handler.startDragToDesktopTransition(task.taskId, dragAnimator)
         return token
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 189684d..7144a1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -114,7 +114,6 @@
     public void testRootTaskAppeared() {
         assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId);
         verify(mCallbacks).onRootTaskAppeared();
-        verify(mCallbacks, never()).onStageHasChildrenChanged(mStageTaskListener);
         verify(mCallbacks, never()).onStageVisibilityChanged(mStageTaskListener);
     }
 
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index afa0a32..65e83b9 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -323,10 +323,15 @@
 
         int status;
         try {
-            status = mSoundTriggerSession.startRecognition(mSoundModel,
-                    mRecognitionCallback, new RecognitionConfig(captureTriggerAudio,
-                            allowMultipleTriggers, null, null, audioCapabilities),
-                    runInBatterySaver);
+            status = mSoundTriggerSession.startRecognition(
+                mSoundModel,
+                mRecognitionCallback,
+                new RecognitionConfig.Builder()
+                    .setCaptureRequested(captureTriggerAudio)
+                    .setAllowMultipleTriggers(allowMultipleTriggers)
+                    .setAudioCapabilities(audioCapabilities)
+                    .build(),
+                runInBatterySaver);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
index 4f315a2..63661f6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java
@@ -74,7 +74,23 @@
             new AudioDeviceCallback() {
                 @Override
                 public void onAudioDevicesAdded(@NonNull AudioDeviceInfo[] addedDevices) {
-                    applyDefaultSelectedTypeToAllPresets();
+                    // Activate the last hot plugged valid input device, to match the output device
+                    // behavior.
+                    @AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType;
+                    for (AudioDeviceInfo info : addedDevices) {
+                        if (InputMediaDevice.isSupportedInputDevice(info.getType())) {
+                            deviceTypeToActivate = info.getType();
+                        }
+                    }
+
+                    // Only activate if we find a different valid input device. e.g. if none of the
+                    // addedDevices is supported input device, we don't need to activate anything.
+                    if (mSelectedInputDeviceType != deviceTypeToActivate) {
+                        mSelectedInputDeviceType = deviceTypeToActivate;
+                        AudioDeviceAttributes deviceAttributes =
+                                createInputDeviceAttributes(mSelectedInputDeviceType);
+                        setPreferredDeviceForAllPresets(deviceAttributes);
+                    }
                 }
 
                 @Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
index 782cee2..d808a25 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -138,6 +139,18 @@
                 /* address= */ "");
     }
 
+    private AudioDeviceAttributes getUsbHeadsetDeviceAttributes() {
+        return new AudioDeviceAttributes(
+                AudioDeviceAttributes.ROLE_INPUT,
+                AudioDeviceInfo.TYPE_USB_HEADSET,
+                /* address= */ "");
+    }
+
+    private AudioDeviceAttributes getHdmiDeviceAttributes() {
+        return new AudioDeviceAttributes(
+                AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_HDMI, /* address= */ "");
+    }
+
     private void onPreferredDevicesForCapturePresetChanged(InputRouteManager inputRouteManager) {
         final List<AudioDeviceAttributes> audioDeviceAttributesList =
                 new ArrayList<AudioDeviceAttributes>();
@@ -303,21 +316,47 @@
     }
 
     @Test
-    public void onAudioDevicesAdded_shouldApplyDefaultSelectedDeviceToAllPresets() {
+    public void onAudioDevicesAdded_shouldActivateAddedDevice() {
         final AudioManager audioManager = mock(AudioManager.class);
-        AudioDeviceAttributes wiredHeadsetDeviceAttributes = getWiredHeadsetDeviceAttributes();
-        when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES))
-                .thenReturn(Collections.singletonList(wiredHeadsetDeviceAttributes));
-
         InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
         AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()};
         inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
 
-        // Called twice, one after initiation, the other after onAudioDevicesAdded call.
-        verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES);
+        // The only added wired headset will be activated.
         for (@MediaRecorder.Source int preset : PRESETS) {
-            verify(audioManager, atLeast(2))
-                    .setPreferredDeviceForCapturePreset(preset, wiredHeadsetDeviceAttributes);
+            verify(audioManager, atLeast(1))
+                    .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+        }
+    }
+
+    @Test
+    public void onAudioDevicesAdded_shouldActivateLastAddedDevice() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+        AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockUsbHeadsetInfo()};
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+        // When adding multiple valid input devices, the last added device (usb headset in this
+        // case) will be activated.
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, never())
+                    .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes());
+            verify(audioManager, atLeast(1))
+                    .setPreferredDeviceForCapturePreset(preset, getUsbHeadsetDeviceAttributes());
+        }
+    }
+
+    @Test
+    public void onAudioDevicesAdded_doNotActivateInvalidAddedDevice() {
+        final AudioManager audioManager = mock(AudioManager.class);
+        InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager);
+        AudioDeviceInfo[] devices = {mockHdmiInfo()};
+        inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices);
+
+        // Do not activate since HDMI is not a valid input device.
+        for (@MediaRecorder.Source int preset : PRESETS) {
+            verify(audioManager, never())
+                    .setPreferredDeviceForCapturePreset(preset, getHdmiDeviceAttributes());
         }
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 93754fd..77f1979 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -314,16 +314,6 @@
         }
 
     @Test
-    fun isActiveDreamLockscreenHosted() =
-        testScope.runTest {
-            underTest.setIsActiveDreamLockscreenHosted(true)
-            assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(true)
-
-            underTest.setIsActiveDreamLockscreenHosted(false)
-            assertThat(underTest.isActiveDreamLockscreenHosted.value).isEqualTo(false)
-        }
-
-    @Test
     fun isUdfpsSupported() =
         testScope.runTest {
             whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
@@ -336,26 +326,14 @@
     @Test
     fun isKeyguardGoingAway() =
         testScope.runTest {
-            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
-            var latest: Boolean? = null
-            val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
-            runCurrent()
-            assertThat(latest).isFalse()
+            val isGoingAway by collectLastValue(underTest.isKeyguardGoingAway)
+            assertThat(isGoingAway).isFalse()
 
-            val captor = argumentCaptor<KeyguardStateController.Callback>()
-            verify(keyguardStateController, atLeastOnce()).addCallback(captor.capture())
+            underTest.isKeyguardGoingAway.value = true
+            assertThat(isGoingAway).isTrue()
 
-            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
-            captor.value.onKeyguardGoingAwayChanged()
-            runCurrent()
-            assertThat(latest).isTrue()
-
-            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
-            captor.value.onKeyguardGoingAwayChanged()
-            runCurrent()
-            assertThat(latest).isFalse()
-
-            job.cancel()
+            underTest.isKeyguardGoingAway.value = false
+            assertThat(isGoingAway).isFalse()
         }
 
     @Test
@@ -423,17 +401,17 @@
             runCurrent()
             listener.onDozeTransition(
                 DozeMachine.State.DOZE_REQUEST_PULSE,
-                DozeMachine.State.DOZE_PULSING
+                DozeMachine.State.DOZE_PULSING,
             )
             runCurrent()
             listener.onDozeTransition(
                 DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
-                DozeMachine.State.DOZE_PULSE_DONE
+                DozeMachine.State.DOZE_PULSE_DONE,
             )
             runCurrent()
             listener.onDozeTransition(
                 DozeMachine.State.DOZE_AOD_PAUSING,
-                DozeMachine.State.DOZE_AOD_PAUSED
+                DozeMachine.State.DOZE_AOD_PAUSED,
             )
             runCurrent()
 
@@ -443,22 +421,22 @@
                         // Initial value will be UNINITIALIZED
                         DozeTransitionModel(
                             DozeStateModel.UNINITIALIZED,
-                            DozeStateModel.UNINITIALIZED
+                            DozeStateModel.UNINITIALIZED,
                         ),
                         DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE),
                         DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD),
                         DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH),
                         DozeTransitionModel(
                             DozeStateModel.DOZE_REQUEST_PULSE,
-                            DozeStateModel.DOZE_PULSING
+                            DozeStateModel.DOZE_PULSING,
                         ),
                         DozeTransitionModel(
                             DozeStateModel.DOZE_SUSPEND_TRIGGERS,
-                            DozeStateModel.DOZE_PULSE_DONE
+                            DozeStateModel.DOZE_PULSE_DONE,
                         ),
                         DozeTransitionModel(
                             DozeStateModel.DOZE_AOD_PAUSING,
-                            DozeStateModel.DOZE_AOD_PAUSED
+                            DozeStateModel.DOZE_AOD_PAUSED,
                         ),
                     )
                 )
@@ -510,12 +488,7 @@
             // consuming it should handle that properly.
             assertThat(values).isEqualTo(listOf(null))
 
-            listOf(
-                    Point(500, 500),
-                    Point(0, 0),
-                    null,
-                    Point(250, 250),
-                )
+            listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
                 .onEach {
                     facePropertyRepository.setSensorLocation(it)
                     runCurrent()
@@ -551,7 +524,7 @@
                 .onEach { biometricSourceType ->
                     underTest.setBiometricUnlockState(
                         BiometricUnlockMode.NONE,
-                        BiometricUnlockSource.Companion.fromBiometricSourceType(biometricSourceType)
+                        BiometricUnlockSource.Companion.fromBiometricSourceType(biometricSourceType),
                     )
                     runCurrent()
                 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 3b6e5d0..df44425 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -90,6 +90,7 @@
 
     private lateinit var powerInteractor: PowerInteractor
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
 
     companion object {
         @JvmStatic
@@ -106,6 +107,7 @@
     @Before
     fun setup() {
         powerInteractor = kosmos.powerInteractor
+        keyguardInteractor = kosmos.keyguardInteractor
         transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
         underTest = kosmos.fromDozingTransitionInteractor
 
@@ -137,6 +139,20 @@
         }
 
     @Test
+    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testTransitionToGone_onWakeup_whenGoingAway() =
+        testScope.runTest {
+            keyguardInteractor.setIsKeyguardGoingAway(true)
+            runCurrent()
+
+            powerInteractor.setAwakeForTest()
+            advanceTimeBy(60L)
+
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GONE)
+        }
+
+    @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun testTransitionToLockscreen_onWake_canDream_glanceableHubAvailable() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
index 4862104..e60d971 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
@@ -173,17 +173,6 @@
 
     @Test
     @EnableSceneContainer
-    fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_isActiveDreamLockscreenHosted_true() =
-        testScope.runTest {
-            val value by collectLastValue(underTest.clockShouldBeCentered)
-            kosmos.shadeRepository.setShadeLayoutWide(true)
-            kosmos.activeNotificationListRepository.setActiveNotifs(1)
-            kosmos.keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            assertThat(value).isTrue()
-        }
-
-    @Test
-    @EnableSceneContainer
     fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_hasPulsingNotifications_false() =
         testScope.runTest {
             val value by collectLastValue(underTest.clockShouldBeCentered)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
deleted file mode 100644
index 2ac0ed0..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shade
-
-import android.os.PowerManager
-import android.view.MotionEvent
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidJUnit4::class)
-class LockscreenHostedDreamGestureListenerTest : SysuiTestCase() {
-    @Mock private lateinit var falsingManager: FalsingManager
-    @Mock private lateinit var falsingCollector: FalsingCollector
-    @Mock private lateinit var statusBarStateController: StatusBarStateController
-    @Mock private lateinit var shadeLogger: ShadeLogger
-    @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
-
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
-    private lateinit var powerRepository: FakePowerRepository
-    private lateinit var keyguardRepository: FakeKeyguardRepository
-    private lateinit var underTest: LockscreenHostedDreamGestureListener
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        powerRepository = FakePowerRepository()
-        keyguardRepository = FakeKeyguardRepository()
-
-        underTest =
-            LockscreenHostedDreamGestureListener(
-                falsingManager,
-                PowerInteractorFactory.create(
-                        repository = powerRepository,
-                        statusBarStateController = statusBarStateController,
-                    )
-                    .powerInteractor,
-                statusBarStateController,
-                primaryBouncerInteractor,
-                keyguardRepository,
-                shadeLogger,
-            )
-        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        whenever(primaryBouncerInteractor.isBouncerShowing()).thenReturn(false)
-    }
-
-    @Test
-    fun testGestureDetector_onSingleTap_whileDreaming() =
-        testScope.runTest {
-            // GIVEN device dreaming and the dream is hosted in lockscreen
-            whenever(statusBarStateController.isDreaming).thenReturn(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            testScope.runCurrent()
-
-            // GIVEN the falsing manager does NOT think the tap is a false tap
-            whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false)
-
-            // WHEN there's a tap
-            underTest.onSingleTapUp(upEv)
-
-            // THEN wake up device if dreaming
-            Truth.assertThat(powerRepository.lastWakeWhy).isNotNull()
-            Truth.assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP)
-        }
-
-    @Test
-    fun testGestureDetector_onSingleTap_notOnKeyguard() =
-        testScope.runTest {
-            // GIVEN device dreaming and the dream is hosted in lockscreen
-            whenever(statusBarStateController.isDreaming).thenReturn(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            testScope.runCurrent()
-
-            // GIVEN shade is open
-            whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
-
-            // GIVEN the falsing manager does NOT think the tap is a false tap
-            whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false)
-
-            // WHEN there's a tap
-            underTest.onSingleTapUp(upEv)
-
-            // THEN the falsing manager never gets a call
-            verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt())
-        }
-
-    @Test
-    fun testGestureDetector_onSingleTap_bouncerShown() =
-        testScope.runTest {
-            // GIVEN device dreaming and the dream is hosted in lockscreen
-            whenever(statusBarStateController.isDreaming).thenReturn(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            testScope.runCurrent()
-
-            // GIVEN bouncer is expanded
-            whenever(primaryBouncerInteractor.isBouncerShowing()).thenReturn(true)
-
-            // GIVEN the falsing manager does NOT think the tap is a false tap
-            whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(false)
-
-            // WHEN there's a tap
-            underTest.onSingleTapUp(upEv)
-
-            // THEN the falsing manager never gets a call
-            verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt())
-        }
-
-    @Test
-    fun testGestureDetector_onSingleTap_falsing() =
-        testScope.runTest {
-            // GIVEN device dreaming and the dream is hosted in lockscreen
-            whenever(statusBarStateController.isDreaming).thenReturn(true)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
-            testScope.runCurrent()
-
-            // GIVEN the falsing manager thinks the tap is a false tap
-            whenever(falsingManager.isFalseTap(ArgumentMatchers.anyInt())).thenReturn(true)
-
-            // WHEN there's a tap
-            underTest.onSingleTapUp(upEv)
-
-            // THEN the device doesn't wake up
-            Truth.assertThat(powerRepository.lastWakeWhy).isNull()
-            Truth.assertThat(powerRepository.lastWakeReason).isNull()
-        }
-
-    @Test
-    fun testSingleTap_notDreaming_noFalsingCheck() =
-        testScope.runTest {
-            // GIVEN device not dreaming with lockscreen hosted dream
-            whenever(statusBarStateController.isDreaming).thenReturn(false)
-            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
-            testScope.runCurrent()
-
-            // WHEN there's a tap
-            underTest.onSingleTapUp(upEv)
-
-            // THEN the falsing manager never gets a call
-            verify(falsingManager, never()).isFalseTap(ArgumentMatchers.anyInt())
-        }
-}
-
-private val upEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index aed9af6..406ca05 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
@@ -67,6 +68,8 @@
     @Mock
     private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
     @Mock
+    private Lazy<KeyguardInteractor> mKeyguardInteractorLazy;
+    @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
     private KeyguardUpdateMonitorLogger mLogger;
@@ -86,6 +89,7 @@
                 mKeyguardUnlockAnimationControllerLazy,
                 mLogger,
                 mDumpManager,
+                mKeyguardInteractorLazy,
                 mFeatureFlags,
                 mSelectedUserInteractor);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 95cd9eb..61832875 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -165,7 +165,7 @@
     val QS_USER_DETAIL_SHORTCUT =
         resourceBooleanFlag(
             R.bool.flag_lockscreen_qs_user_detail_shortcut,
-            "qs_user_detail_shortcut"
+            "qs_user_detail_shortcut",
         )
 
     // TODO(b/254512383): Tracking Bug
@@ -365,11 +365,6 @@
     val ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD =
         releasedFlag("zj_285570694_lockscreen_transition_from_aod")
 
-    // 3000 - dream
-    // TODO(b/285059790) : Tracking Bug
-    @JvmField
-    val LOCKSCREEN_WALLPAPER_DREAM_ENABLED = unreleasedFlag("enable_lockscreen_wallpaper_dream")
-
     // TODO(b/283447257): Tracking bug
     @JvmField
     val BIGPICTURE_NOTIFICATION_LAZY_LOADING =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index fbc76c5..60a306b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2975,7 +2975,7 @@
         @Override
         public void run() {
             Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
-            if (DEBUG) Log.d(TAG, "keyguardGoingAway");
+            Log.d(TAG, "keyguardGoingAwayRunnable");
             mKeyguardViewControllerLazy.get().keyguardGoingAway();
 
             int flags = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 8210174..9e99a87 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -114,7 +114,7 @@
             "away' is isInTransitionToState(GONE), but consider using more specific flows " +
             "whenever possible."
     )
-    val isKeyguardGoingAway: Flow<Boolean>
+    val isKeyguardGoingAway: MutableStateFlow<Boolean>
 
     /**
      * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
@@ -184,9 +184,6 @@
     /** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */
     val isDreamingWithOverlay: Flow<Boolean>
 
-    /** Observable for device dreaming state and the active dream is hosted in lockscreen */
-    val isActiveDreamLockscreenHosted: StateFlow<Boolean>
-
     /**
      * Observable for the amount of doze we are currently in.
      *
@@ -308,8 +305,6 @@
 
     fun setIsDozing(isDozing: Boolean)
 
-    fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean)
-
     fun dozeTimeTick()
 
     fun showDismissibleKeyguard()
@@ -637,9 +632,6 @@
     private val _isQuickSettingsVisible = MutableStateFlow(false)
     override val isQuickSettingsVisible: Flow<Boolean> = _isQuickSettingsVisible.asStateFlow()
 
-    private val _isActiveDreamLockscreenHosted = MutableStateFlow(false)
-    override val isActiveDreamLockscreenHosted = _isActiveDreamLockscreenHosted.asStateFlow()
-
     private val _shortcutAbsoluteTop = MutableStateFlow(0F)
     override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow()
 
@@ -655,10 +647,6 @@
                 override fun onUnlockedChanged() {
                     isKeyguardDismissible.value = keyguardStateController.isUnlocked
                 }
-
-                override fun onKeyguardGoingAwayChanged() {
-                    isKeyguardGoingAway.value = keyguardStateController.isKeyguardGoingAway
-                }
             }
 
         keyguardStateController.addCallback(callback)
@@ -698,10 +686,6 @@
         _isQuickSettingsVisible.value = isVisible
     }
 
-    override fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) {
-        _isActiveDreamLockscreenHosted.value = isLockscreenHosted
-    }
-
     override fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
         _clockShouldBeCentered.value = shouldBeCentered
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 8c7fe5f..0c2d577 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -131,12 +131,13 @@
                 .collect { (_, isCommunalAvailable, isIdleOnCommunal) ->
                     val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
                     val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
+                    val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value
 
                     if (!deviceEntryInteractor.isLockscreenEnabled()) {
                         if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GONE)
                         }
-                    } else if (canDismissLockscreen()) {
+                    } else if (canDismissLockscreen() || isKeyguardGoingAway) {
                         if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GONE)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index 5b7eedd..d18d6dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -105,21 +105,13 @@
             combine(
                 shadeInteractor.isShadeLayoutWide,
                 activeNotificationsInteractor.areAnyNotificationsPresent,
-                keyguardInteractor.isActiveDreamLockscreenHosted,
                 isOnAod,
                 headsUpNotificationInteractor.isHeadsUpOrAnimatingAway,
                 keyguardInteractor.isDozing,
-            ) {
-                isShadeLayoutWide,
-                areAnyNotificationsPresent,
-                isActiveDreamLockscreenHosted,
-                isOnAod,
-                isHeadsUp,
-                isDozing ->
+            ) { isShadeLayoutWide, areAnyNotificationsPresent, isOnAod, isHeadsUp, isDozing ->
                 when {
                     !isShadeLayoutWide -> true
                     !areAnyNotificationsPresent -> true
-                    isActiveDreamLockscreenHosted -> true
                     // Pulsing notification appears on the right. Move clock left to avoid overlap.
                     isHeadsUp && isDozing -> false
                     else -> isOnAod
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 6ecbc61..0d5ad54 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -189,9 +189,6 @@
     /** Whether any dreaming is running, including the doze dream. */
     val isDreamingAny: Flow<Boolean> = repository.isDreaming
 
-    /** Whether the system is dreaming and the active dream is hosted in lockscreen */
-    val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
-
     /** Event for when the camera gesture is detected */
     val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> =
         repository.onCameraLaunchDetected.filter { it.type != CameraLaunchType.IGNORE }
@@ -244,7 +241,7 @@
 
     /** Whether the keyguard is going away. */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE")
-    val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
+    val isKeyguardGoingAway: StateFlow<Boolean> = repository.isKeyguardGoingAway.asStateFlow()
 
     /** Keyguard can be clipped at the top as the shade is dragged */
     val topClippingBounds: Flow<Int?> by lazy {
@@ -477,10 +474,6 @@
         }
     }
 
-    fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) {
-        repository.setIsActiveDreamLockscreenHosted(isLockscreenHosted)
-    }
-
     /** Sets whether quick settings or quick-quick settings is visible. */
     fun setQuickSettingsVisible(isVisible: Boolean) {
         repository.setQuickSettingsVisible(isVisible)
@@ -549,6 +542,10 @@
         repository.setShortcutAbsoluteTop(top)
     }
 
+    fun setIsKeyguardGoingAway(isGoingAway: Boolean) {
+        repository.isKeyguardGoingAway.value = isGoingAway
+    }
+
     companion object {
         private const val TAG = "KeyguardInteractor"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index cf747c8..34173a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.sample
 import dagger.Lazy
 import javax.inject.Inject
@@ -96,16 +97,19 @@
 
     /** Limit the max alpha for the scrim to allow for some transparency */
     val maxAlpha: Flow<Float> =
-        transitionInteractor
-            .isInTransition(
-                edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
-                edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
+        anyOf(
+                transitionInteractor.isInTransition(
+                    edge = Edge.create(Scenes.Gone, KeyguardState.AOD),
+                    edgeWithoutSceneContainer = Edge.create(KeyguardState.GONE, KeyguardState.AOD),
+                ),
+                transitionInteractor.isInTransition(
+                    Edge.create(KeyguardState.OCCLUDED, KeyguardState.AOD)
+                ),
             )
             .flatMapLatest { isInTransition ->
-                // During GONE->AOD transitions, the home screen and wallpaper are still visible
-                // until
-                // WM is told to hide them, which occurs at the end of the animation. Use an opaque
-                // scrim until this transition is complete
+                // During transitions like GONE->AOD, surfaces like the launcher may be visible
+                // until WM is told to hide them, which occurs at the end of the animation. Use an
+                // opaque scrim until this transition is complete.
                 if (isInTransition) {
                     flowOf(1f)
                 } else {
@@ -149,7 +153,6 @@
             KeyguardState.DOZING -> false
             KeyguardState.AOD -> false
             KeyguardState.DREAMING -> true
-            KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
             KeyguardState.GLANCEABLE_HUB -> true
             KeyguardState.ALTERNATE_BOUNCER -> true
             KeyguardState.PRIMARY_BOUNCER -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 080ddfd..f0e79b8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -41,12 +41,6 @@
      */
     DREAMING,
     /**
-     * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
-     * It is a special version of DREAMING state but not DOZING. The active dream will be windowless
-     * and hosted in the lockscreen.
-     */
-    DREAMING_LOCKSCREEN_HOSTED,
-    /**
      * The device has entered a special low-power mode within SystemUI, also called the Always-on
      * Display (AOD). A minimal UI is presented to show critical information. If the device is in
      * low-power mode without a UI, then it is DOZING.
@@ -125,7 +119,6 @@
             OFF,
             DOZING,
             DREAMING,
-            DREAMING_LOCKSCREEN_HOSTED,
             AOD,
             ALTERNATE_BOUNCER,
             OCCLUDED,
@@ -142,7 +135,6 @@
             OFF,
             DOZING,
             DREAMING,
-            DREAMING_LOCKSCREEN_HOSTED,
             AOD,
             ALTERNATE_BOUNCER,
             OCCLUDED,
@@ -166,7 +158,6 @@
                 OFF -> false
                 DOZING -> false
                 DREAMING -> false
-                DREAMING_LOCKSCREEN_HOSTED -> false
                 GLANCEABLE_HUB -> true
                 AOD -> false
                 ALTERNATE_BOUNCER -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
index 32757ce..741cc02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -19,6 +19,7 @@
 import android.animation.ValueAnimator
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators.ALPHA_IN
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -43,14 +44,24 @@
                         }
                     }
                     launch("$TAG#viewModel.maxAlpha") {
-                        viewModel.maxAlpha.collect { alpha ->
+                        var animator: ValueAnimator? = null
+                        viewModel.maxAlpha.collect { (alpha, animate) ->
                             if (alpha != revealScrim.alpha) {
-                                ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply {
-                                    duration = 400
-                                    addUpdateListener { animation ->
-                                        revealScrim.alpha = animation.getAnimatedValue() as Float
-                                    }
-                                    start()
+                                animator?.cancel()
+                                if (!animate) {
+                                    revealScrim.alpha = alpha
+                                } else {
+                                    animator =
+                                        ValueAnimator.ofFloat(revealScrim.alpha, alpha).apply {
+                                            startDelay = 333
+                                            duration = 733
+                                            interpolator = ALPHA_IN
+                                            addUpdateListener { animation ->
+                                                revealScrim.alpha =
+                                                    animation.getAnimatedValue() as Float
+                                            }
+                                            start()
+                                        }
                                 }
                             }
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 68244d8..4c667c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -114,7 +114,6 @@
                             keyguardTransitionInteractor.currentKeyguardState.replayCache.last()
                         ) {
                             KeyguardState.GLANCEABLE_HUB,
-                            KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
                             KeyguardState.GONE,
                             KeyguardState.OCCLUDED,
                             KeyguardState.OFF,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index d3bb4f5..f5e0c81 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -95,11 +95,10 @@
             .shareIn(scope, SharingStarted.WhileSubscribed())
             .onStart { emit(initialAlphaFromKeyguardState(transitionInteractor.getCurrentState())) }
     private val alphaMultiplierFromShadeExpansion: Flow<Float> =
-        combine(
-                showingAlternateBouncer,
+        combine(showingAlternateBouncer, shadeExpansion, qsProgress) {
+                showingAltBouncer,
                 shadeExpansion,
-                qsProgress,
-            ) { showingAltBouncer, shadeExpansion, qsProgress ->
+                qsProgress ->
                 val interpolatedQsProgress = (qsProgress * 2).coerceIn(0f, 1f)
                 if (showingAltBouncer) {
                     1f
@@ -113,13 +112,9 @@
         combine(
             burnInInteractor.deviceEntryIconXOffset,
             burnInInteractor.deviceEntryIconYOffset,
-            burnInInteractor.udfpsProgress
+            burnInInteractor.udfpsProgress,
         ) { fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
-            BurnInOffsets(
-                fullyDozingBurnInX,
-                fullyDozingBurnInY,
-                fullyDozingBurnInProgress,
-            )
+            BurnInOffsets(fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress)
         }
 
     private val dozeAmount: Flow<Float> = transitionInteractor.transitionValue(KeyguardState.AOD)
@@ -129,22 +124,15 @@
             BurnInOffsets(
                 intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x),
                 intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y),
-                floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress)
+                floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress),
             )
         }
 
     val deviceEntryViewAlpha: Flow<Float> =
-        combine(
-                transitionAlpha,
-                alphaMultiplierFromShadeExpansion,
-            ) { alpha, alphaMultiplier ->
+        combine(transitionAlpha, alphaMultiplierFromShadeExpansion) { alpha, alphaMultiplier ->
                 alpha * alphaMultiplier
             }
-            .stateIn(
-                scope = scope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = 0f,
-            )
+            .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initialValue = 0f)
 
     private fun initialAlphaFromKeyguardState(keyguardState: KeyguardState): Float {
         return when (keyguardState) {
@@ -155,11 +143,10 @@
             KeyguardState.GLANCEABLE_HUB,
             KeyguardState.GONE,
             KeyguardState.OCCLUDED,
-            KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
-            KeyguardState.UNDEFINED, -> 0f
+            KeyguardState.UNDEFINED -> 0f
             KeyguardState.AOD,
             KeyguardState.ALTERNATE_BOUNCER,
-            KeyguardState.LOCKSCREEN, -> 1f
+            KeyguardState.LOCKSCREEN -> 1f
         }
     }
 
@@ -171,7 +158,7 @@
                     combine(
                         transitionInteractor.startedKeyguardTransitionStep.sample(
                             shadeInteractor.isAnyFullyExpanded,
-                            ::Pair
+                            ::Pair,
                         ),
                         animatedBurnInOffsets,
                         nonAnimatedBurnInOffsets,
@@ -228,10 +215,9 @@
             }
 
     val iconType: Flow<DeviceEntryIconView.IconType> =
-        combine(
-            deviceEntryUdfpsInteractor.isListeningForUdfps,
-            isUnlocked,
-        ) { isListeningForUdfps, isUnlocked ->
+        combine(deviceEntryUdfpsInteractor.isListeningForUdfps, isUnlocked) {
+            isListeningForUdfps,
+            isUnlocked ->
             if (isListeningForUdfps) {
                 if (isUnlocked) {
                     // Don't show any UI until isUnlocked=false. This covers the case
@@ -250,10 +236,7 @@
     val isVisible: Flow<Boolean> = deviceEntryViewAlpha.map { it > 0f }.distinctUntilChanged()
 
     private val isInteractive: Flow<Boolean> =
-        combine(
-            iconType,
-            isUdfpsSupported,
-        ) { deviceEntryStatus, isUdfps ->
+        combine(iconType, isUdfpsSupported) { deviceEntryStatus, isUdfps ->
             when (deviceEntryStatus) {
                 DeviceEntryIconView.IconType.LOCK -> isUdfps
                 DeviceEntryIconView.IconType.UNLOCK -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
index af6cd16..6d1aefe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -21,6 +21,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
 
 /**
  * Models UI state for the light reveal scrim, which is used during screen on and off animations to
@@ -32,7 +33,16 @@
 constructor(private val interactor: LightRevealScrimInteractor) {
     val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
     val revealAmount: Flow<Float> = interactor.revealAmount
-    val maxAlpha: Flow<Float> = interactor.maxAlpha
+
+    /** Max alpha for the scrim + whether to animate the change */
+    val maxAlpha: Flow<Pair<Float, Boolean>> =
+        interactor.maxAlpha.map { alpha ->
+            Pair(
+                alpha,
+                // Darken immediately if going to be fully opaque
+                if (alpha == 1f) false else true,
+            )
+        }
 
     fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
         interactor.setWallpaperSupportsAmbientMode(supportsAmbientMode)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt
deleted file mode 100644
index 45fc68a..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/LockscreenHostedDreamGestureListener.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shade
-
-import android.os.PowerManager
-import android.view.GestureDetector
-import android.view.MotionEvent
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.statusbar.StatusBarState
-import javax.inject.Inject
-
-/**
- * This gestureListener will wake up by tap when the device is dreaming but not dozing, and the
- * selected screensaver is hosted in lockscreen. Tap is gated by the falsing manager.
- *
- * Touches go through the [NotificationShadeWindowViewController].
- */
-@SysUISingleton
-class LockscreenHostedDreamGestureListener
-@Inject
-constructor(
-    private val falsingManager: FalsingManager,
-    private val powerInteractor: PowerInteractor,
-    private val statusBarStateController: StatusBarStateController,
-    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
-    private val keyguardRepository: KeyguardRepository,
-    private val shadeLogger: ShadeLogger,
-) : GestureDetector.SimpleOnGestureListener() {
-    private val TAG = this::class.simpleName
-
-    override fun onSingleTapUp(e: MotionEvent): Boolean {
-        if (shouldHandleMotionEvent()) {
-            if (!falsingManager.isFalseTap(LOW_PENALTY)) {
-                shadeLogger.d("$TAG#onSingleTapUp tap handled, requesting wakeUpIfDreaming")
-                powerInteractor.wakeUpIfDreaming(
-                    "DREAMING_SINGLE_TAP",
-                    PowerManager.WAKE_REASON_TAP
-                )
-            } else {
-                shadeLogger.d("$TAG#onSingleTapUp false tap ignored")
-            }
-            return true
-        }
-        return false
-    }
-
-    private fun shouldHandleMotionEvent(): Boolean {
-        return keyguardRepository.isActiveDreamLockscreenHosted.value &&
-            statusBarStateController.state == StatusBarState.KEYGUARD &&
-            !primaryBouncerInteractor.isBouncerShowing()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index c256e64..00116aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -71,6 +72,7 @@
             new UpdateMonitorCallback();
     private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
     private final KeyguardUpdateMonitorLogger mLogger;
+    private final Lazy<KeyguardInteractor> mKeyguardInteractorLazy;
 
     private boolean mCanDismissLockScreen;
     private boolean mShowing;
@@ -123,6 +125,7 @@
             Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
             KeyguardUpdateMonitorLogger logger,
             DumpManager dumpManager,
+            Lazy<KeyguardInteractor> keyguardInteractor,
             FeatureFlags featureFlags,
             SelectedUserInteractor userInteractor) {
         mContext = context;
@@ -133,6 +136,7 @@
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
         mUnlockAnimationControllerLazy = keyguardUnlockAnimationController;
         mFeatureFlags = featureFlags;
+        mKeyguardInteractorLazy = keyguardInteractor;
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
 
@@ -354,6 +358,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway",
                     keyguardGoingAway ? 1 : 0);
             mKeyguardGoingAway = keyguardGoingAway;
+            mKeyguardInteractorLazy.get().setIsKeyguardGoingAway(keyguardGoingAway);
             invokeForEachCallback(Callback::onKeyguardGoingAwayChanged);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt
new file mode 100644
index 0000000..73b97f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.data
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.update
+
+/** Stores the state of volume dialog ringer model */
+@SysUISingleton
+class VolumeDialogRingerRepository @Inject constructor() {
+
+    private val mutableRingerModel = MutableStateFlow<VolumeDialogRingerModel?>(null)
+    val ringerModel: Flow<VolumeDialogRingerModel> = mutableRingerModel.filterNotNull()
+
+    fun updateRingerModel(update: (current: VolumeDialogRingerModel?) -> VolumeDialogRingerModel) {
+        mutableRingerModel.update(update)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
new file mode 100644
index 0000000..cf23f1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/shared/model/VolumeDialogRingerModel.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.shared.model
+
+import com.android.settingslib.volume.shared.model.RingerMode
+
+/** Models the state of the volume dialog ringer. */
+data class VolumeDialogRingerModel(
+    val availableModes: List<RingerMode>,
+    /** Current ringer mode internal */
+    val currentRingerMode: RingerMode,
+    /** whether the ringer is allowed given the current ZenMode */
+    val isEnabled: Boolean,
+    /** Whether the current ring stream level is zero or the controller state is muted */
+    val isMuted: Boolean,
+    /** Ring stream level */
+    val level: Int,
+    /** Ring stream maximum level */
+    val levelMax: Int,
+)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index e513e8d..0878649 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -88,9 +88,6 @@
     private val _isDreamingWithOverlay = MutableStateFlow(false)
     override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay
 
-    private val _isActiveDreamLockscreenHosted = MutableStateFlow(false)
-    override val isActiveDreamLockscreenHosted: StateFlow<Boolean> = _isActiveDreamLockscreenHosted
-
     private val _dozeAmount = MutableStateFlow(0f)
     override val linearDozeAmount: Flow<Float> = _dozeAmount
 
@@ -102,8 +99,7 @@
 
     private val _isUdfpsSupported = MutableStateFlow(false)
 
-    private val _isKeyguardGoingAway = MutableStateFlow(false)
-    override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
+    override val isKeyguardGoingAway = MutableStateFlow(false)
 
     private val _biometricUnlockState =
         MutableStateFlow(BiometricUnlockModel(BiometricUnlockMode.NONE, null))
@@ -169,7 +165,7 @@
     }
 
     fun setKeyguardGoingAway(isGoingAway: Boolean) {
-        _isKeyguardGoingAway.value = isGoingAway
+        isKeyguardGoingAway.value = isGoingAway
     }
 
     fun setKeyguardOccluded(isOccluded: Boolean) {
@@ -235,10 +231,6 @@
         _isDreamingWithOverlay.value = isDreaming
     }
 
-    override fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) {
-        _isActiveDreamLockscreenHosted.value = isLockscreenHosted
-    }
-
     fun setDozeAmount(dozeAmount: Float) {
         _dozeAmount.value = dozeAmount
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepositoryKosmos.kt
new file mode 100644
index 0000000..2c51886
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.data
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumeDialogRingerRepository by Kosmos.Fixture { VolumeDialogRingerRepository() }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index d918201..ff2abd2 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -100,6 +100,9 @@
     srcs: [
         "runtime-helper-src/libcore-fake/**/*.java",
     ],
+    libs: [
+        "app-compat-annotations",
+    ],
     static_libs: [
         "ravenwood-runtime-common",
     ],
@@ -121,6 +124,7 @@
     ],
     static_libs: [
         "ravenwood-runtime-common",
+        "androidx.annotation_annotation",
     ],
     libs: [
         "framework-minus-apex.ravenwood",
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
index 3535cb2..870a10a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
@@ -42,6 +42,10 @@
 
     private final RavenwoodConfig mConfig;
 
+    // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by
+    // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry?
+    RavenwoodContext mSystemServerContext;
+
     public RavenwoodConfigState(RavenwoodConfig config) {
         mConfig = config;
     }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 9a145cb..c2806da 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -16,6 +16,8 @@
 
 package android.platform.test.ravenwood;
 
+import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME;
+
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -267,6 +269,13 @@
         config.mInstContext = instContext;
         config.mTargetContext = targetContext;
 
+        final Supplier<Resources> systemResourcesLoader = () -> {
+            return config.mState.loadResources(null);
+        };
+
+        config.mState.mSystemServerContext =
+                new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
+
         // Prepare other fields.
         config.mInstrumentation = new Instrumentation();
         config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
@@ -314,6 +323,9 @@
             ((RavenwoodContext) config.mTargetContext).cleanUp();
             config.mTargetContext = null;
         }
+        if (config.mState.mSystemServerContext != null) {
+            config.mState.mSystemServerContext.cleanUp();
+        }
 
         Looper.getMainLooper().quit();
         Looper.clearMainLooperForTest();
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 3946dd84..f198a08 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -33,6 +33,9 @@
 import java.util.Set;
 
 public class RavenwoodSystemServer {
+
+    static final String ANDROID_PACKAGE_NAME = "android";
+
     /**
      * Set of services that we know how to provide under Ravenwood. We keep this set distinct
      * from {@code com.android.server.SystemServer} to give us the ability to choose either
@@ -67,7 +70,7 @@
 
         sStartedServices = new ArraySet<>();
         sTimings = new TimingsTraceAndSlog();
-        sServiceManager = new SystemServiceManager(config.mInstContext);
+        sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext);
         sServiceManager.setStartInfo(false,
                 SystemClock.elapsedRealtime(),
                 SystemClock.uptimeMillis());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 1f6e11d..37b0abc 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -67,6 +67,7 @@
     String mTargetPackageName;
 
     int mMinSdkLevel;
+    int mTargetSdkLevel;
 
     boolean mProvideMainThread = false;
 
@@ -150,6 +151,14 @@
         }
 
         /**
+         * Configure the target SDK level of the test.
+         */
+        public Builder setTargetSdkLevel(int sdkLevel) {
+            mConfig.mTargetSdkLevel = sdkLevel;
+            return this;
+        }
+
+        /**
          * Configure a "main" thread to be available for the duration of the test, as defined
          * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
          *
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index ced1519..9bc45be 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -146,6 +146,9 @@
         if (root.startsWith("soc.")) return true;
         if (root.startsWith("system.")) return true;
 
+        // For PropertyInvalidatedCache
+        if (root.startsWith("cache_key.")) return true;
+
         switch (key) {
             case "gsm.version.baseband":
             case "no.such.thing":
@@ -170,6 +173,9 @@
 
         if (root.startsWith("debug.")) return true;
 
+        // For PropertyInvalidatedCache
+        if (root.startsWith("cache_key.")) return true;
+
         return mKeyWritable.contains(key);
     }
 
diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java
new file mode 100644
index 0000000..1e3b3fc
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+// [ravenwood] This is an exact copy from StatsD, until we make StatsD available on Ravenwood.
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Build;
+import android.os.SystemClock;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * StatsEvent builds and stores the buffer sent over the statsd socket.
+ * This class defines and encapsulates the socket protocol.
+ *
+ * <p>Usage:</p>
+ * <pre>
+ *      // Pushed event
+ *      StatsEvent statsEvent = StatsEvent.newBuilder()
+ *          .setAtomId(atomId)
+ *          .writeBoolean(false)
+ *          .writeString("annotated String field")
+ *          .addBooleanAnnotation(annotationId, true)
+ *          .usePooledBuffer()
+ *          .build();
+ *      StatsLog.write(statsEvent);
+ *
+ *      // Pulled event
+ *      StatsEvent statsEvent = StatsEvent.newBuilder()
+ *          .setAtomId(atomId)
+ *          .writeBoolean(false)
+ *          .writeString("annotated String field")
+ *          .addBooleanAnnotation(annotationId, true)
+ *          .build();
+ * </pre>
+ * @hide
+ **/
+@SystemApi
+public final class StatsEvent {
+    // Type Ids.
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_INT = 0x00;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_LONG = 0x01;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_STRING = 0x02;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_LIST = 0x03;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_FLOAT = 0x04;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_BOOLEAN = 0x05;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_BYTE_ARRAY = 0x06;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_OBJECT = 0x07;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_KEY_VALUE_PAIRS = 0x08;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_ATTRIBUTION_CHAIN = 0x09;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final byte TYPE_ERRORS = 0x0F;
+
+    // Error flags.
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_NO_TIMESTAMP = 0x1;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_NO_ATOM_ID = 0x2;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_OVERFLOW = 0x4;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_TOO_MANY_KEY_VALUE_PAIRS = 0x10;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x20;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_INVALID_ANNOTATION_ID = 0x40;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x80;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_TOO_MANY_ANNOTATIONS = 0x100;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_TOO_MANY_FIELDS = 0x200;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting public static final int ERROR_LIST_TOO_LONG = 0x4000;
+
+    // Size limits.
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int MAX_ANNOTATION_COUNT = 15;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int MAX_ATTRIBUTION_NODES = 127;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int MAX_NUM_ELEMENTS = 127;
+
+    /**
+     * @hide
+     **/
+    @VisibleForTesting
+    public static final int MAX_KEY_VALUE_PAIRS = 127;
+
+    private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;
+
+    // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
+    // See android_util_StatsLog.cpp.
+    private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+
+    private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB
+
+    private final int mAtomId;
+    private final byte[] mPayload;
+    private Buffer mBuffer;
+    private final int mNumBytes;
+
+    private StatsEvent(final int atomId, @Nullable final Buffer buffer,
+            @NonNull final byte[] payload, final int numBytes) {
+        mAtomId = atomId;
+        mBuffer = buffer;
+        mPayload = payload;
+        mNumBytes = numBytes;
+    }
+
+    /**
+     * Returns a new StatsEvent.Builder for building StatsEvent object.
+     **/
+    @NonNull
+    public static Builder newBuilder() {
+        return new Builder(Buffer.obtain());
+    }
+
+    /**
+     * Get the atom Id of the atom encoded in this StatsEvent object.
+     *
+     * @hide
+     **/
+    public int getAtomId() {
+        return mAtomId;
+    }
+
+    /**
+     * Get the byte array that contains the encoded payload that can be sent to statsd.
+     *
+     * @hide
+     **/
+    @NonNull
+    public byte[] getBytes() {
+        return mPayload;
+    }
+
+    /**
+     * Get the number of bytes used to encode the StatsEvent payload.
+     *
+     * @hide
+     **/
+    public int getNumBytes() {
+        return mNumBytes;
+    }
+
+    /**
+     * Recycle resources used by this StatsEvent object.
+     * No actions should be taken on this StatsEvent after release() is called.
+     *
+     * @hide
+     **/
+    public void release() {
+        if (mBuffer != null) {
+            mBuffer.release();
+            mBuffer = null;
+        }
+    }
+
+    /**
+     * Builder for constructing a StatsEvent object.
+     *
+     * <p>This class defines and encapsulates the socket encoding for the
+     *buffer. The write methods must be called in the same order as the order of
+     *fields in the atom definition.</p>
+     *
+     * <p>setAtomId() must be called immediately after
+     *StatsEvent.newBuilder().</p>
+     *
+     * <p>Example:</p>
+     * <pre>
+     *     // Atom definition.
+     *     message MyAtom {
+     *         optional int32 field1 = 1;
+     *         optional int64 field2 = 2;
+     *         optional string field3 = 3 [(annotation1) = true];
+     *         optional repeated int32 field4 = 4;
+     *     }
+     *
+     *     // StatsEvent construction for pushed event.
+     *     StatsEvent.newBuilder()
+     *     StatsEvent statsEvent = StatsEvent.newBuilder()
+     *         .setAtomId(atomId)
+     *         .writeInt(3) // field1
+     *         .writeLong(8L) // field2
+     *         .writeString("foo") // field 3
+     *         .addBooleanAnnotation(annotation1Id, true)
+     *         .writeIntArray({ 1, 2, 3 });
+     *         .usePooledBuffer()
+     *         .build();
+     *
+     *     // StatsEvent construction for pulled event.
+     *     StatsEvent.newBuilder()
+     *     StatsEvent statsEvent = StatsEvent.newBuilder()
+     *         .setAtomId(atomId)
+     *         .writeInt(3) // field1
+     *         .writeLong(8L) // field2
+     *         .writeString("foo") // field 3
+     *         .addBooleanAnnotation(annotation1Id, true)
+     *         .writeIntArray({ 1, 2, 3 });
+     *         .build();
+     * </pre>
+     **/
+    public static final class Builder {
+        // Fixed positions.
+        private static final int POS_NUM_ELEMENTS = 1;
+        private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES;
+        private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES;
+
+        private final Buffer mBuffer;
+        private long mTimestampNs;
+        private int mAtomId;
+        private byte mCurrentAnnotationCount;
+        private int mPos;
+        private int mPosLastField;
+        private byte mLastType;
+        private int mNumElements;
+        private int mErrorMask;
+        private boolean mUsePooledBuffer = false;
+
+        private Builder(final Buffer buffer) {
+            mBuffer = buffer;
+            mCurrentAnnotationCount = 0;
+            mAtomId = 0;
+            mTimestampNs = SystemClock.elapsedRealtimeNanos();
+            mNumElements = 0;
+
+            // Set mPos to 0 for writing TYPE_OBJECT at 0th position.
+            mPos = 0;
+            writeTypeId(TYPE_OBJECT);
+
+            // Write timestamp.
+            mPos = POS_TIMESTAMP_NS;
+            writeLong(mTimestampNs);
+        }
+
+        /**
+         * Sets the atom id for this StatsEvent.
+         *
+         * This should be called immediately after StatsEvent.newBuilder()
+         * and should only be called once.
+         * Not calling setAtomId will result in ERROR_NO_ATOM_ID.
+         * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION.
+         **/
+        @NonNull
+        public Builder setAtomId(final int atomId) {
+            if (0 == mAtomId) {
+                mAtomId = atomId;
+
+                if (1 == mNumElements) { // Only timestamp is written so far.
+                    writeInt(atomId);
+                } else {
+                    // setAtomId called out of order.
+                    mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION;
+                }
+            }
+
+            return this;
+        }
+
+        /**
+         * Write a boolean field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeBoolean(final boolean value) {
+            // Write boolean typeId byte followed by boolean byte representation.
+            writeTypeId(TYPE_BOOLEAN);
+            mPos += mBuffer.putBoolean(mPos, value);
+            mNumElements++;
+            return this;
+        }
+
+        /**
+         * Write an integer field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeInt(final int value) {
+            // Write integer typeId byte followed by 4-byte representation of value.
+            writeTypeId(TYPE_INT);
+            mPos += mBuffer.putInt(mPos, value);
+            mNumElements++;
+            return this;
+        }
+
+        /**
+         * Write a long field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeLong(final long value) {
+            // Write long typeId byte followed by 8-byte representation of value.
+            writeTypeId(TYPE_LONG);
+            mPos += mBuffer.putLong(mPos, value);
+            mNumElements++;
+            return this;
+        }
+
+        /**
+         * Write a float field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeFloat(final float value) {
+            // Write float typeId byte followed by 4-byte representation of value.
+            writeTypeId(TYPE_FLOAT);
+            mPos += mBuffer.putFloat(mPos, value);
+            mNumElements++;
+            return this;
+        }
+
+        /**
+         * Write a String field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeString(@NonNull final String value) {
+            // Write String typeId byte, followed by 4-byte representation of number of bytes
+            // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value.
+            final byte[] valueBytes = stringToBytes(value);
+            writeByteArray(valueBytes, TYPE_STRING);
+            return this;
+        }
+
+        /**
+         * Write a byte array field to this StatsEvent.
+         **/
+        @NonNull
+        public Builder writeByteArray(@NonNull final byte[] value) {
+            // Write byte array typeId byte, followed by 4-byte representation of number of bytes
+            // in value, followed by the actual byte array.
+            writeByteArray(value, TYPE_BYTE_ARRAY);
+            return this;
+        }
+
+        private void writeByteArray(@NonNull final byte[] value, final byte typeId) {
+            writeTypeId(typeId);
+            final int numBytes = value.length;
+            mPos += mBuffer.putInt(mPos, numBytes);
+            mPos += mBuffer.putByteArray(mPos, value);
+            mNumElements++;
+        }
+
+        /**
+         * Write an attribution chain field to this StatsEvent.
+         *
+         * The sizes of uids and tags must be equal. The AttributionNode at position i is
+         * made up of uids[i] and tags[i].
+         *
+         * @param uids array of uids in the attribution nodes.
+         * @param tags array of tags in the attribution nodes.
+         **/
+        @NonNull
+        public Builder writeAttributionChain(
+                @NonNull final int[] uids, @NonNull final String[] tags) {
+            final byte numUids = (byte) uids.length;
+            final byte numTags = (byte) tags.length;
+
+            if (numUids != numTags) {
+                mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL;
+            } else if (numUids > MAX_ATTRIBUTION_NODES) {
+                mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
+            } else {
+                // Write attribution chain typeId byte, followed by 1-byte representation of
+                // number of attribution nodes, followed by encoding of each attribution node.
+                writeTypeId(TYPE_ATTRIBUTION_CHAIN);
+                mPos += mBuffer.putByte(mPos, numUids);
+                for (int i = 0; i < numUids; i++) {
+                    // Each uid is encoded as 4-byte representation of its int value.
+                    mPos += mBuffer.putInt(mPos, uids[i]);
+
+                    // Each tag is encoded as 4-byte representation of number of bytes in its
+                    // UTF-8 encoding, followed by the actual UTF-8 bytes.
+                    final byte[] tagBytes = stringToBytes(tags[i]);
+                    mPos += mBuffer.putInt(mPos, tagBytes.length);
+                    mPos += mBuffer.putByteArray(mPos, tagBytes);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write KeyValuePairsAtom entries to this StatsEvent.
+         *
+         * @param intMap Integer key-value pairs.
+         * @param longMap Long key-value pairs.
+         * @param stringMap String key-value pairs.
+         * @param floatMap Float key-value pairs.
+         **/
+        @NonNull
+        public Builder writeKeyValuePairs(
+                @Nullable final SparseIntArray intMap,
+                @Nullable final SparseLongArray longMap,
+                @Nullable final SparseArray<String> stringMap,
+                @Nullable final SparseArray<Float> floatMap) {
+            final int intMapSize = null == intMap ? 0 : intMap.size();
+            final int longMapSize = null == longMap ? 0 : longMap.size();
+            final int stringMapSize = null == stringMap ? 0 : stringMap.size();
+            final int floatMapSize = null == floatMap ? 0 : floatMap.size();
+            final int totalCount = intMapSize + longMapSize + stringMapSize + floatMapSize;
+
+            if (totalCount > MAX_KEY_VALUE_PAIRS) {
+                mErrorMask |= ERROR_TOO_MANY_KEY_VALUE_PAIRS;
+            } else {
+                writeTypeId(TYPE_KEY_VALUE_PAIRS);
+                mPos += mBuffer.putByte(mPos, (byte) totalCount);
+
+                for (int i = 0; i < intMapSize; i++) {
+                    final int key = intMap.keyAt(i);
+                    final int value = intMap.valueAt(i);
+                    mPos += mBuffer.putInt(mPos, key);
+                    writeTypeId(TYPE_INT);
+                    mPos += mBuffer.putInt(mPos, value);
+                }
+
+                for (int i = 0; i < longMapSize; i++) {
+                    final int key = longMap.keyAt(i);
+                    final long value = longMap.valueAt(i);
+                    mPos += mBuffer.putInt(mPos, key);
+                    writeTypeId(TYPE_LONG);
+                    mPos += mBuffer.putLong(mPos, value);
+                }
+
+                for (int i = 0; i < stringMapSize; i++) {
+                    final int key = stringMap.keyAt(i);
+                    final String value = stringMap.valueAt(i);
+                    mPos += mBuffer.putInt(mPos, key);
+                    writeTypeId(TYPE_STRING);
+                    final byte[] valueBytes = stringToBytes(value);
+                    mPos += mBuffer.putInt(mPos, valueBytes.length);
+                    mPos += mBuffer.putByteArray(mPos, valueBytes);
+                }
+
+                for (int i = 0; i < floatMapSize; i++) {
+                    final int key = floatMap.keyAt(i);
+                    final float value = floatMap.valueAt(i);
+                    mPos += mBuffer.putInt(mPos, key);
+                    writeTypeId(TYPE_FLOAT);
+                    mPos += mBuffer.putFloat(mPos, value);
+                }
+
+                mNumElements++;
+            }
+
+            return this;
+        }
+
+        /**
+         * Write a repeated boolean field to this StatsEvent.
+         *
+         * The list size must not exceed 127. Otherwise, the array isn't written
+         * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
+         * StatsEvent errors field.
+         *
+         * @param elements array of booleans.
+         **/
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+        @NonNull
+        public Builder writeBooleanArray(@NonNull final boolean[] elements) {
+            final byte numElements = (byte)elements.length;
+
+            if (writeArrayInfo(numElements, TYPE_BOOLEAN)) {
+                // Write encoding of each element.
+                for (int i = 0; i < numElements; i++) {
+                    mPos += mBuffer.putBoolean(mPos, elements[i]);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write a repeated int field to this StatsEvent.
+         *
+         * The list size must not exceed 127. Otherwise, the array isn't written
+         * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
+         * StatsEvent errors field.
+         *
+         * @param elements array of ints.
+         **/
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+        @NonNull
+        public Builder writeIntArray(@NonNull final int[] elements) {
+            final byte numElements = (byte)elements.length;
+
+            if (writeArrayInfo(numElements, TYPE_INT)) {
+              // Write encoding of each element.
+              for (int i = 0; i < numElements; i++) {
+                mPos += mBuffer.putInt(mPos, elements[i]);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write a repeated long field to this StatsEvent.
+         *
+         * The list size must not exceed 127. Otherwise, the array isn't written
+         * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
+         * StatsEvent errors field.
+         *
+         * @param elements array of longs.
+         **/
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+        @NonNull
+        public Builder writeLongArray(@NonNull final long[] elements) {
+            final byte numElements = (byte)elements.length;
+
+            if (writeArrayInfo(numElements, TYPE_LONG)) {
+                // Write encoding of each element.
+                for (int i = 0; i < numElements; i++) {
+                    mPos += mBuffer.putLong(mPos, elements[i]);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write a repeated float field to this StatsEvent.
+         *
+         * The list size must not exceed 127. Otherwise, the array isn't written
+         * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
+         * StatsEvent errors field.
+         *
+         * @param elements array of floats.
+         **/
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+        @NonNull
+        public Builder writeFloatArray(@NonNull final float[] elements) {
+            final byte numElements = (byte)elements.length;
+
+            if (writeArrayInfo(numElements, TYPE_FLOAT)) {
+                // Write encoding of each element.
+                for (int i = 0; i < numElements; i++) {
+                  mPos += mBuffer.putFloat(mPos, elements[i]);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write a repeated string field to this StatsEvent.
+         *
+         * The list size must not exceed 127. Otherwise, the array isn't written
+         * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
+         * StatsEvent errors field.
+         *
+         * @param elements array of strings.
+         **/
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+        @NonNull
+        public Builder writeStringArray(@NonNull final String[] elements) {
+            final byte numElements = (byte)elements.length;
+
+            if (writeArrayInfo(numElements, TYPE_STRING)) {
+                // Write encoding of each element.
+                for (int i = 0; i < numElements; i++) {
+                    final byte[] elementBytes = stringToBytes(elements[i]);
+                    mPos += mBuffer.putInt(mPos, elementBytes.length);
+                    mPos += mBuffer.putByteArray(mPos, elementBytes);
+                }
+                mNumElements++;
+            }
+            return this;
+        }
+
+        /**
+         * Write a boolean annotation for the last field written.
+         **/
+        @NonNull
+        public Builder addBooleanAnnotation(
+                final byte annotationId, final boolean value) {
+            // Ensure there's a field written to annotate.
+            if (mNumElements < 2) {
+                mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+            } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
+                mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
+            } else {
+                mPos += mBuffer.putByte(mPos, annotationId);
+                mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN);
+                mPos += mBuffer.putBoolean(mPos, value);
+                mCurrentAnnotationCount++;
+                writeAnnotationCount();
+            }
+
+            return this;
+        }
+
+        /**
+         * Write an integer annotation for the last field written.
+         **/
+        @NonNull
+        public Builder addIntAnnotation(final byte annotationId, final int value) {
+            if (mNumElements < 2) {
+                mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
+            } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
+                mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
+            } else {
+                mPos += mBuffer.putByte(mPos, annotationId);
+                mPos += mBuffer.putByte(mPos, TYPE_INT);
+                mPos += mBuffer.putInt(mPos, value);
+                mCurrentAnnotationCount++;
+                writeAnnotationCount();
+            }
+
+            return this;
+        }
+
+        /**
+         * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent.
+         * This should be called for pushed events to reduce memory allocations and garbage
+         * collections.
+         **/
+        @NonNull
+        public Builder usePooledBuffer() {
+            mUsePooledBuffer = true;
+            mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
+            return this;
+        }
+
+        /**
+         * Builds a StatsEvent object with values entered in this Builder.
+         **/
+        @NonNull
+        public StatsEvent build() {
+            if (0L == mTimestampNs) {
+                mErrorMask |= ERROR_NO_TIMESTAMP;
+            }
+            if (0 == mAtomId) {
+                mErrorMask |= ERROR_NO_ATOM_ID;
+            }
+            if (mBuffer.hasOverflowed()) {
+                mErrorMask |= ERROR_OVERFLOW;
+            }
+            if (mNumElements > MAX_NUM_ELEMENTS) {
+                mErrorMask |= ERROR_TOO_MANY_FIELDS;
+            }
+
+            if (0 == mErrorMask) {
+                mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
+            } else {
+                // Write atom id and error mask. Overwrite any annotations for atom Id.
+                mPos = POS_ATOM_ID;
+                mPos += mBuffer.putByte(mPos, TYPE_INT);
+                mPos += mBuffer.putInt(mPos, mAtomId);
+                mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
+                mPos += mBuffer.putInt(mPos, mErrorMask);
+                mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
+            }
+
+            final int size = mPos;
+
+            if (mUsePooledBuffer) {
+                return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
+            } else {
+                // Create a copy of the buffer with the required number of bytes.
+                final byte[] payload = new byte[size];
+                System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size);
+
+                // Return Buffer instance to the pool.
+                mBuffer.release();
+
+                return new StatsEvent(mAtomId, null, payload, size);
+            }
+        }
+
+        private void writeTypeId(final byte typeId) {
+            mPosLastField = mPos;
+            mLastType = typeId;
+            mCurrentAnnotationCount = 0;
+            final byte encodedId = (byte) (typeId & 0x0F);
+            mPos += mBuffer.putByte(mPos, encodedId);
+        }
+
+        private void writeAnnotationCount() {
+            // Use first 4 bits for annotation count and last 4 bits for typeId.
+            final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F));
+            mBuffer.putByte(mPosLastField, encodedId);
+        }
+
+        @NonNull
+        private static byte[] stringToBytes(@Nullable final String value) {
+            return (null == value ? "" : value).getBytes(UTF_8);
+        }
+
+        private boolean writeArrayInfo(final byte numElements,
+                                       final byte elementTypeId) {
+            if (numElements > MAX_NUM_ELEMENTS) {
+                mErrorMask |= ERROR_LIST_TOO_LONG;
+                return false;
+            }
+            // Write list typeId byte, 1-byte representation of number of
+            // elements, and element typeId byte.
+            writeTypeId(TYPE_LIST);
+            mPos += mBuffer.putByte(mPos, numElements);
+            // Write element typeId byte without setting mPosLastField and mLastType (i.e. don't use
+            // #writeTypeId)
+            final byte encodedId = (byte) (elementTypeId & 0x0F);
+            mPos += mBuffer.putByte(mPos, encodedId);
+            return true;
+        }
+    }
+
+    private static final class Buffer {
+        private static Object sLock = new Object();
+
+        @GuardedBy("sLock")
+        private static Buffer sPool;
+
+        private byte[] mBytes;
+        private boolean mOverflow = false;
+        private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;
+
+        @NonNull
+        private static Buffer obtain() {
+            final Buffer buffer;
+            synchronized (sLock) {
+                buffer = null == sPool ? new Buffer() : sPool;
+                sPool = null;
+            }
+            buffer.reset();
+            return buffer;
+        }
+
+        private Buffer() {
+            final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(MAX_PUSH_PAYLOAD_SIZE);
+            mBytes = tempBuffer.hasArray() ? tempBuffer.array() : new byte [MAX_PUSH_PAYLOAD_SIZE];
+        }
+
+        @NonNull
+        private byte[] getBytes() {
+            return mBytes;
+        }
+
+        private void release() {
+            // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
+            if (mMaxSize <= MAX_PUSH_PAYLOAD_SIZE) {
+                synchronized (sLock) {
+                    if (null == sPool) {
+                        sPool = this;
+                    }
+                }
+            }
+        }
+
+        private void reset() {
+            mOverflow = false;
+            mMaxSize = MAX_PULL_PAYLOAD_SIZE;
+        }
+
+        private void setMaxSize(final int maxSize, final int numBytesWritten) {
+            mMaxSize = maxSize;
+            if (numBytesWritten > maxSize) {
+                mOverflow = true;
+            }
+        }
+
+        private boolean hasOverflowed() {
+            return mOverflow;
+        }
+
+        /**
+         * Checks for available space in the byte array.
+         *
+         * @param index starting position in the buffer to start the check.
+         * @param numBytes number of bytes to check from index.
+         * @return true if space is available, false otherwise.
+         **/
+        private boolean hasEnoughSpace(final int index, final int numBytes) {
+            final int totalBytesNeeded = index + numBytes;
+
+            if (totalBytesNeeded > mMaxSize) {
+                mOverflow = true;
+                return false;
+            }
+
+            // Expand buffer if needed.
+            if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
+                int newSize = mBytes.length;
+                do {
+                    newSize *= 2;
+                } while (newSize <= totalBytesNeeded);
+
+                if (newSize > mMaxSize) {
+                    newSize = mMaxSize;
+                }
+
+                mBytes = Arrays.copyOf(mBytes, newSize);
+            }
+
+            return true;
+        }
+
+        /**
+         * Writes a byte into the buffer.
+         *
+         * @param index position in the buffer where the byte is written.
+         * @param value the byte to write.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putByte(final int index, final byte value) {
+            if (hasEnoughSpace(index, Byte.BYTES)) {
+                mBytes[index] = (byte) (value);
+                return Byte.BYTES;
+            }
+            return 0;
+        }
+
+        /**
+         * Writes a boolean into the buffer.
+         *
+         * @param index position in the buffer where the boolean is written.
+         * @param value the boolean to write.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putBoolean(final int index, final boolean value) {
+            return putByte(index, (byte) (value ? 1 : 0));
+        }
+
+        /**
+         * Writes an integer into the buffer.
+         *
+         * @param index position in the buffer where the integer is written.
+         * @param value the integer to write.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putInt(final int index, final int value) {
+            if (hasEnoughSpace(index, Integer.BYTES)) {
+                // Use little endian byte order.
+                mBytes[index] = (byte) (value);
+                mBytes[index + 1] = (byte) (value >> 8);
+                mBytes[index + 2] = (byte) (value >> 16);
+                mBytes[index + 3] = (byte) (value >> 24);
+                return Integer.BYTES;
+            }
+            return 0;
+        }
+
+        /**
+         * Writes a long into the buffer.
+         *
+         * @param index position in the buffer where the long is written.
+         * @param value the long to write.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putLong(final int index, final long value) {
+            if (hasEnoughSpace(index, Long.BYTES)) {
+                // Use little endian byte order.
+                mBytes[index] = (byte) (value);
+                mBytes[index + 1] = (byte) (value >> 8);
+                mBytes[index + 2] = (byte) (value >> 16);
+                mBytes[index + 3] = (byte) (value >> 24);
+                mBytes[index + 4] = (byte) (value >> 32);
+                mBytes[index + 5] = (byte) (value >> 40);
+                mBytes[index + 6] = (byte) (value >> 48);
+                mBytes[index + 7] = (byte) (value >> 56);
+                return Long.BYTES;
+            }
+            return 0;
+        }
+
+        /**
+         * Writes a float into the buffer.
+         *
+         * @param index position in the buffer where the float is written.
+         * @param value the float to write.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putFloat(final int index, final float value) {
+            return putInt(index, Float.floatToIntBits(value));
+        }
+
+        /**
+         * Copies a byte array into the buffer.
+         *
+         * @param index position in the buffer where the byte array is copied.
+         * @param value the byte array to copy.
+         * @return number of bytes written to buffer from this write operation.
+         **/
+        private int putByteArray(final int index, @NonNull final byte[] value) {
+            final int numBytes = value.length;
+            if (hasEnoughSpace(index, numBytes)) {
+                System.arraycopy(value, 0, mBytes, index, numBytes);
+                return numBytes;
+            }
+            return 0;
+        }
+    }
+}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java
new file mode 100644
index 0000000..c1c20cf
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/*
+ * [Ravenwood] This is copied from StatsD, with the following changes:
+ * - The static {} is commented out.
+ * - All references to IStatsD and StatsdStatsLog are commented out.
+ * - The native method is no-oped.
+ */
+
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Build;
+//import android.os.IStatsd;
+import android.os.Process;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.RequiresApi;
+
+//import com.android.internal.statsd.StatsdStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * StatsLog provides an API for developers to send events to statsd. The events can be used to
+ * define custom metrics in side statsd.
+ */
+public final class StatsLog {
+
+//    // Load JNI library
+//    static {
+//        System.loadLibrary("stats_jni");
+//    }
+    private static final String TAG = "StatsLog";
+    private static final boolean DEBUG = false;
+    private static final int EXPERIMENT_IDS_FIELD_ID = 1;
+
+    /**
+     * Annotation ID constant for logging UID field.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_IS_UID = 1;
+
+    /**
+     * Annotation ID constant to indicate logged atom event's timestamp should be truncated.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
+
+    /**
+     * Annotation ID constant for a state atom's primary field.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3;
+
+    /**
+     * Annotation ID constant for state atom's state field.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4;
+
+    /**
+     * Annotation ID constant to indicate the first UID in the attribution chain
+     * is a primary field.
+     * Should only be used for attribution chain fields.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
+
+    /**
+     * Annotation ID constant to indicate which state is default for the state atom.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_DEFAULT_STATE = 6;
+
+    /**
+     * Annotation ID constant to signal all states should be reset to the default state.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
+
+    /**
+     * Annotation ID constant to indicate state changes need to account for nesting.
+     * This should only be used with binary state atoms.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    public static final byte ANNOTATION_ID_STATE_NESTED = 8;
+
+    /**
+     * Annotation ID constant to indicate the restriction category of an atom.
+     * This annotation must only be attached to the atom id. This is an int annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains peripheral device info.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains app usage information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains app activity information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains health connect information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains accessibility information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains system search information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains user engagement information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains ambient sensing information.
+     * This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17;
+
+    /**
+     * Annotation ID to indicate that a field of an atom contains demographic classification
+     * information. This is a bool annotation.
+     *
+     * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+     * accept byte as the type for annotation ids to save space.
+     *
+     * @hide
+     */
+    @SuppressLint("NoByteOrShort")
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18;
+
+
+    /** @hide */
+    @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = {
+            RESTRICTION_CATEGORY_DIAGNOSTIC,
+            RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE,
+            RESTRICTION_CATEGORY_AUTHENTICATION,
+            RESTRICTION_CATEGORY_FRAUD_AND_ABUSE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RestrictionCategory {}
+
+    /**
+     * Restriction category for atoms about diagnostics.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1;
+
+    /**
+     * Restriction category for atoms about system intelligence.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2;
+
+    /**
+     * Restriction category for atoms about authentication.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3;
+
+    /**
+     * Restriction category for atoms about fraud and abuse.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4;
+
+    private StatsLog() {
+    }
+
+    /**
+     * Logs a start event.
+     *
+     * @param label developer-chosen label.
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logStart(int label) {
+        int callingUid = Process.myUid();
+//        StatsdStatsLog.write(
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED,
+//                callingUid,
+//                label,
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
+        return true;
+    }
+
+    /**
+     * Logs a stop event.
+     *
+     * @param label developer-chosen label.
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logStop(int label) {
+        int callingUid = Process.myUid();
+//        StatsdStatsLog.write(
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED,
+//                callingUid,
+//                label,
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
+        return true;
+    }
+
+    /**
+     * Logs an event that does not represent a start or stop boundary.
+     *
+     * @param label developer-chosen label.
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logEvent(int label) {
+        int callingUid = Process.myUid();
+//        StatsdStatsLog.write(
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED,
+//                callingUid,
+//                label,
+//                StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
+        return true;
+    }
+
+    /**
+     * Logs an event for binary push for module updates.
+     *
+     * @param trainName        name of install train.
+     * @param trainVersionCode version code of the train.
+     * @param options          optional flags about this install.
+     *                         The last 3 bits indicate options:
+     *                             0x01: FLAG_REQUIRE_STAGING
+     *                             0x02: FLAG_ROLLBACK_ENABLED
+     *                             0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR
+     * @param state            current install state. Defined as State enums in
+     *                         BinaryPushStateChanged atom in
+     *                         frameworks/proto_logging/stats/atoms.proto
+     * @param experimentIds    experiment ids.
+     * @return True if the log request was sent to statsd.
+     */
+    @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
+    public static boolean logBinaryPushStateChanged(@NonNull String trainName,
+            long trainVersionCode, int options, int state,
+            @NonNull long[] experimentIds) {
+        ProtoOutputStream proto = new ProtoOutputStream();
+        for (long id : experimentIds) {
+            proto.write(
+                    ProtoOutputStream.FIELD_TYPE_INT64
+                    | ProtoOutputStream.FIELD_COUNT_REPEATED
+                    | EXPERIMENT_IDS_FIELD_ID,
+                    id);
+        }
+//        StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED,
+//                trainName,
+//                trainVersionCode,
+//                (options & IStatsd.FLAG_REQUIRE_STAGING) > 0,
+//                (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0,
+//                (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0,
+//                state,
+//                proto.getBytes(),
+//                0,
+//                0,
+//                false);
+        return true;
+    }
+
+    /**
+     * Write an event to stats log using the raw format.
+     *
+     * @param buffer    The encoded buffer of data to write.
+     * @param size      The number of bytes from the buffer to write.
+     * @hide
+     * @deprecated Use {@link write(final StatsEvent statsEvent)} instead.
+     *
+     */
+    @Deprecated
+    @SystemApi
+    public static void writeRaw(@NonNull byte[] buffer, int size) {
+        writeImpl(buffer, size, 0);
+    }
+
+    /**
+     * Write an event to stats log using the raw format.
+     *
+     * @param buffer    The encoded buffer of data to write.
+     * @param size      The number of bytes from the buffer to write.
+     * @param atomId    The id of the atom to which the event belongs.
+     */
+//    private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId);
+    private static void writeImpl(@NonNull byte[] buffer, int size, int atomId) {
+        // no-op for now
+    }
+
+    /**
+     * Write an event to stats log using the raw format encapsulated in StatsEvent.
+     * After writing to stats log, release() is called on the StatsEvent object.
+     * No further action should be taken on the StatsEvent object following this call.
+     *
+     * @param statsEvent    The StatsEvent object containing the encoded buffer of data to write.
+     * @hide
+     */
+    @SystemApi
+    public static void write(@NonNull final StatsEvent statsEvent) {
+        writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
+        statsEvent.release();
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java
new file mode 100644
index 0000000..c737684
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/compat/Compatibility.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.compat;
+
+// [Ravenwood] Copied from libcore, with "RAVENWOOD-CHANGE"
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+
+import libcore.api.IntraCoreApi;
+import libcore.util.NonNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Internal APIs for logging and gating compatibility changes.
+ *
+ * @see ChangeId
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@IntraCoreApi
+public final class Compatibility {
+
+    private Compatibility() {}
+
+    /**
+     * Reports that a compatibility change is affecting the current process now.
+     *
+     * <p>Calls to this method from a non-app process are ignored. This allows code implementing
+     * APIs that are used by apps and by other code (e.g. the system server) to report changes
+     * regardless of the process it's running in. When called in a non-app process, this method is
+     * a no-op.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long)}, you do not need to
+     * call this API directly. The change will be reported for you in the case that
+     * {@link #isChangeEnabled(long)} returns {@code true}.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static void reportUnconditionalChange(@ChangeId long changeId) {
+        sCallbacks.onChangeReported(changeId);
+    }
+
+    /**
+     * Query if a given compatibility change is enabled for the current process. This method should
+     * only be called by code running inside a process of the affected app.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>When this method returns {@code true}, it will also report the change as
+     * {@link #reportUnconditionalChange(long)} would, so there is no need to call that method
+     * directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @return {@code true} if the change is enabled for the current app.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static boolean isChangeEnabled(@ChangeId long changeId) {
+        return sCallbacks.isChangeEnabled(changeId);
+    }
+
+    private static final BehaviorChangeDelegate DEFAULT_CALLBACKS = new BehaviorChangeDelegate(){};
+
+    private volatile static BehaviorChangeDelegate sCallbacks = DEFAULT_CALLBACKS;
+
+    /**
+     * Sets the behavior change delegate.
+     *
+     * All changes reported via the {@link Compatibility} class will be forwarded to this class.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void setBehaviorChangeDelegate(BehaviorChangeDelegate callbacks) {
+        sCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    /**
+     * Removes a behavior change delegate previously set via {@link #setBehaviorChangeDelegate}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void clearBehaviorChangeDelegate() {
+        sCallbacks = DEFAULT_CALLBACKS;
+    }
+
+    /**
+     * Return the behavior change delegate
+     *
+     * @hide
+     */
+    // VisibleForTesting
+    @NonNull
+    public static BehaviorChangeDelegate getBehaviorChangeDelegate() {
+        return sCallbacks;
+    }
+
+    /**
+     * For use by tests only. Causes values from {@code overrides} to be returned instead of the
+     * real value.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void setOverrides(ChangeConfig overrides) {
+        // Setting overrides twice in a row does not need to be supported because
+        // this method is only for enabling/disabling changes for the duration of
+        // a single test.
+        // In production, the app is restarted when changes get enabled or disabled,
+        // and the ChangeConfig is then set exactly once on that app process.
+        if (sCallbacks instanceof OverrideCallbacks) {
+            throw new IllegalStateException("setOverrides has already been called!");
+        }
+        sCallbacks = new OverrideCallbacks(sCallbacks, overrides);
+    }
+
+    /**
+     * For use by tests only. Removes overrides set by {@link #setOverrides}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void clearOverrides() {
+        if (!(sCallbacks instanceof OverrideCallbacks)) {
+            throw new IllegalStateException("No overrides set");
+        }
+        sCallbacks = ((OverrideCallbacks) sCallbacks).delegate;
+    }
+
+    /**
+     * Base class for compatibility API implementations. The default implementation logs a warning
+     * to logcat.
+     *
+     * This is provided as a class rather than an interface to allow new methods to be added without
+     * breaking @SystemApi binary compatibility.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public interface BehaviorChangeDelegate {
+        /**
+         * Called when a change is reported via {@link Compatibility#reportUnconditionalChange}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        default void onChangeReported(long changeId) {
+            // Do not use String.format here (b/160912695)
+
+            // RAVENWOOD-CHANGE
+            System.out.println("No Compatibility callbacks set! Reporting change " + changeId);
+        }
+
+        /**
+         * Called when a change is queried via {@link Compatibility#isChangeEnabled}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        default boolean isChangeEnabled(long changeId) {
+            // Do not use String.format here (b/160912695)
+            // TODO(b/289900411): Rate limit this log if it's necessary in the release build.
+            // System.logW("No Compatibility callbacks set! Querying change " + changeId);
+            return true;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @IntraCoreApi
+    public static final class ChangeConfig {
+        private final Set<Long> enabled;
+        private final Set<Long> disabled;
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public ChangeConfig(@NonNull Set<@NonNull Long> enabled, @NonNull Set<@NonNull Long> disabled) {
+            this.enabled = Objects.requireNonNull(enabled);
+            this.disabled = Objects.requireNonNull(disabled);
+            if (enabled.contains(null)) {
+                throw new NullPointerException();
+            }
+            if (disabled.contains(null)) {
+                throw new NullPointerException();
+            }
+            Set<Long> intersection = new HashSet<>(enabled);
+            intersection.retainAll(disabled);
+            if (!intersection.isEmpty()) {
+                throw new IllegalArgumentException("Cannot have changes " + intersection
+                        + " enabled and disabled!");
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isEmpty() {
+            return enabled.isEmpty() && disabled.isEmpty();
+        }
+
+        private static long[] toLongArray(Set<Long> values) {
+            long[] result = new long[values.size()];
+            int idx = 0;
+            for (Long value: values) {
+                result[idx++] = value;
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull long[] getEnabledChangesArray() {
+            return toLongArray(enabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull long[] getDisabledChangesArray() {
+            return toLongArray(disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getEnabledSet() {
+            return Collections.unmodifiableSet(enabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getDisabledSet() {
+            return Collections.unmodifiableSet(disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isForceEnabled(long changeId) {
+            return enabled.contains(changeId);
+        }
+
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @IntraCoreApi
+        public boolean isForceDisabled(long changeId) {
+            return disabled.contains(changeId);
+        }
+
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof ChangeConfig)) {
+                return false;
+            }
+            ChangeConfig that = (ChangeConfig) o;
+            return enabled.equals(that.enabled) &&
+                    disabled.equals(that.disabled);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hash(enabled, disabled);
+        }
+
+
+        /**
+         * @hide
+         */
+        @Override
+        public String toString() {
+            return "ChangeConfig{enabled=" + enabled + ", disabled=" + disabled + '}';
+        }
+    }
+
+    private static class OverrideCallbacks implements BehaviorChangeDelegate {
+        private final BehaviorChangeDelegate delegate;
+        private final ChangeConfig changeConfig;
+
+        private OverrideCallbacks(BehaviorChangeDelegate delegate, ChangeConfig changeConfig) {
+            this.delegate = Objects.requireNonNull(delegate);
+            this.changeConfig = Objects.requireNonNull(changeConfig);
+        }
+        @Override
+        public boolean isChangeEnabled(long changeId) {
+           if (changeConfig.isForceEnabled(changeId)) {
+               return true;
+           }
+           if (changeConfig.isForceDisabled(changeId)) {
+               return false;
+           }
+           return delegate.isChangeEnabled(changeId);
+        }
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java
new file mode 100644
index 0000000..00730ef
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/CorePlatformApi.java
@@ -0,0 +1,69 @@
+
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is part of a contract provided by the "core" set of
+ * libraries to select parts of the Android software stack.
+ *
+ * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre>
+ * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with
+ * metalava's {@code --show-single-annotation} option and so must be applied at the class level and
+ * applied again each member that is to be made part of the API. Members that are not part of the
+ * API do not have to be explicitly hidden.
+ *
+ * @hide
+ */
+@IntraCoreApi
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface CorePlatformApi {
+
+    /** Enumeration of the possible statuses of the API in the core/platform API surface. */
+    @IntraCoreApi
+    enum Status {
+
+        /**
+         * This API is considered stable, and so present in both the stable and legacy version of
+         * the API surface.
+        */
+        @IntraCoreApi
+        STABLE,
+
+        /**
+         * This API is not (yet) considered stable, and so only present in the legacy version of
+         * the API surface.
+         */
+        @IntraCoreApi
+        LEGACY_ONLY
+    }
+
+    /** The status of the API in the core/platform API surface. */
+    @IntraCoreApi
+    Status status() default Status.LEGACY_ONLY;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java
new file mode 100644
index 0000000..f87ff11d
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/Hide.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that an API is hidden by default, in a similar fashion to the
+ * <pre>@hide</pre> javadoc tag.
+ *
+ * <p>Note that, in order for this to work, metalava has to be invoked with
+ * the flag {@code --hide-annotation libcore.api.Hide}.
+ *
+ * <p>This annotation should be used in {@code .annotated.java} stub files which
+ * contain API inclusion information about {@code libcore/ojluni} classes, to
+ * avoid patching the source files with <pre>@hide</pre> javadoc tags. All
+ * build targets which consume these stub files should also apply the above
+ * metalava flag.
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Hide {
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java
new file mode 100644
index 0000000..87cfcff2
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/api/IntraCoreApi.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.api;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is part of a contract within the "core" set of libraries, some of which may
+ * be mmodules.
+ *
+ * <p>This annotation should only appear on either (a) classes that are hidden by <pre>@hide</pre>
+ * javadoc tags or equivalent annotations, or (b) members of such classes. It is for use with
+ * metalava's {@code --show-single-annotation} option and so must be applied at the class level and
+ * applied again each member that is to be made part of the API. Members that are not part of the
+ * API do not have to be explicitly hidden.
+ *
+ * @hide
+ */
+@IntraCoreApi // @IntraCoreApi is itself part of the intra-core API
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface IntraCoreApi {
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
new file mode 100644
index 0000000..db3cd8ed
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a type use can never be null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ * @hide
+ */
+@Documented
+@Retention(SOURCE)
+@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
+@libcore.api.IntraCoreApi
+public @interface NonNull {
+   /**
+    * Min Android API level (inclusive) to which this annotation is applied.
+    */
+   int from() default Integer.MIN_VALUE;
+
+   /**
+    * Max Android API level to which this annotation is applied.
+    */
+   int to() default Integer.MAX_VALUE;
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
new file mode 100644
index 0000000..3371978
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package libcore.util;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a type use can be a null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ * @hide
+ */
+@Documented
+@Retention(SOURCE)
+@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
+@libcore.api.IntraCoreApi
+public @interface Nullable {
+   /**
+    * Min Android API level (inclusive) to which this annotation is applied.
+    */
+   int from() default Integer.MIN_VALUE;
+
+   /**
+    * Max Android API level to which this annotation is applied.
+    */
+   int to() default Integer.MAX_VALUE;
+}
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 3649f0e..b64944e 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -5,6 +5,10 @@
 rename com/.*/nano/   devicenano/
 rename android/.*/nano/   devicenano/
 
+
+# StatsD autogenerated classes. Maybe add a heuristic?
+class com.android.internal.util.FrameworkStatsLog keepclass
+
 # Exported to Mainline modules; cannot use annotations
 class com.android.internal.util.FastXmlSerializer keepclass
 class com.android.internal.util.FileRotator keepclass
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index a77ba62..ce1a292 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -64,6 +64,7 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.Flags;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.ArrayList;
@@ -396,7 +397,7 @@
                             mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
                         sendSpecToAnimation(mCurrentMagnificationSpec, null);
                     }
-                    onMagnificationChangedLocked();
+                    onMagnificationChangedLocked(/* isScaleTransient= */ false);
                 }
                 magnified.recycle();
             }
@@ -474,8 +475,16 @@
             return mIdOfLastServiceToMagnify;
         }
 
+        /**
+         * This is invoked whenever magnification change happens.
+         *
+         * @param isScaleTransient represents that if the scale is being changed and the changed
+         *                         value may be short lived and be updated again soon.
+         *                         Calling the method usually notifies input manager to update the
+         *                         cursor scale, but setting this value {@code true} prevents it.
+         */
         @GuardedBy("mLock")
-        void onMagnificationChangedLocked() {
+        void onMagnificationChangedLocked(boolean isScaleTransient) {
             final float scale = getScale();
             final float centerX = getCenterX();
             final float centerY = getCenterY();
@@ -498,6 +507,10 @@
             } else {
                 hideThumbnail();
             }
+
+            if (!isScaleTransient) {
+                notifyScaleForInput(mDisplayId, scale);
+            }
         }
 
         @GuardedBy("mLock")
@@ -611,8 +624,9 @@
          * Directly Zooms out the scale to 1f with animating the transition. This method is
          * triggered only by service automatically, such as when user context changed.
          */
+        @GuardedBy("mLock")
         void zoomOutFromService() {
-            setScaleAndCenter(1.0f, Float.NaN, Float.NaN,
+            setScaleAndCenter(1.0f, Float.NaN, Float.NaN, /* isScaleTransient= */ false,
                     transformToStubCallback(true),
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             mZoomedOutFromService = true;
@@ -640,7 +654,7 @@
             setActivated(false);
             if (changed) {
                 spec.clear();
-                onMagnificationChangedLocked();
+                onMagnificationChangedLocked(/* isScaleTransient= */ false);
             }
             mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
             sendSpecToAnimation(spec, animationCallback);
@@ -651,7 +665,7 @@
         }
 
         @GuardedBy("mLock")
-        boolean setScale(float scale, float pivotX, float pivotY,
+        boolean setScale(float scale, float pivotX, float pivotY, boolean isScaleTransient,
                 boolean animate, int id) {
             if (!mRegistered) {
                 return false;
@@ -674,12 +688,14 @@
             final float centerX = normPivotX + offsetX;
             final float centerY = normPivotY + offsetY;
             mIdOfLastServiceToMagnify = id;
-            return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id);
+            return setScaleAndCenter(scale, centerX, centerY, isScaleTransient,
+                    transformToStubCallback(animate), id);
         }
 
         @GuardedBy("mLock")
         boolean setScaleAndCenter(float scale, float centerX, float centerY,
-                MagnificationAnimationCallback animationCallback, int id) {
+                boolean isScaleTransient, MagnificationAnimationCallback animationCallback,
+                int id) {
             if (!mRegistered) {
                 return false;
             }
@@ -696,7 +712,7 @@
                                 + animationCallback + ", id = " + id + ")");
             }
             boolean changed = setActivated(true);
-            changed |= updateMagnificationSpecLocked(scale, centerX, centerY);
+            changed |= updateMagnificationSpecLocked(scale, centerX, centerY, isScaleTransient);
             sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
             if (isActivated() && (id != INVALID_SERVICE_ID)) {
                 mIdOfLastServiceToMagnify = id;
@@ -773,7 +789,9 @@
          * @return {@code true} if the magnification spec changed or {@code false}
          *         otherwise
          */
-        boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+        @GuardedBy("mLock")
+        boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY,
+                boolean isScaleTransient) {
             // Handle defaults.
             if (Float.isNaN(centerX)) {
                 centerX = getCenterX();
@@ -801,7 +819,7 @@
             changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
 
             if (changed) {
-                onMagnificationChangedLocked();
+                onMagnificationChangedLocked(isScaleTransient);
             }
 
             return changed;
@@ -816,7 +834,7 @@
             final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
             final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
             if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
-                onMagnificationChangedLocked();
+                onMagnificationChangedLocked(/* isScaleTransient= */ false);
             }
             if (id != INVALID_SERVICE_ID) {
                 mIdOfLastServiceToMagnify = id;
@@ -861,7 +879,7 @@
                             }
                             synchronized (mLock) {
                                 mCurrentMagnificationSpec.setTo(lastSpecSent);
-                                onMagnificationChangedLocked();
+                                onMagnificationChangedLocked(/* isScaleTransient= */ false);
                             }
                         }
                     });
@@ -955,6 +973,7 @@
                         context,
                         traceManager,
                         LocalServices.getService(WindowManagerInternal.class),
+                        LocalServices.getService(InputManagerInternal.class),
                         new Handler(context.getMainLooper()),
                         context.getResources().getInteger(R.integer.config_longAnimTime)),
                 lock,
@@ -1464,20 +1483,24 @@
      * @param scale the target scale, must be >= 1
      * @param pivotX the screen-relative X coordinate around which to scale
      * @param pivotY the screen-relative Y coordinate around which to scale
+     * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed
+     *                         soon. {@code false} otherwise.
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
      * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
+    @SuppressWarnings("GuardedBy")
+    // errorprone cannot recognize an inner class guarded by an outer class member.
     public boolean setScale(int displayId, float scale, float pivotX, float pivotY,
-            boolean animate, int id) {
+            boolean isScaleTransient, boolean animate, int id) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.setScale(scale, pivotX, pivotY, animate, id);
+            return display.setScale(scale, pivotX, pivotY, isScaleTransient, animate, id);
         }
     }
 
@@ -1496,6 +1519,8 @@
      * @return {@code true} if the magnification spec changed, {@code false} if
      * the spec did not change
      */
+    @SuppressWarnings("GuardedBy")
+    // errorprone cannot recognize an inner class guarded by an outer class member.
     public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
@@ -1503,7 +1528,7 @@
                 return false;
             }
             return display.setScaleAndCenter(Float.NaN, centerX, centerY,
-                    animate ? STUB_ANIMATION_CALLBACK : null, id);
+                    /* isScaleTransient= */ false, animate ? STUB_ANIMATION_CALLBACK : null, id);
         }
     }
 
@@ -1526,7 +1551,32 @@
      */
     public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
             boolean animate, int id) {
-        return setScaleAndCenter(displayId, scale, centerX, centerY,
+        return setScaleAndCenter(displayId, scale, centerX, centerY, /* isScaleTransient= */ false,
+                transformToStubCallback(animate), id);
+    }
+
+    /**
+     * Sets the scale and center of the magnified region, optionally
+     * animating the transition. If animation is disabled, the transition
+     * is immediate.
+     *
+     * @param displayId        The logical display id.
+     * @param scale            the target scale, or {@link Float#NaN} to leave unchanged
+     * @param centerX          the screen-relative X coordinate around which to
+     *                         center and scale, or {@link Float#NaN} to leave unchanged
+     * @param centerY          the screen-relative Y coordinate around which to
+     *                         center and scale, or {@link Float#NaN} to leave unchanged
+     * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed
+     *                         soon. {@code false} otherwise.
+     * @param animate          {@code true} to animate the transition, {@code false}
+     *                         to transition immediately
+     * @param id               the ID of the service requesting the change
+     * @return {@code true} if the magnification spec changed, {@code false} if
+     * the spec did not change
+     */
+    public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
+            boolean isScaleTransient, boolean animate, int id) {
+        return setScaleAndCenter(displayId, scale, centerX, centerY, isScaleTransient,
                 transformToStubCallback(animate), id);
     }
 
@@ -1541,20 +1591,25 @@
      *                center and scale, or {@link Float#NaN} to leave unchanged
      * @param centerY the screen-relative Y coordinate around which to
      *                center and scale, or {@link Float#NaN} to leave unchanged
+     * @param isScaleTransient {@code true} if the scale is for a short time and potentially changed
+     *                         soon. {@code false} otherwise.
      * @param animationCallback Called when the animation result is valid.
      *                           {@code null} to transition immediately
      * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
+    @SuppressWarnings("GuardedBy")
+    // errorprone cannot recognize an inner class guarded by an outer class member.
     public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
-            MagnificationAnimationCallback animationCallback, int id) {
+            boolean isScaleTransient, MagnificationAnimationCallback animationCallback, int id) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id);
+            return display.setScaleAndCenter(scale, centerX, centerY, isScaleTransient,
+                    animationCallback, id);
         }
     }
 
@@ -1569,6 +1624,8 @@
      *                screen pixels.
      * @param id      the ID of the service requesting the change
      */
+    @SuppressWarnings("GuardedBy")
+    // errorprone cannot recognize an inner class guarded by an outer class member.
     public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
@@ -1640,6 +1697,8 @@
      */
     public void persistScale(int displayId) {
         final float scale = getScale(displayId);
+        notifyScaleForInput(displayId, scale);
+
         if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) {
             return;
         }
@@ -1665,6 +1724,8 @@
      *
      * @param displayId The logical display id.
      */
+    @SuppressWarnings("GuardedBy")
+    // errorprone cannot recognize an inner class guarded by an outer class member.
     private void zoomOutFromService(int displayId) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
@@ -1691,6 +1752,20 @@
     }
 
     /**
+     * Notifies input manager that magnification scale changed non-transiently
+     * so that pointer cursor is scaled as well.
+     *
+     * @param displayId The logical display id.
+     * @param scale     The new scale factor.
+     */
+    public void notifyScaleForInput(int displayId, float scale) {
+        if (Flags.magnificationEnlargePointer()) {
+            mControllerCtx.getInputManager()
+                    .setAccessibilityPointerIconScaleFactor(displayId, scale);
+        }
+    }
+
+    /**
      * Resets all displays' magnification if last magnifying service is disabled.
      *
      * @param connectionId
@@ -2166,6 +2241,7 @@
         private final Context mContext;
         private final AccessibilityTraceManager mTrace;
         private final WindowManagerInternal mWindowManager;
+        private final InputManagerInternal mInputManager;
         private final Handler mHandler;
         private final Long mAnimationDuration;
 
@@ -2175,11 +2251,13 @@
         public ControllerContext(@NonNull Context context,
                 @NonNull AccessibilityTraceManager traceManager,
                 @NonNull WindowManagerInternal windowManager,
+                @NonNull InputManagerInternal inputManager,
                 @NonNull Handler handler,
                 long animationDuration) {
             mContext = context;
             mTrace = traceManager;
             mWindowManager = windowManager;
+            mInputManager = inputManager;
             mHandler = handler;
             mAnimationDuration = animationDuration;
         }
@@ -2209,6 +2287,14 @@
         }
 
         /**
+         * @return InputManagerInternal
+         */
+        @NonNull
+        public InputManagerInternal getInputManager() {
+            return mInputManager;
+        }
+
+        /**
          * @return Handler for main looper
          */
         @NonNull
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 963334b..c6a966f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -617,7 +617,8 @@
             }
 
             if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
-            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
+            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY,
+                    /* isScaleTransient= */ true, /* animate= */ false,
                     AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
 
             checkShouldDetectPassPersistedScale();
@@ -1974,6 +1975,7 @@
                     /* scale= */ scale,
                     /* centerX= */ mPivotEdge.x,
                     /* centerY= */ mPivotEdge.y,
+                    /* isScaleTransient= */ true,
                     /* animate= */ true,
                     /* id= */ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
             if (scale == 1.0f) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 1489d16..d40e747 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -176,7 +176,8 @@
     public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
         if (getFullScreenMagnificationController().isActivated(displayId)) {
             getFullScreenMagnificationController().setScaleAndCenter(displayId, scale,
-                    Float.NaN, Float.NaN, false, MAGNIFICATION_GESTURE_HANDLER_ID);
+                    Float.NaN, Float.NaN, /* isScaleTransient= */ !updatePersistence, false,
+                    MAGNIFICATION_GESTURE_HANDLER_ID);
             if (updatePersistence) {
                 getFullScreenMagnificationController().persistScale(displayId);
             }
@@ -371,7 +372,7 @@
                         }
                         screenMagnificationController.setScaleAndCenter(displayId, targetScale,
                                 magnificationCenter.x, magnificationCenter.y,
-                                magnificationAnimationCallback, id);
+                                /* isScaleTransient= */ false, magnificationAnimationCallback, id);
                     } else {
                         if (screenMagnificationController.isRegistered(displayId)) {
                             screenMagnificationController.reset(displayId, false);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 95281c8..5911070 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -715,11 +715,17 @@
 
         @Override
         public byte[] getBackupPayload(int userId) {
+            if (getCallingUid() != SYSTEM_UID) {
+                throw new SecurityException("Caller must be system");
+            }
             return mBackupRestoreProcessor.getBackupPayload(userId);
         }
 
         @Override
         public void applyRestoredPayload(byte[] payload, int userId) {
+            if (getCallingUid() != SYSTEM_UID) {
+                throw new SecurityException("Caller must be system");
+            }
             mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 217ef20..6ba8514 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -60,6 +60,7 @@
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTATION;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
+import static android.content.Intent.isPreventIntentRedirectEnabled;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -130,7 +131,6 @@
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
-import static android.security.Flags.preventIntentRedirect;
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -19272,7 +19272,7 @@
      * @hide
      */
     public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
-        if (!preventIntentRedirect()) return;
+        if (!isPreventIntentRedirectEnabled()) return;
 
         if (intent == null || intent.getExtraIntentKeys() == null) return;
         for (String key : intent.getExtraIntentKeys()) {
@@ -19283,7 +19283,9 @@
                             + "} does not correspond to an intent in the extra bundle.");
                     continue;
                 }
-                Slog.wtf(TAG, "A creator token is added to an intent.");
+                Slog.wtf(TAG,
+                        "A creator token is added to an intent. creatorPackage: " + creatorPackage
+                                + "; intent: " + intent);
                 IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
                 if (creatorToken != null) {
                     extraIntent.setCreatorToken(creatorToken);
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 99f7f12..c888eef 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -262,4 +262,12 @@
      */
     public abstract void handleKeyGestureInKeyGestureController(int deviceId, int[] keycodes,
             int modifierState, @KeyGestureEvent.KeyGestureType int event);
+
+    /**
+     * Sets the magnification scale factor for pointer icons.
+     *
+     * @param displayId   the ID of the display where the new scale factor is applied.
+     * @param scaleFactor the new scale factor to be applied for pointer icons.
+     */
+    public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor);
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8acf583..98e5319 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3506,6 +3506,11 @@
                 int modifierState, @KeyGestureEvent.KeyGestureType int gestureType) {
             mKeyGestureController.handleKeyGesture(deviceId, keycodes, modifierState, gestureType);
         }
+
+        @Override
+        public void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) {
+            InputManagerService.this.setAccessibilityPointerIconScaleFactor(displayId, scaleFactor);
+        }
     }
 
     @Override
@@ -3688,6 +3693,10 @@
         mPointerIconCache.setPointerScale(scale);
     }
 
+    void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) {
+        mPointerIconCache.setAccessibilityScaleFactor(displayId, scaleFactor);
+    }
+
     interface KeyboardBacklightControllerInterface {
         default void incrementKeyboardBacklight(int deviceId) {}
         default void decrementKeyboardBacklight(int deviceId) {}
diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java
index 297cd68..e16031c 100644
--- a/services/core/java/com/android/server/input/PointerIconCache.java
+++ b/services/core/java/com/android/server/input/PointerIconCache.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseDoubleArray;
 import android.util.SparseIntArray;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
@@ -34,6 +35,7 @@
 import android.view.PointerIcon;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.UiThread;
 
 import java.util.Objects;
@@ -51,7 +53,7 @@
     private final NativeInputManagerService mNative;
 
     // We use the UI thread for loading pointer icons.
-    private final Handler mUiThreadHandler = UiThread.getHandler();
+    private final Handler mUiThreadHandler;
 
     @GuardedBy("mLoadedPointerIconsByDisplayAndType")
     private final SparseArray<SparseArray<PointerIcon>> mLoadedPointerIconsByDisplayAndType =
@@ -70,6 +72,9 @@
             POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
     @GuardedBy("mLoadedPointerIconsByDisplayAndType")
     private float mPointerIconScale = DEFAULT_POINTER_SCALE;
+    // Note that android doesn't have SparseFloatArray, so this falls back to use double instead.
+    @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+    private final SparseDoubleArray mAccessibilityScaleFactorPerDisplay = new SparseDoubleArray();
 
     private final DisplayManager.DisplayListener mDisplayListener =
             new DisplayManager.DisplayListener() {
@@ -86,6 +91,7 @@
                         mLoadedPointerIconsByDisplayAndType.remove(displayId);
                         mDisplayContexts.remove(displayId);
                         mDisplayDensities.delete(displayId);
+                        mAccessibilityScaleFactorPerDisplay.delete(displayId);
                     }
                 }
 
@@ -96,8 +102,15 @@
             };
 
     /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService) {
+        this(context, nativeService, UiThread.getHandler());
+    }
+
+    @VisibleForTesting
+    /* package */ PointerIconCache(Context context, NativeInputManagerService nativeService,
+            Handler handler) {
         mContext = context;
         mNative = nativeService;
+        mUiThreadHandler = handler;
     }
 
     public void systemRunning() {
@@ -134,6 +147,11 @@
         mUiThreadHandler.post(() -> handleSetPointerScale(scale));
     }
 
+    /** Set the scale for accessibility (magnification) for vector pointer icons. */
+    public void setAccessibilityScaleFactor(int displayId, float scaleFactor) {
+        mUiThreadHandler.post(() -> handleAccessibilityScaleFactor(displayId, scaleFactor));
+    }
+
     /**
      * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if
      * it isn't already cached.
@@ -155,8 +173,10 @@
                         /* force= */ true);
                 theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle),
                         /* force= */ true);
+                final float scale = mPointerIconScale
+                        * (float) mAccessibilityScaleFactorPerDisplay.get(displayId, 1f);
                 icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),
-                        type, mUseLargePointerIcons, mPointerIconScale);
+                        type, mUseLargePointerIcons, scale);
                 iconsByType.put(type, icon);
             }
             return Objects.requireNonNull(icon);
@@ -261,6 +281,19 @@
         mNative.reloadPointerIcons();
     }
 
+    @android.annotation.UiThread
+    private void handleAccessibilityScaleFactor(int displayId, float scale) {
+        synchronized (mLoadedPointerIconsByDisplayAndType) {
+            if (mAccessibilityScaleFactorPerDisplay.get(displayId, 1f) == scale) {
+                return;
+            }
+            mAccessibilityScaleFactorPerDisplay.put(displayId, scale);
+            // Clear cached icons on the display.
+            mLoadedPointerIconsByDisplayAndType.remove(displayId);
+        }
+        mNative.reloadPointerIcons();
+    }
+
     // Updates the cached display density for the given displayId, and returns true if
     // the cached density changed.
     @GuardedBy("mLoadedPointerIconsByDisplayAndType")
diff --git a/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java b/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
new file mode 100644
index 0000000..caca011
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/BackupTransportConnection.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.forensic;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.security.forensic.ForensicEvent;
+import android.security.forensic.IBackupTransport;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class BackupTransportConnection implements ServiceConnection {
+    private static final String TAG = "BackupTransportConnection";
+    private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins
+    private final Context mContext;
+    private String mForensicBackupTransportConfig;
+    volatile IBackupTransport mService;
+
+    public BackupTransportConnection(Context context) {
+        mContext = context;
+        mService = null;
+    }
+
+    /**
+     * Initialize the BackupTransport binder service.
+     * @return Whether the initialization succeed.
+     */
+    public boolean initialize() {
+        if (!bindService()) {
+            return false;
+        }
+        AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+        try {
+            mService.initialize(resultFuture);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote Exception", e);
+            unbindService();
+            return false;
+        }
+        Integer result = getFutureResult(resultFuture);
+        if (result != null && result == 0) {
+            return true;
+        } else {
+            unbindService();
+            return false;
+        }
+    }
+
+    /**
+     * Add data to the BackupTransport binder service.
+     * @param data List of ForensicEvent.
+     * @return Whether the data is added to the binder service.
+     */
+    public boolean addData(List<ForensicEvent> data) {
+        AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+        try {
+            mService.addData(data, resultFuture);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote Exception", e);
+            return false;
+        }
+        Integer result = getFutureResult(resultFuture);
+        return result != null && result == 0;
+    }
+
+    /**
+     * Release the BackupTransport binder service.
+     */
+    public void release() {
+        AndroidFuture<Integer> resultFuture = new AndroidFuture<>();
+        try {
+            mService.release(resultFuture);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote Exception", e);
+        } finally {
+            unbindService();
+        }
+    }
+
+    private <T> T getFutureResult(AndroidFuture<T> future) {
+        try {
+            return future.get(FUTURE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException
+                 | CancellationException e) {
+            Slog.w(TAG, "Failed to get result from transport:", e);
+            return null;
+        }
+    }
+
+    private boolean bindService() {
+        mForensicBackupTransportConfig = mContext.getString(
+                com.android.internal.R.string.config_forensicBackupTransport);
+        if (TextUtils.isEmpty(mForensicBackupTransportConfig)) {
+            return false;
+        }
+
+        ComponentName serviceComponent =
+                ComponentName.unflattenFromString(mForensicBackupTransportConfig);
+        if (serviceComponent == null) {
+            return false;
+        }
+
+        Intent intent = new Intent().setComponent(serviceComponent);
+        boolean result = mContext.bindServiceAsUser(
+                intent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+        if (!result) {
+            unbindService();
+        }
+        return result;
+    }
+
+    private void unbindService() {
+        mContext.unbindService(this);
+        mService = null;
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        mService = IBackupTransport.Stub.asInterface(service);
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        mService = null;
+    }
+}
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 07639d1..20c648e 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -63,6 +63,7 @@
 
     private final Context mContext;
     private final Handler mHandler;
+    private final BackupTransportConnection mBackupTransportConnection;
     private final BinderService mBinderService;
 
     private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
@@ -77,6 +78,7 @@
         super(injector.getContext());
         mContext = injector.getContext();
         mHandler = new EventHandler(injector.getLooper(), this);
+        mBackupTransportConnection = injector.getBackupTransportConnection();
         mBinderService = new BinderService(this);
     }
 
@@ -221,6 +223,10 @@
     private void enable(IForensicServiceCommandCallback callback) throws RemoteException {
         switch (mState) {
             case STATE_VISIBLE:
+                if (!mBackupTransportConnection.initialize()) {
+                    callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
+                    break;
+                }
                 mState = STATE_ENABLED;
                 notifyStateMonitors();
                 callback.onSuccess();
@@ -236,6 +242,7 @@
     private void disable(IForensicServiceCommandCallback callback) throws RemoteException {
         switch (mState) {
             case STATE_ENABLED:
+                mBackupTransportConnection.release();
                 mState = STATE_VISIBLE;
                 notifyStateMonitors();
                 callback.onSuccess();
@@ -266,6 +273,8 @@
         Context getContext();
 
         Looper getLooper();
+
+        BackupTransportConnection getBackupTransportConnection();
     }
 
     private static final class InjectorImpl implements Injector {
@@ -289,6 +298,11 @@
             serviceThread.start();
             return serviceThread.getLooper();
         }
+
+        @Override
+        public BackupTransportConnection getBackupTransportConnection() {
+            return new BackupTransportConnection(mContext);
+        }
     }
 }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index c1f5a27..a2c2dfc 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -478,10 +478,10 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logForIntentRedirect(
+                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                     "Creator URI Grant Caused Exception.", intent, creatorUid,
-                                    creatorPackage, filterCallingUid, callingPackage);
-                            // TODO b/368559093 - rethrow the securityException.
+                                    creatorPackage, filterCallingUid, callingPackage,
+                                    securityException);
                         }
                     }
                     if ((aInfo.applicationInfo.privateFlags
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5d3ae54..2c4179f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -54,6 +54,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.ActivityInfo.launchModeToString;
 import static android.os.Process.INVALID_UID;
+import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -100,9 +101,11 @@
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.WindowConfiguration;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -188,6 +191,10 @@
     @Disabled
     static final long ASM_RESTRICTIONS = 230590090L;
 
+    @ChangeId
+    @Overridable
+    private static final long ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION = 29623414L;
+
     private final ActivityTaskManagerService mService;
     private final RootWindowContainer mRootWindowContainer;
     private final ActivityTaskSupervisor mSupervisor;
@@ -608,11 +615,10 @@
             // Check if the Intent was redirected
             if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
                     != 0) {
-                ActivityStarter.logForIntentRedirect(
+                ActivityStarter.logAndThrowExceptionForIntentRedirect(
                         "Unparceled intent does not have a creator token set.", intent,
-                        intentCreatorUid,
-                        intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage);
-                // TODO b/368559093 - eventually ramp up to throw SecurityException
+                        intentCreatorUid, intentCreatorPackage, resolvedCallingUid,
+                        resolvedCallingPackage, null);
             }
             if (IntentCreatorToken.isValid(intent)) {
                 IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken();
@@ -645,11 +651,10 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logForIntentRedirect(
+                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                     "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                     intentCreatorPackage, resolvedCallingUid,
-                                    resolvedCallingPackage);
-                            // TODO b/368559093 - rethrow the securityException.
+                                    resolvedCallingPackage, securityException);
                         }
                     }
                 } else {
@@ -670,11 +675,10 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logForIntentRedirect(
+                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                     "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                     intentCreatorPackage, resolvedCallingUid,
-                                    resolvedCallingPackage);
-                            // TODO b/368559093 - rethrow the securityException.
+                                    resolvedCallingPackage, securityException);
                         }
                     }
                 }
@@ -1045,7 +1049,7 @@
         int callingUid = request.callingUid;
         int intentCreatorUid = request.intentCreatorUid;
         String intentCreatorPackage = request.intentCreatorPackage;
-        String intentCallingPackage = request.callingPackage;
+        String callingPackage = request.callingPackage;
         String callingFeatureId = request.callingFeatureId;
         final int realCallingPid = request.realCallingPid;
         final int realCallingUid = request.realCallingUid;
@@ -1130,7 +1134,7 @@
                 // launched in the app flow to redirect to an activity picked by the user, where
                 // we want the final activity to consider it to have been launched by the
                 // previous app activity.
-                intentCallingPackage = sourceRecord.launchedFromPackage;
+                callingPackage = sourceRecord.launchedFromPackage;
                 callingFeatureId = sourceRecord.launchedFromFeatureId;
             }
         }
@@ -1152,7 +1156,7 @@
                 if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
                     err = packageArchiver
                             .requestUnarchiveOnActivityStart(
-                                    intent, intentCallingPackage, mRequest.userId, realCallingUid);
+                                    intent, callingPackage, mRequest.userId, realCallingUid);
                 }
             }
         }
@@ -1211,7 +1215,7 @@
         boolean abort;
         try {
             abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                    requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId,
+                    requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
                     request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,
                     resultRootTask);
         } catch (SecurityException e) {
@@ -1239,7 +1243,7 @@
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
-                intentCallingPackage);
+                callingPackage);
 
         if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) {
             try {
@@ -1247,36 +1251,29 @@
                         requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
                         request.ignoreTargetSecurity, inTask != null, null, resultRecord,
                         resultRootTask)) {
-                    logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.",
+                    abort = logAndAbortForIntentRedirect(
+                            "Creator checkStartAnyActivityPermission Caused abortion.",
                             intent, intentCreatorUid, intentCreatorPackage, callingUid,
-                            intentCallingPackage);
-                    // TODO b/368559093 - set abort to true.
-                    // abort = true;
+                            callingPackage);
                 }
             } catch (SecurityException e) {
-                logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.",
-                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
-                        intentCallingPackage);
-                // TODO b/368559093 - rethrow the exception.
-                //throw e;
+                logAndThrowExceptionForIntentRedirect(
+                        "Creator checkStartAnyActivityPermission Caused Exception.",
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage,
+                        e);
             }
-            if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0,
-                    resolvedType, aInfo.applicationInfo)) {
-                logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.",
-                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
-                        intentCallingPackage);
-                // TODO b/368559093 - set abort to true.
-                // abort = true;
+            if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid,
+                    0, resolvedType, aInfo.applicationInfo)) {
+                abort = logAndAbortForIntentRedirect(
+                        "Creator IntentFirewall.checkStartActivity Caused abortion.",
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
 
-            if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid,
-                    intentCreatorPackage)) {
-                logForIntentRedirect(
+            if (!mService.getPermissionPolicyInternal().checkStartActivity(intent,
+                    intentCreatorUid, intentCreatorPackage)) {
+                abort = logAndAbortForIntentRedirect(
                         "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
-                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
-                        intentCallingPackage);
-                // TODO b/368559093 - set abort to true.
-                // abort = true;
+                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
             intent.removeCreatorTokenInfo();
         }
@@ -1296,7 +1293,7 @@
                         balController.checkBackgroundActivityStart(
                             callingUid,
                             callingPid,
-                                intentCallingPackage,
+                            callingPackage,
                             realCallingUid,
                             realCallingPid,
                             callerApp,
@@ -1317,7 +1314,7 @@
         if (request.allowPendingRemoteAnimationRegistryLookup) {
             checkedOptions = mService.getActivityStartController()
                     .getPendingRemoteAnimationRegistry()
-                    .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions);
+                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
         }
         if (mService.mController != null) {
             try {
@@ -1334,7 +1331,7 @@
         final TaskDisplayArea suggestedLaunchDisplayArea =
                 computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions);
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags,
-                intentCallingPackage,
+                callingPackage,
                 callingFeatureId);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
                 callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
@@ -1372,7 +1369,7 @@
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 final IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage,
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                         callingFeatureId,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1436,7 +1433,7 @@
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
-                    intentCallingPackage, callingFeatureId, verificationBundle, resolvedType,
+                    callingPackage, callingFeatureId, verificationBundle, resolvedType,
                     userId);
             resolvedType = null;
             callingUid = realCallingUid;
@@ -1460,7 +1457,7 @@
                 .setCaller(callerApp)
                 .setLaunchedFromPid(callingPid)
                 .setLaunchedFromUid(callingUid)
-                .setLaunchedFromPackage(intentCallingPackage)
+                .setLaunchedFromPackage(callingPackage)
                 .setLaunchedFromFeature(callingFeatureId)
                 .setIntent(intent)
                 .setResolvedType(resolvedType)
@@ -3588,16 +3585,32 @@
         pw.println(mInTaskFragment);
     }
 
-    static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid,
-            String intentCreatorPackage, int callingUid, String callingPackage) {
+    static void logAndThrowExceptionForIntentRedirect(@NonNull String message,
+            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
+            int callingUid, @Nullable String callingPackage,
+            @Nullable SecurityException originalException) {
         String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
                 intentCreatorPackage, callingUid, callingPackage);
         Slog.wtf(TAG, msg);
+        if (preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
+                ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid)) {
+            throw new SecurityException(msg, originalException);
+        }
     }
 
-    private static String getIntentRedirectPreventedLogMessage(String message, Intent intent,
-            int intentCreatorUid, String intentCreatorPackage, int callingUid,
-            String callingPackage) {
+    private static boolean logAndAbortForIntentRedirect(@NonNull String message,
+            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
+            int callingUid, @Nullable String callingPackage) {
+        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
+                intentCreatorPackage, callingUid, callingPackage);
+        Slog.wtf(TAG, msg);
+        return preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
+                ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid);
+    }
+
+    private static String getIntentRedirectPreventedLogMessage(@NonNull String message,
+            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
+            int callingUid, @Nullable String callingPackage) {
         return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
                 + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
                 + "; callingPackage: " + callingPackage + "; intent: " + intent;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0b7ce75..e052f94 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -106,6 +106,7 @@
 import com.android.internal.os.ApplicationSharedMemory;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
+import com.android.internal.pm.RoSystemFeatures;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.protolog.ProtoLogConfigurationServiceImpl;
@@ -1495,8 +1496,7 @@
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
 
-        boolean isWatch = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_WATCH);
+        boolean isWatch = RoSystemFeatures.hasFeatureWatch(context);
 
         boolean isArc = context.getPackageManager().hasSystemFeature(
                 "org.chromium.arc");
@@ -2758,7 +2758,7 @@
             t.traceEnd();
         }
 
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
+        if (RoSystemFeatures.hasFeatureEmbedded(context)) {
             t.traceBegin("StartIoTSystemService");
             mSystemServiceManager.startService(IOT_SERVICE_CLASS);
             t.traceEnd();
@@ -3137,9 +3137,7 @@
                 }, WEBVIEW_PREPARATION);
             }
 
-            boolean isAutomotive = mPackageManager
-                    .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-            if (isAutomotive) {
+            if (RoSystemFeatures.hasFeatureAutomotive(context)) {
                 t.traceBegin("StartCarServiceHelperService");
                 final SystemService cshs = mSystemServiceManager
                         .startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 5c718d9..b2fe138 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -1726,6 +1726,8 @@
         }
 
         // Top-started job
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-stared jobs are out of quota enforcement.
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
             trackJobs(job, jobDefIWF, jobHigh);
@@ -1755,6 +1757,38 @@
             assertEquals(timeUntilQuotaConsumedMs,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh));
         }
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Quota is enforced for top-started job after the process leaves TOP/BTOP state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            trackJobs(job, jobDefIWF, jobHigh);
+            mQuotaController.prepareForExecutionLocked(job);
+            mQuotaController.prepareForExecutionLocked(jobDefIWF);
+            mQuotaController.prepareForExecutionLocked(jobHigh);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobDefIWF)));
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked((jobHigh)));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobDefIWF, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobHigh, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobDefIWF));
+            assertEquals(timeUntilQuotaConsumedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(jobHigh));
+        }
     }
 
     @Test
@@ -1824,6 +1858,7 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
         // Top-started job
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
@@ -1831,6 +1866,7 @@
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         synchronized (mQuotaController.mLock) {
+            // Top-started job is out of quota enforcement.
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
@@ -1842,6 +1878,28 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            // Top-started job is enforced by quota policy after the app leaves the TOP state.
+            // The max execution time should be the total EJ session limit of the RARE bucket
+            // minus the time has been used.
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
         // Test used quota rolling out of window.
         synchronized (mQuotaController.mLock) {
             mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
@@ -1856,6 +1914,7 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
         // Top-started job
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
@@ -1864,6 +1923,7 @@
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         synchronized (mQuotaController.mLock) {
+            // Top-started job is out of quota enforcement.
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
@@ -1874,6 +1934,28 @@
             assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            // Top-started job is enforced by quota policy after the app leaves the TOP state.
+            // The max execution time should be the total EJ session limit of the RARE bucket.
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
     }
 
     @Test
@@ -1902,6 +1984,7 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
         // Top-started job
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
@@ -1909,6 +1992,7 @@
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         synchronized (mQuotaController.mLock) {
+            // Top-started job is out of quota enforcement.
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
@@ -1920,6 +2004,27 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            // Top-started job is enforced by quota policy after the app leaves the TOP state.
+            // The max execution time should be the total EJ session limit of the RARE bucket.
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
+
         // Test used quota rolling out of window.
         synchronized (mQuotaController.mLock) {
             mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
@@ -1935,6 +2040,7 @@
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
 
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
         // Top-started job
         setProcessState(ActivityManager.PROCESS_STATE_TOP);
         synchronized (mQuotaController.mLock) {
@@ -1943,6 +2049,7 @@
         }
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
         synchronized (mQuotaController.mLock) {
+            // Top-started job is out of quota enforcement.
             assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
             mQuotaController.maybeStopTrackingJobLocked(job, null);
@@ -1953,6 +2060,28 @@
             assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+            mQuotaController.prepareForExecutionLocked(job);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        synchronized (mQuotaController.mLock) {
+            // Top-started job is enforced by quota policy after the app leaves the TOP state.
+            // The max execution time should be the total EJ session limit of the RARE bucket.
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+            mQuotaController.maybeStopTrackingJobLocked(job, null);
+        }
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        synchronized (mQuotaController.mLock) {
+            assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+        }
     }
 
     /**
@@ -4608,6 +4737,7 @@
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         advanceElapsedClock(SECOND_IN_MILLIS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
 
         // Bg job starts while inactive, spans an entire active session, and ends after the
         // active session.
@@ -4686,8 +4816,66 @@
             mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
             mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
         }
+        // jobBg2 and jobFg1 are counted, jobTop is not counted.
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+
+        // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
+        // foreground_service and a new job starts. Shortly after, uid goes
+        // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 2, which accounts for the bg2 and fg and top jobs.
+        //    Top started jobs are not quota free any more if the process leaves TOP/BTOP state.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg1);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
+        }
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobFg1);
+        }
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg2);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
+        }
+        // jobBg2, jobFg1 and jobTop are counted.
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3));
+        assertEquals(expected, mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
@@ -4807,7 +4995,8 @@
      * Tests that TOP jobs aren't stopped when an app runs out of quota.
      */
     @Test
-    public void testTracking_OutOfQuota_ForegroundAndBackground() {
+    @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS)
+    public void testTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() {
         setDischarging();
 
         JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
@@ -4851,6 +5040,7 @@
         // Go to a background state.
         setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
         advanceElapsedClock(remainingTimeMs / 2 + 1);
+        // Only Bg job will be changed from in-quota to out-of-quota.
         inOrder.verify(mJobSchedulerService,
                         timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
                 .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
@@ -4897,6 +5087,105 @@
     }
 
     /**
+     * Tests that TOP jobs are stopped when an app runs out of quota.
+     */
+    @Test
+    @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS)
+    public void testTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() {
+        setDischarging();
+
+        JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
+        JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
+        trackJobs(jobBg, jobTop);
+        setStandbyBucket(WORKING_INDEX, jobTop, jobBg); // 2 hour window
+        // Now the package only has 20 seconds to run.
+        final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false);
+
+        InOrder inOrder = inOrder(mJobSchedulerService);
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Start the job.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg);
+        }
+        advanceElapsedClock(remainingTimeMs / 2);
+        // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+        // should continue to have remainingTimeMs / 2 time remaining.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop);
+        }
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        inOrder.verify(mJobSchedulerService,
+                        timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
+        synchronized (mQuotaController.mLock) {
+            assertEquals(remainingTimeMs / 2,
+                    mQuotaController.getRemainingExecutionTimeLocked(jobBg));
+            assertEquals(remainingTimeMs / 2,
+                    mQuotaController.getRemainingExecutionTimeLocked(jobTop));
+        }
+        // Go to a background state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        advanceElapsedClock(remainingTimeMs / 2 + 1);
+        // Both Bg and Top jobs should be changed from in-quota to out-of-quota
+        inOrder.verify(mJobSchedulerService,
+                        timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
+        // Top job should NOT be allowed to run.
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // New jobs to run.
+        JobStatus jobBg2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 3);
+        JobStatus jobTop2 = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 4);
+        JobStatus jobFg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 5);
+        setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        // Both Bg and Top jobs should be changed from out-of-quota to in-quota.
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
+        trackJobs(jobFg, jobTop);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop);
+        }
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        // App still in foreground so everything should be in quota.
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        // App is in background so everything should be out of quota.
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Bg, Fg and Top jobs should be changed from in-quota to out-of-quota.
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 3));
+        // App is now in background and out of quota. Fg should now change to out of quota
+        // since it wasn't started. Top should now changed to out of quota even it started
+        // when the app was in TOP.
+        assertFalse(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+        trackJobs(jobBg2);
+        assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+    }
+
+    /**
      * Tests that a job is properly updated and JobSchedulerService is notified when a job reaches
      * its quota.
      */
@@ -6280,6 +6569,7 @@
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
 
         advanceElapsedClock(SECOND_IN_MILLIS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
 
         // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
         // foreground_service and a new job starts. Shortly after, uid goes
@@ -6333,6 +6623,63 @@
         expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
         assertEquals(expected,
                 mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        advanceElapsedClock(SECOND_IN_MILLIS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS);
+
+        // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
+        // foreground_service and a new job starts. Shortly after, uid goes
+        // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs.
+        // This should result in two TimingSessions:
+        //  * The first should have a count of 1
+        //  * The second should have a count of 3, which accounts for the bg2, fg and top jobs.
+        //    Top started jobs are not quota free any more if the process leaves TOP/BTOP state.
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+            mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+        }
+        setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg1);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1);
+        }
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobFg1);
+        }
+        advanceElapsedClock(5 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+        start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg2);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobTop, null);
+        }
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.maybeStopTrackingJobLocked(jobBg2, null);
+            mQuotaController.maybeStopTrackingJobLocked(jobFg1, null);
+        }
+        expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 3));
+        assertEquals(expected,
+                mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
     }
 
     /**
@@ -6701,7 +7048,8 @@
      * Tests that expedited jobs aren't stopped when an app runs out of quota.
      */
     @Test
-    public void testEJTracking_OutOfQuota_ForegroundAndBackground() {
+    @DisableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS)
+    public void testEJTracking_OutOfQuota_ForegroundAndBackground_DisableTopStartedJobsThrottling() {
         setDischarging();
         setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0);
 
@@ -6813,6 +7161,129 @@
     }
 
     /**
+     * Tests that expedited jobs are stopped when an app runs out of quota.
+     */
+    @Test
+    @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS)
+    public void testEJTracking_OutOfQuota_ForegroundAndBackground_EnableTopStartedJobsThrottling() {
+        setDischarging();
+        setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0);
+
+        JobStatus jobBg =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1);
+        JobStatus jobTop =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 2);
+        JobStatus jobUnstarted =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 3);
+        trackJobs(jobBg, jobTop, jobUnstarted);
+        setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted);
+        // Now the package only has 20 seconds to run.
+        final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+        mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+                createTimingSession(
+                        JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+                        mQcConstants.EJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true);
+
+        InOrder inOrder = inOrder(mJobSchedulerService);
+
+        // UID starts out inactive.
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Start the job.
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobBg);
+        }
+        advanceElapsedClock(remainingTimeMs / 2);
+        // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+        // should continue to have remainingTimeMs / 2 time remaining.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop);
+        }
+        advanceElapsedClock(remainingTimeMs);
+
+        // Wait for some extra time to allow for job processing.
+        inOrder.verify(mJobSchedulerService,
+                        timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
+        synchronized (mQuotaController.mLock) {
+            assertEquals(remainingTimeMs / 2,
+                    mQuotaController.getRemainingEJExecutionTimeLocked(
+                            SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+        // Go to a background state.
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        advanceElapsedClock(remainingTimeMs / 2 + 1);
+        // Bg, Top and jobUnstarted should be changed from in-quota to out-of-quota.
+        inOrder.verify(mJobSchedulerService,
+                        timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 3));
+        // Top should still NOT be "in quota" even it started before the app
+        // ran on top out of quota.
+        assertFalse(jobBg.isExpeditedQuotaApproved());
+        assertFalse(jobTop.isExpeditedQuotaApproved());
+        assertFalse(jobUnstarted.isExpeditedQuotaApproved());
+        synchronized (mQuotaController.mLock) {
+            assertTrue(
+                    0 >= mQuotaController
+                            .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+
+        // New jobs to run.
+        JobStatus jobBg2 =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 4);
+        JobStatus jobTop2 =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 5);
+        JobStatus jobFg =
+                createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 6);
+        setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota.
+        // jobBg, jobFg and jobUnstarted are changed from out-of-quota to in-quota.
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 3));
+        trackJobs(jobTop2, jobFg);
+        synchronized (mQuotaController.mLock) {
+            mQuotaController.prepareForExecutionLocked(jobTop2);
+        }
+        assertTrue(jobTop.isExpeditedQuotaApproved());
+        assertTrue(jobTop2.isExpeditedQuotaApproved());
+        assertTrue(jobFg.isExpeditedQuotaApproved());
+        assertTrue(jobBg.isExpeditedQuotaApproved());
+        assertTrue(jobUnstarted.isExpeditedQuotaApproved());
+
+        // App still in foreground so everything should be in quota.
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(getProcessStateQuotaFreeThreshold());
+        assertTrue(jobTop.isExpeditedQuotaApproved());
+        assertTrue(jobTop2.isExpeditedQuotaApproved());
+        assertTrue(jobFg.isExpeditedQuotaApproved());
+        assertTrue(jobBg.isExpeditedQuotaApproved());
+        assertTrue(jobUnstarted.isExpeditedQuotaApproved());
+
+        advanceElapsedClock(20 * SECOND_IN_MILLIS);
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+        // Bg, Fg, Top, Top2 and jobUnstarted should be changed from in-quota to out-of-quota
+        inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+                .onControllerStateChanged(argThat(jobs -> jobs.size() == 5));
+        // App is now in background and out of quota. Fg should now change to out of quota since it
+        // wasn't started. Top should change to out of quota as the app leaves TOP state.
+        assertFalse(jobTop.isExpeditedQuotaApproved());
+        assertFalse(jobTop2.isExpeditedQuotaApproved());
+        assertFalse(jobFg.isExpeditedQuotaApproved());
+        assertFalse(jobBg.isExpeditedQuotaApproved());
+        trackJobs(jobBg2);
+        assertFalse(jobBg2.isExpeditedQuotaApproved());
+        assertFalse(jobUnstarted.isExpeditedQuotaApproved());
+        synchronized (mQuotaController.mLock) {
+            assertTrue(
+                    0 >= mQuotaController
+                            .getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+        }
+    }
+
+    /**
      * Tests that Timers properly track overlapping top and background jobs.
      */
     @Test
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 7aa2e0f..2b55303 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -19,6 +19,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -50,7 +53,8 @@
             IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
 
     @Mock
-    private Context mContextSpy;
+    private Context mContext;
+    private BackupTransportConnection mBackupTransportConnection;
 
     private ForensicService mForensicService;
     private TestLooper mTestLooper;
@@ -63,7 +67,7 @@
 
         mTestLooper = new TestLooper();
         mLooper = mTestLooper.getLooper();
-        mForensicService = new ForensicService(new MockInjector(mContextSpy));
+        mForensicService = new ForensicService(new MockInjector(mContext));
         mForensicService.onStart();
     }
 
@@ -253,6 +257,8 @@
         assertEquals(STATE_VISIBLE, scb1.mState);
         assertEquals(STATE_VISIBLE, scb2.mState);
 
+        doReturn(true).when(mBackupTransportConnection).initialize();
+
         CommandCallback ccb = new CommandCallback();
         mForensicService.getBinderService().enable(ccb);
         mTestLooper.dispatchAll();
@@ -262,6 +268,29 @@
     }
 
     @Test
+    public void testEnable_FromVisible_TwoMonitors_BackupTransportUnavailable()
+            throws RemoteException {
+        mForensicService.setState(STATE_VISIBLE);
+        StateCallback scb1 = new StateCallback();
+        StateCallback scb2 = new StateCallback();
+        mForensicService.getBinderService().monitorState(scb1);
+        mForensicService.getBinderService().monitorState(scb2);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+
+        doReturn(false).when(mBackupTransportConnection).initialize();
+
+        CommandCallback ccb = new CommandCallback();
+        mForensicService.getBinderService().enable(ccb);
+        mTestLooper.dispatchAll();
+        assertEquals(STATE_VISIBLE, scb1.mState);
+        assertEquals(STATE_VISIBLE, scb2.mState);
+        assertNotNull(ccb.mErrorCode);
+        assertEquals(ERROR_BACKUP_TRANSPORT_UNAVAILABLE, ccb.mErrorCode.intValue());
+    }
+
+    @Test
     public void testEnable_FromEnabled_TwoMonitors() throws RemoteException {
         mForensicService.setState(STATE_ENABLED);
         StateCallback scb1 = new StateCallback();
@@ -330,6 +359,8 @@
         assertEquals(STATE_ENABLED, scb1.mState);
         assertEquals(STATE_ENABLED, scb2.mState);
 
+        doNothing().when(mBackupTransportConnection).release();
+
         CommandCallback ccb = new CommandCallback();
         mForensicService.getBinderService().disable(ccb);
         mTestLooper.dispatchAll();
@@ -356,6 +387,12 @@
             return mLooper;
         }
 
+        @Override
+        public BackupTransportConnection getBackupTransportConnection() {
+            mBackupTransportConnection = spy(new BackupTransportConnection(mContext));
+            return mBackupTransportConnection;
+        }
+
     }
 
     private static class StateCallback extends IForensicServiceStateCallback.Stub {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 7829fcc..8df18a8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -480,7 +480,7 @@
         if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) {
             mFullScreenMagnificationControllerStub.resetAndStubMethods();
             mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
-                    config.getCenterX(), config.getCenterY(), false, SERVICE_ID);
+                    config.getCenterX(), config.getCenterY(), true, false, SERVICE_ID);
             mMagnificationManagerStub.deactivateIfNeed();
         } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
             mMagnificationManagerStub.resetAndStubMethods();
@@ -531,6 +531,9 @@
             };
             doAnswer(enableMagnificationStubAnswer).when(
                     mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(),
+                    anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), eq(SERVICE_ID));
+            doAnswer(enableMagnificationStubAnswer).when(
+                    mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), anyFloat(),
                     anyFloat(), anyFloat(), anyBoolean(), eq(SERVICE_ID));
 
             Answer disableMagnificationStubAnswer = invocation -> {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index c4b4afd..5985abc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
 
+import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER;
 import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
 import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY;
 import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
@@ -76,6 +77,7 @@
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 
@@ -126,6 +128,7 @@
     final Resources mMockResources = mock(Resources.class);
     final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
+    final InputManagerInternal mMockInputManager = mock(InputManagerInternal.class);
     private final MagnificationAnimationCallback mAnimationCallback = mock(
             MagnificationAnimationCallback.class);
     private final MagnificationInfoChangedCallback mRequestObserver = mock(
@@ -163,6 +166,7 @@
         when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
         when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
         when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
+        when(mMockControllerCtx.getInputManager()).thenReturn(mMockInputManager);
         when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
         when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
         mResolver = new MockContentResolver();
@@ -285,10 +289,11 @@
                 mFullScreenMagnificationController.magnificationRegionContains(displayId, 100,
                         100));
         assertFalse(mFullScreenMagnificationController.reset(displayId, true));
-        assertFalse(mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, 0));
+        assertFalse(
+                mFullScreenMagnificationController.setScale(displayId, 2, 100, 100, true, true, 0));
         assertFalse(mFullScreenMagnificationController.setCenter(displayId, 100, 100, false, 1));
         assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                1.5f, 100, 100, false, 2));
+                1.5f, 100, 100, true, false, 2));
         assertTrue(mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0);
 
         mFullScreenMagnificationController.getMagnificationRegion(displayId, new Region());
@@ -313,7 +318,7 @@
         final float scale = 2.0f;
         final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         assertFalse(mFullScreenMagnificationController
-                .setScale(TEST_DISPLAY, scale, center.x, center.y, false, SERVICE_ID_1));
+                .setScale(TEST_DISPLAY, scale, center.x, center.y, true, false, SERVICE_ID_1));
         assertFalse(mFullScreenMagnificationController.isActivated(TEST_DISPLAY));
     }
 
@@ -331,7 +336,7 @@
         final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale);
         assertTrue(mFullScreenMagnificationController
-                .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1));
+                .setScale(displayId, scale, center.x, center.y, true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets);
@@ -357,7 +362,7 @@
         float scale = 2.0f;
         PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mFullScreenMagnificationController
-                .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
+                .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         // New center should be halfway between original center and pivot
@@ -405,7 +410,7 @@
         float scale = 2.0f;
         assertTrue(mFullScreenMagnificationController.setScale(displayId, scale,
                 INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(),
-                false, SERVICE_ID_1));
+                true, false, SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
 
         PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
@@ -440,7 +445,7 @@
         MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
 
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
-                newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1));
+                newCenter.x, newCenter.y, true, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -486,11 +491,11 @@
         final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         final float targetScale = 2.0f;
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                targetScale, center.x, center.y, false, SERVICE_ID_1));
+                targetScale, center.x, center.y, true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1));
+                targetScale, center.x, center.y, true, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         verify(mMockValueAnimator, never()).start();
@@ -516,7 +521,7 @@
 
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
                 MagnificationScaleProvider.MAX_SCALE + 1.0f,
-                newCenter.x, newCenter.y, false, SERVICE_ID_1));
+                newCenter.x, newCenter.y, true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -527,7 +532,7 @@
         // Verify that we can't zoom below 1x
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, 0.5f,
                 INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
-                false, SERVICE_ID_1));
+                true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x,
@@ -551,7 +556,7 @@
 
         // Off the edge to the top and left
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                scale, -100f, -200f, false, SERVICE_ID_1));
+                scale, -100f, -200f, true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
@@ -565,7 +570,7 @@
         // Off the edge to the bottom and right
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
                 INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1,
-                false, SERVICE_ID_1));
+                true, false, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
@@ -619,7 +624,7 @@
         PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale);
         // First zoom in
         assertTrue(mFullScreenMagnificationController
-                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
+                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         Mockito.reset(mMockWindowManager);
@@ -673,7 +678,7 @@
         // Upper left edges
         PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
         assertTrue(mFullScreenMagnificationController
-                .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false,
+                .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, true, false,
                         SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
         MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId);
@@ -685,7 +690,7 @@
         // Lower right edges
         PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mFullScreenMagnificationController
-                .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false,
+                .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, true, false,
                         SERVICE_ID_1));
         Mockito.reset(mMockWindowManager);
         MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId);
@@ -710,7 +715,7 @@
         float scale = 2.0f;
         // First zoom in
         assertTrue(mFullScreenMagnificationController
-                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
+                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
@@ -753,7 +758,7 @@
         PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale);
         // First zoom in
         assertTrue(mFullScreenMagnificationController
-                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
+                .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
@@ -786,12 +791,12 @@
         register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         assertTrue(mFullScreenMagnificationController
-                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_1));
         assertEquals(SERVICE_ID_1,
                 mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId));
         assertTrue(mFullScreenMagnificationController
-                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_2));
         assertEquals(SERVICE_ID_2,
                 mFullScreenMagnificationController.getIdOfLastServiceToMagnify(displayId));
@@ -809,10 +814,10 @@
         register(displayId);
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         mFullScreenMagnificationController
-                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+                .setScale(displayId, 2.0f, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_1);
         mFullScreenMagnificationController
-                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+                .setScale(displayId, 1.5f, startCenter.x, startCenter.y, true, false,
                         SERVICE_ID_2);
         assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
         checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId);
@@ -873,7 +878,7 @@
         float scale = 2.5f;
         PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1));
+                scale, firstCenter.x, firstCenter.y, true, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         Mockito.reset(mMockValueAnimator);
         // Stubs the logic after the animation is started.
@@ -1076,7 +1081,7 @@
         float scale = 2.0f;
         // setting animate parameter to true is differ from zoomIn2xToMiddle()
         mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
-                true, SERVICE_ID_1);
+                true, true, SERVICE_ID_1);
         MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
         Mockito.reset(mMockWindowManager);
@@ -1107,7 +1112,7 @@
         PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
         float scale = 2.0f;
         mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
         mMessageCapturingHandler.sendAllMessages();
         MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
@@ -1148,7 +1153,7 @@
         PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
         float scale = 2.0f;
         mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
-                true, SERVICE_ID_1);
+                true, true, SERVICE_ID_1);
         mMessageCapturingHandler.sendAllMessages();
         MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
         when(mMockValueAnimator.isRunning()).thenReturn(true);
@@ -1335,7 +1340,7 @@
                 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
 
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1));
+                scale, firstCenter.x, firstCenter.y, true, true, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(firstCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -1404,7 +1409,7 @@
         register(DISPLAY_0);
         final float scale = 1.0f;
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1);
+                DISPLAY_0, scale, Float.NaN, Float.NaN, true, true, SERVICE_ID_1);
 
         checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ false, DISPLAY_0);
         verify(mMockWindowManager).setFullscreenMagnificationActivated(DISPLAY_0, true);
@@ -1449,7 +1454,7 @@
 
         PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         mFullScreenMagnificationController.setScale(TEST_DISPLAY, 1.0f, pivotPoint.x, pivotPoint.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
         mFullScreenMagnificationController.persistScale(TEST_DISPLAY);
 
         // persistScale may post a task to a background thread. Let's wait for it completes.
@@ -1464,10 +1469,10 @@
         register(DISPLAY_1);
         final PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         mFullScreenMagnificationController.setScale(DISPLAY_0, 3.0f, pivotPoint.x, pivotPoint.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
         mFullScreenMagnificationController.persistScale(DISPLAY_0);
         mFullScreenMagnificationController.setScale(DISPLAY_1, 4.0f, pivotPoint.x, pivotPoint.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
         mFullScreenMagnificationController.persistScale(DISPLAY_1);
 
         // persistScale may post a task to a background thread. Let's wait for it completes.
@@ -1479,6 +1484,101 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void persistScale_setValue_notifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y,
+                /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1);
+        verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(),
+                anyFloat());
+
+        mFullScreenMagnificationController.persistScale(TEST_DISPLAY);
+
+        // persistScale may post a task to a background thread. Let's wait for it completes.
+        waitForBackgroundThread();
+        Assert.assertEquals(mFullScreenMagnificationController.getPersistedScale(TEST_DISPLAY),
+                4.0f);
+        verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void setScale_setNonTransientScale_notifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScale(TEST_DISPLAY, 4.0f, pivotPoint.x, pivotPoint.y,
+                /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1);
+
+        verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 4.0f);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void setScaleAndCenter_setTransientScale_notNotifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x,
+                point.y, /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1);
+
+        verify(mRequestObserver).onFullScreenMagnificationChanged(anyInt(), any(Region.class),
+                any(MagnificationConfig.class));
+        verify(mMockInputManager, never()).setAccessibilityPointerIconScaleFactor(anyInt(),
+                anyFloat());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void setScaleAndCenter_setNonTransientScale_notifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 3.0f, point.x,
+                point.y, /* isScaleTransient= */ false, /* animate= */ false, SERVICE_ID_1);
+
+        verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY, 3.0f);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void setCenter_notNotifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y,
+                /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1);
+        mFullScreenMagnificationController.setCenter(TEST_DISPLAY, point.x, point.y, false,
+                SERVICE_ID_1);
+
+        // Note that setCenter doesn't change scale, so it's not necessary to notify the input
+        // manager, but we currently do. The input manager skips redundant computation if the
+        // notified scale is the same as the previous call.
+        verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY,
+                2.0f);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER)
+    public void offsetMagnifiedRegion_notNotifyInput() {
+        register(TEST_DISPLAY);
+
+        PointF point = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
+        mFullScreenMagnificationController.setScale(TEST_DISPLAY, 2.0f, point.x, point.y,
+                /* isScaleTransient= */ true, /* animate= */ false, SERVICE_ID_1);
+        mFullScreenMagnificationController.offsetMagnifiedRegion(TEST_DISPLAY, 100, 50,
+                SERVICE_ID_1);
+
+        // Note that setCenter doesn't change scale, so it's not necessary to notify the input
+        // manager, but we currently do. The input manager skips redundant computation if the
+        // notified scale is the same as the previous call.
+        verify(mMockInputManager).setAccessibilityPointerIconScaleFactor(TEST_DISPLAY,
+                2.0f);
+    }
+
+    @Test
     public void testOnContextChanged_alwaysOnFeatureDisabled_resetMagnification() {
         setScaleToMagnifying();
 
@@ -1535,7 +1635,7 @@
         PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
 
         mFullScreenMagnificationController.setScale(DISPLAY_0, scale, pivotPoint.x, pivotPoint.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
     }
 
     private void initMockWindowManager() {
@@ -1578,7 +1678,7 @@
         PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
         float scale = 2.0f;
         mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
-                false, SERVICE_ID_1);
+                true, false, SERVICE_ID_1);
         checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index e5831b3..9f5dd93 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -90,6 +90,7 @@
 import com.android.server.accessibility.EventStreamTransformation;
 import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
 import com.android.server.wm.WindowManagerInternal;
@@ -227,9 +228,11 @@
         final FullScreenMagnificationController.ControllerContext mockController =
                 mock(FullScreenMagnificationController.ControllerContext.class);
         final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
+        final InputManagerInternal mockInputManager = mock(InputManagerInternal.class);
         when(mockController.getContext()).thenReturn(mContext);
         when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
         when(mockController.getWindowManager()).thenReturn(mockWindowManager);
+        when(mockController.getInputManager()).thenReturn(mockInputManager);
         when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
         when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
         when(mockController.getAnimationDuration()).thenReturn(1000L);
@@ -1343,7 +1346,7 @@
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale,
                 UserHandle.USER_SYSTEM);
         mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
-                DEFAULT_Y, /* animate= */ false,
+                DEFAULT_Y, true, /* animate= */ false,
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
 
         mMgh.transitionTo(mMgh.mPanningScalingState);
@@ -1364,7 +1367,7 @@
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale,
                 UserHandle.USER_SYSTEM);
         mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
-                DEFAULT_Y, /* animate= */ false,
+                DEFAULT_Y, true, /* animate= */ false,
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
 
         mMgh.transitionTo(mMgh.mPanningScalingState);
@@ -1401,7 +1404,7 @@
                 mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
 
         mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
-                DEFAULT_Y, /* animate= */ false,
+                DEFAULT_Y, true, /* animate= */ false,
                 AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         mMgh.transitionTo(mMgh.mPanningScalingState);
 
@@ -1438,7 +1441,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
         centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
 
@@ -1530,7 +1533,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 6.2f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE);
         send(event, InputDevice.SOURCE_MOUSE);
         fastForward(20);
@@ -1571,7 +1574,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
 
         // HOVER_MOVE should change magnifier viewport.
         MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE);
@@ -1615,7 +1618,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 5.3f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
         send(event, source);
         fastForward(20);
@@ -1649,7 +1652,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 2.7f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
         send(event, source);
         fastForward(20);
@@ -1685,7 +1688,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 3.8f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
         centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
 
@@ -1722,7 +1725,7 @@
                 (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
         float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
         mFullScreenMagnificationController.setScaleAndCenter(
-                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+                DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
         centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
         centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 2528177..8164ef9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -76,6 +76,7 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.window.flags.Flags;
 
@@ -154,6 +155,8 @@
     private WindowManagerInternal mWindowManagerInternal;
     @Mock
     private WindowManagerInternal.AccessibilityControllerInternal mA11yController;
+    @Mock
+    private InputManagerInternal mInputManagerInternal;
 
     @Mock
     private DisplayManagerInternal mDisplayManagerInternal;
@@ -200,6 +203,7 @@
         when(mControllerCtx.getContext()).thenReturn(mContext);
         when(mControllerCtx.getTraceManager()).thenReturn(mTraceManager);
         when(mControllerCtx.getWindowManager()).thenReturn(mWindowManagerInternal);
+        when(mControllerCtx.getInputManager()).thenReturn(mInputManagerInternal);
         when(mControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
         when(mControllerCtx.getAnimationDuration()).thenReturn(1000L);
         when(mControllerCtx.newValueAnimator()).thenReturn(mValueAnimator);
@@ -417,7 +421,7 @@
         assertTrue(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
                 DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
-                true, MAGNIFICATION_GESTURE_HANDLER_ID);
+                true, true, MAGNIFICATION_GESTURE_HANDLER_ID);
         verify(mTransitionCallBack).onResult(TEST_DISPLAY, false);
     }
 
@@ -467,7 +471,7 @@
         assertFalse(mMagnificationConnectionManager.isWindowMagnifierEnabled(TEST_DISPLAY));
         verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
                 eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
-                any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
+                eq(false), any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
     }
 
     @Test
@@ -484,7 +488,7 @@
 
         verify(mScreenMagnificationController, never()).setScaleAndCenter(anyInt(),
                 anyFloat(), anyFloat(), anyFloat(),
-                anyBoolean(), anyInt());
+                anyBoolean(), anyBoolean(), anyInt());
     }
 
     @Test
@@ -546,7 +550,7 @@
                 config, animate, TEST_SERVICE_ID);
         verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
                 /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(),
-                mCallbackArgumentCaptor.capture(), /* id= */ anyInt());
+                anyBoolean(), mCallbackArgumentCaptor.capture(), /* id= */ anyInt());
         mCallbackArgumentCaptor.getValue().onResult(true);
         mMockConnection.invokeCallbacks();
 
@@ -616,7 +620,7 @@
     @Test
     public void magnifyThroughExternalRequest_showMagnificationButton() {
         mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
-                MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID);
+                MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, false, TEST_SERVICE_ID);
 
         // The first time is trigger when fullscreen mode is activated.
         // The second time is triggered when magnification spec is changed.
@@ -638,7 +642,7 @@
         mMagnificationController.onPerformScaleAction(TEST_DISPLAY, newScale, updatePersistence);
 
         verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(newScale),
-                anyFloat(), anyFloat(), anyBoolean(), anyInt());
+                anyFloat(), anyFloat(), anyBoolean(), anyBoolean(), anyInt());
         verify(mScreenMagnificationController).persistScale(eq(TEST_DISPLAY));
     }
 
@@ -681,7 +685,7 @@
         final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN);
         mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
                 config.getScale(), config.getCenterX(), config.getCenterY(),
-                true, TEST_SERVICE_ID);
+                true, true, TEST_SERVICE_ID);
 
         // The notify method is triggered when setting magnification enabled.
         // The setScaleAndCenter call should not trigger notify method due to same scale and center.
@@ -930,7 +934,7 @@
     public void onWindowModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() {
         mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
                 DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
-                true, TEST_SERVICE_ID);
+                true, true, TEST_SERVICE_ID);
 
         mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
 
@@ -1317,7 +1321,8 @@
         }
         if (mode == MODE_FULLSCREEN) {
             mScreenMagnificationController.setScaleAndCenter(displayId, DEFAULT_SCALE, centerX,
-                    centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+                    centerY, true, true,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
         } else {
             mMagnificationConnectionManager.enableWindowMagnification(displayId, DEFAULT_SCALE,
                     centerX, centerY, null, TEST_SERVICE_ID);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
index ff2ce15..6dba967 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -41,6 +41,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Locale;
 
 @RunWith(AndroidJUnit4.class)
@@ -62,18 +64,23 @@
         final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
                 | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
         final var data = new byte[] {0x11, 0x22};
-        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
-        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
+        final var keyphrases = new ArrayList<SoundTrigger.KeyphraseRecognitionExtra>(2);
+        keyphrases.add(new SoundTrigger.KeyphraseRecognitionExtra(99,
                 RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
                     new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
-                                           new ConfidenceLevel(5000, 80)});
-        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
+                                           new ConfidenceLevel(5000, 80)}));
+        keyphrases.add(new SoundTrigger.KeyphraseRecognitionExtra(101,
                 RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
                     new ConfidenceLevel(7777, 30),
-                    new ConfidenceLevel(2222, 60)});
+                    new ConfidenceLevel(2222, 60)}));
 
-        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
-                keyphrases, data, flags);
+        var apiconfig = new SoundTrigger.RecognitionConfig.Builder()
+            .setCaptureRequested(true)
+            .setAllowMultipleTriggers(false) // must be false
+            .setKeyphrases(keyphrases)
+            .setData(data)
+            .setAudioCapabilities(flags)
+            .build();
         assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
     }
 
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 6742cbe..168141b 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -41,6 +41,7 @@
         "hamcrest-library",
         "junit-params",
         "kotlin-test",
+        "mockito-kotlin-nodeps",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "platform-screenshot-diff-core",
diff --git a/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt
new file mode 100644
index 0000000..47e7ac7
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/PointerIconCacheTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.view.Display
+import android.view.PointerIcon
+import androidx.test.platform.app.InstrumentationRegistry
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for {@link PointerIconCache}.
+ */
+@Presubmit
+class PointerIconCacheTest {
+
+    @get:Rule
+    val rule = MockitoJUnit.rule()!!
+
+    @Mock
+    private lateinit var native: NativeInputManagerService
+    @Mock
+    private lateinit var defaultDisplay: Display
+
+    private lateinit var context: Context
+    private lateinit var testLooper: TestLooper
+    private lateinit var cache: PointerIconCache
+
+    @Before
+    fun setup() {
+        whenever(defaultDisplay.displayId).thenReturn(Display.DEFAULT_DISPLAY)
+
+        context = object : ContextWrapper(InstrumentationRegistry.getInstrumentation().context) {
+            override fun getDisplay() = defaultDisplay
+        }
+
+        testLooper = TestLooper()
+        cache = PointerIconCache(context, native, Handler(testLooper.looper))
+    }
+
+    @Test
+    fun testSetPointerScale() {
+        val defaultBitmap = getDefaultIcon().bitmap
+        cache.setPointerScale(2f)
+
+        testLooper.dispatchAll()
+        verify(native).reloadPointerIcons()
+
+        val bitmap =
+            cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap
+
+        assertEquals(defaultBitmap.height * 2, bitmap.height)
+        assertEquals(defaultBitmap.width * 2, bitmap.width)
+    }
+
+    @Test
+    fun testSetAccessibilityScaleFactor() {
+        val defaultBitmap = getDefaultIcon().bitmap
+        cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 4f)
+
+        testLooper.dispatchAll()
+        verify(native).reloadPointerIcons()
+
+        val bitmap =
+            cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap
+
+        assertEquals(defaultBitmap.height * 4, bitmap.height)
+        assertEquals(defaultBitmap.width * 4, bitmap.width)
+    }
+
+    @Test
+    fun testSetAccessibilityScaleFactorOnSecondaryDisplay() {
+        val defaultBitmap = getDefaultIcon().bitmap
+        val secondaryDisplayId = Display.DEFAULT_DISPLAY + 1
+        cache.setAccessibilityScaleFactor(secondaryDisplayId, 4f)
+
+        testLooper.dispatchAll()
+        verify(native).reloadPointerIcons()
+
+        val bitmap =
+            cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap
+        assertEquals(defaultBitmap.height, bitmap.height)
+        assertEquals(defaultBitmap.width, bitmap.width)
+
+        val bitmapSecondary =
+            cache.getLoadedPointerIcon(secondaryDisplayId, PointerIcon.TYPE_ARROW).bitmap
+        assertEquals(defaultBitmap.height * 4, bitmapSecondary.height)
+        assertEquals(defaultBitmap.width * 4, bitmapSecondary.width)
+    }
+
+    @Test
+    fun testSetPointerScaleAndAccessibilityScaleFactor() {
+        val defaultBitmap = getDefaultIcon().bitmap
+        cache.setPointerScale(2f)
+        cache.setAccessibilityScaleFactor(Display.DEFAULT_DISPLAY, 3f)
+
+        testLooper.dispatchAll()
+        verify(native, times(2)).reloadPointerIcons()
+
+        val bitmap =
+            cache.getLoadedPointerIcon(Display.DEFAULT_DISPLAY, PointerIcon.TYPE_ARROW).bitmap
+
+        assertEquals(defaultBitmap.height * 6, bitmap.height)
+        assertEquals(defaultBitmap.width * 6, bitmap.width)
+    }
+
+    private fun getDefaultIcon() =
+        PointerIcon.getLoadedSystemIcon(context, PointerIcon.TYPE_ARROW, false, 1f)
+}